Using complex user type linked to existing table in the new ASP.NET Identity - asp.net

I'm trying to implement new ASP.NET Identity in my old project. I have an existing table called tda_Contacts in the database. The following code works fine without table attribute and creates all new identity related tables plus TdaContacts table. But when put existing table name in the table attribute ([Table("tda_Contacts")]), then it does nothing and throws Invalid object name 'dbo.UserSecrets' exeption. Also if I put different name in that attribute it works fine and creates a correct table with exactly the same columns and types as existing tda_Contacts.
What am I doing wrong? How to force it to use my existing table?
public class IdentityUser : User
{
public int ContactID { get; set; }
[ForeignKey("ContactID ")]
public virtual TdaContact TdaContact { get; set; }
}
public class CustomUserContext : IdentityStoreContext
{
public CustomUserContext(DbContext db) : base(db)
{
Users = new UserStore<IdentityUser>(db);
}
}
public class MyDbContext : IdentityDbContext<IdentityUser, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
public MyDbContext() : base("Name=MyConnectionString")
{
}
public IDbSet<TdaContact> TdaContacts { get; set; }
}
[Table("tda_Contacts")]
public class TdaContact
{
[Key]
public int ContactID { get; set; }
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
[MaxLength(255)]
public string Email { get; set; }
}
P.S. Just discovered that with precreated IdentityUsers table with correct foreign key to tda_Contacts it works as expected.

Related

Entity Framework Core Join identity AspNetUser table. AspNetUser Id to custom tables/Entities

I started a new Blazor application and I am trying to use Entity FrameworkCore. I want to link the Identity AspNetUsers table. I want a 1-many relationship with an UserAddress Table. One AspNetUser has many addresses.
public class UserAddress
{
public string Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string ZipCode { get; set; }
' I don't know what to put here to connect this table with the AspNetUsers Table
public int UserId {get;set} This doesn't work
}
I don't know what to do to have EF construct the 1 to many relation between the AspNetUsers Id and the UserAddress table
You can create a one-to-many relationship like this.
UserAddress class:
public class UserAddress
{
public string Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string ZipCode { get; set; }
}
New ApplicationUser inherits Identityuser:
public class ApplicationUser:IdentityUser
{
public ICollection<UserAddress> UserAddresses { get; set; }
}
Make changes in your ApplicationDbContext as follows:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(b => { b.HasMany(p => p.UserAddresses); }) ;
}
public DbSet<UserAddress> UserAddresses { get; set; }
}
Then start the migration and update the database.
Result:

include user data in query

I would like to query a table, which holds workflow events with the appr. user id. And instead of the user id, I would like to show the user name.
I have this model:
public class WorkflowEvent
{
public long Id { get; set; }
public DateTime EventDate { get; set; }
public WorkflowStatus Status { get; set; }
public int UserId { get; set; }
public AppUser User { get; }
public string Note { get; set; }
public long WorkflowId { get; set; }
public long DocumentId { get; set; }
}
And this is the user model (it is the standard IdentityUser, I use Asp.Net Identity):
public class AppUser : IdentityUser<int>
{
public string RealName { get; set; }
public string AppTheme { get; set; }
}
And this is my query:
public IEnumerable<WorkflowEvent> WorkflowEvents(int DocumentId)
{
return DataContext.WorkflowEvents.Where(e => e.DocumentId == DocumentId).Include(e => e.User).OrderBy(e => e.EventDate);
}
Unfortunatelly, I get following error on this: System.InvalidOperationException: Lambda expression used inside Include is not valid. In the WorkflowEvents table, there is a foreign key on the UserId, of course.
Have you any idea, why do I get this error message? What goes wrong?
Both Sami's and Nik's suggestions helped. I added the setter to the AppUser. And then I got the error, that the AppUser object isn't exists. This due to the fact, that I have in my app two datacontexts: one for the domain models and one for Identity. So, I just add this line of code to the domain data context:
public DbSet<AppUser> AspNetUsers { get; set; }
And everything worked like a charm.

Cannot migrate AspNetUser entity in Entity Framework Code First Approach in .NET

I am developing an ASP.NET MVC project using Visual Studio 2013. I am using Entity Framework Code First Approach to interact with database. But I am having a problem with migrating data because of built-in identity system in ASP.NET. Everything was fine before I touch to identity system.
These are the steps I have done.
I registered an account from UI using built-in identity system. So AspNetUsers table is created in database.
I created AspNetUser class to my code because I need to work with it.
Then I run migration and update database command. It throws error.
This is my AspNetUser class
public class AspNetUser
{
[Required]
public String Id { get; set; }
[Required]
public String UserName { get; set; }
public String PasswordHash { get; set; }
public String SecurityStamp { get; set; }
[Required]
public String Discriminator { get; set; }
}
This is my db context class
public class AyarDbContext : DbContext
{
public AyarDbContext()
: base("DefaultConnection")
{
}
public DbSet<Category> Categories { get; set; }
public DbSet<Region> Regions { get; set; }
public DbSet<Area> Areas { get; set; }
public DbSet<Item> Items { get; set; }
public DbSet<ItemContactInfo> ItemContacts { get; set; }
public DbSet<Gallery> Galleries { get; set; }
public DbSet<Mail> Mails { get; set; }
public DbSet<AspNetUser> AspNetUsers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
This is the error I got.
There is already an object named 'AspNetUsers' in the database.
How can I migrate AspNetUser class? I registered using UI because I auto create the other tables need for identity system. How can I migrate that class and map with AspNetUsers table that is already existing in database.
As the error explains, that table already exists and is accessible in this way:
var user = context.Users.First(u => u.Id == myId);
If you want to extend the user class and add properties, you can inherit it:
public class MyAppUser : IdentityUser
{
// don't include properties already in IdentityUser
public string MyNewProperty { get; set; }
}
http://johnatten.com/2014/06/22/asp-net-identity-2-0-customizing-users-and-roles/

Entity Framework shows inconsistent behaviour when used with Asp.net Identity

I have 3 tables Violation,Comment and and auto generated AspNetUsers respectively.The relationship between them as follows.
I am using code-first approach and my models are as follows.Some properties are removed for brevity.
Violation Model
public class Violation
{
public Violation()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ApplicationUser CreatorUser { get; set; }
}
Comment Model
public class Comment
{
public int Id { get; set; }
[Required]
public string Content { get; set; }
[Required]
public DateTime PostedDateTime { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public Violation Violation { get; set; }
}
ApplicationUser(AspNetUsers Table)
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Comments = new List<Comment>();
this.Violations = new List<Violation>();
}
public virtual List<Comment> Comments { get; set; }
public virtual List<Violation> Violations { get; set; }
}
The problem is that when I try to retrieve Comment's ApplicationUser navigation property , I see many of them pointing to a null property even database has proper record for each of them.
Shortly,EF doesn't retrieve database records properly.I stuck with it,can't find the reason.
In fact, it's not being lazy-loaded. You didn't add the virtual keyword to your Comment.ApplicationUser property, so Entity Framework cannot override it to add the lazy-loading logic. As a result, it's always going to be null unless you explicitly load it. Add the virtual keyword, and you'll be fine.
If you want the navigation properties populated you need to include them in the query:
var comments = context.Comments
.Include(c => c.Violation)
.Include(c => c.ApplicationUser)
.Where(x => x.Violation.Id == violationId);
https://msdn.microsoft.com/en-us/data/jj574232.aspx#eager

Why do the ASP.NET Identity interfaces use strings for primary and foreign keys?

I'm looking at the interfaces on the new ASP.NET Identity classes and the database it creates using Entity Framework Code First. I'm using the Visual Studio 2013 RC.
At first glance the database schema looks reasonably normal:
But all the keys are NVARCHAR(128)
And for some crazy reason AspNetUserSecrets.Id is a PK that looks like it could point to more than one record in the AspNetUsers table. Does this mean multiple AspNetUsers will have to share the same password?
When I look at the Looking at the interfaces you're forced to implement, these are all strings...
public class User : IUser
{
public string Id { get; set; }
public string UserName { get; set; }
}
public class UserSecret : IUserSecret
{
public string UserName { get; set; }
public string Secret { get; set; }
}
public class UserRole : IUserRole
{
public string UserId { get; set; }
public string RoleId { get; set; }
}
public class UserClaim : IUserClaim
{
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
public class UserManagement : IUserManagement
{
public string UserId { get; set; }
public bool DisableSignIn { get; set; }
public DateTime LastSignInTimeUtc { get; set; }
}
public class Tokens : IToken
{
public string Id { get; set; }
public string Value { get; set; }
public DateTime ValidUntilUtc { get; set; }
}
public class UserLogin : IUserLogin
{
public string UserId { get; set; }
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
public class Role : IRole
{
public string Id { get; set; }
public string Name { get; set; }
}
So I'm coming to terms with the fact that I may have to implement this using strings for PK and FK relationships.
But I'd really love to know WHY it's built like this...?
EDIT: Time has passed and there are now articles on how to extend the asp.net identity to use int (or guid) fields:
http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
The intent was to allow both arbitrary id types (i.e. int, guid, string), but also avoid having serialization/casting issues for the id property.
So you can define your keys however you like and just implement the interface method
public class MyUser : IUser {
public int Id { get; set; }
string IUser.Id { get { return Id.ToString(); } }
}
Adding to what Hao said:
The Identity runtime prefers strings for the user ID because we don’t want to be in the business of figuring out proper serialization of the user IDs (we use strings for claims as well for the same reason), e.g. all (or most) of the Identity interfaces refer to user ID as a string.
People that customize the persistence layer, e.g. the entity types, can choose whatever type they want for keys, but then they own providing us with a string representation of the keys.
By default we use the string representation of GUIDs for each new user, but that is just because it provides a very easy way for us to automatically generate unique IDs.
With ASP.NET Core, you have a very simple way to specify the data type you want for Identity's models.
First step, override identity classes from < string> to < data type you want> :
public class ApplicationUser : IdentityUser<Guid>
{
}
public class ApplicationRole : IdentityRole<Guid>
{
}
Declare your database context, using your classes and the data type you want :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
And in your startup class, declare the identity service using your models and declare the data type you want for the primary keys :
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders();
In ASP.NET identity tables, primary keys will still be in NVARCHAR but in your application it's will be the data type you want.
You can check this in a controller :
[HttpGet]
public async Task<IActionResult> Test()
{
ApplicationUser user = await _userManager.GetUserAsync(HttpContext.User);
Guid userId = user.Id; // No cast from string, it's a Guid data type
throw new NotImplementedException();
}

Resources