Reputation: 1479
I am new to Code first Approach on EF6. So please excuse
I am trying to implement AspNetIdentityframework for my local project.
I have all the tables which comes out of the box in the database.I kept as is same with the model class structure.I have no roles just 1 role.
Here is my DbContext class for this.
public class AuthDbContext : IdentityDbContext<IdentityUser>
{
public AuthDbContext()
: base("AuthDbContext")
{
Database.SetInitializer<AuthDbContext>(null);
}
public virtual DbSet<AspNetRole> AspNetRoles { get; set; }
public virtual DbSet<AspNetUserClaim> AspNetUserClaims { get; set; }
public virtual DbSet<AspNetUserLogin> AspNetUserLogins { get; set; }
public virtual DbSet<AspNetUser> AspNetUsers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(t => t.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(t => t.Id);
modelBuilder.Entity<IdentityUser>().HasKey<string>(t => t.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(t => new { t.RoleId,t.UserId});
modelBuilder.Ignore<IdentityUserRole>();
modelBuilder.Ignore<AspNetRole>();
}
I have 2 users in the table. I am trying to find one . I am getting error Invalid Column Name 'Discriminator' What is that I am doing wrong in generating the model ? I tried lot of options I could not solve this one
Here is the sql generated for userManager.FindByName Method
exec sp_executesql N'SELECT
[Limit1].[C1] AS [C1],
[Limit1].[Id] AS [Id],
[Limit1].[Email] AS [Email],
[Limit1].[EmailConfirmed] AS [EmailConfirmed],
[Limit1].[PasswordHash] AS [PasswordHash],
[Limit1].[SecurityStamp] AS [SecurityStamp],
[Limit1].[PhoneNumber] AS [PhoneNumber],
[Limit1].[PhoneNumberConfirmed] AS [PhoneNumberConfirmed],
[Limit1].[TwoFactorEnabled] AS [TwoFactorEnabled],
[Limit1].[LockoutEndDateUtc] AS [LockoutEndDateUtc],
[Limit1].[LockoutEnabled] AS [LockoutEnabled],
[Limit1].[AccessFailedCount] AS [AccessFailedCount],
[Limit1].[UserName] AS [UserName]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Email] AS [Email],
[Extent1].[EmailConfirmed] AS [EmailConfirmed],
[Extent1].[PasswordHash] AS [PasswordHash],
[Extent1].[SecurityStamp] AS [SecurityStamp],
[Extent1].[PhoneNumber] AS [PhoneNumber],
[Extent1].[PhoneNumberConfirmed] AS [PhoneNumberConfirmed],
[Extent1].[TwoFactorEnabled] AS [TwoFactorEnabled],
[Extent1].[LockoutEndDateUtc] AS [LockoutEndDateUtc],
[Extent1].[LockoutEnabled] AS [LockoutEnabled],
[Extent1].[AccessFailedCount] AS [AccessFailedCount],
[Extent1].[UserName] AS [UserName],
''0X0X'' AS [C1]
FROM [dbo].[AspNetUsers] AS [Extent1]
WHERE ([Extent1].[Discriminator] = N''User'') //Error here ?????????
AND (((UPPER([Extent1].[UserName])) = (UPPER(@p__linq__0))) OR ((UPPER([Extent1].[UserName]) IS NULL) AND (UPPER(@p__linq__0) IS NULL)))
) AS [Limit1]',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'testUser'
Upvotes: 0
Views: 675
Reputation: 2085
As you can see here in the sources of Asp.NET Identity 2.0, IdentityDbContext<T>
already
defines the DbModelBuilder
configuration in OnModelCreating
and the
DbSet<AspNetRole>
,
DbSet<AspNetUserClaim>
,
DbSet<AspNetUserLogin>
,
DbSet<AspNetUser>
, so you don't have to define all those properties, nor override the OnModelCreating
.
Your Context should look something like this:
public class AuthDbContext : IdentityDbContext<IdentityUser>
{
public AuthDbContext() : base("AuthDbContext")
{
Database.SetInitializer<AuthDbContext>(null);
}
}
You are defining a DbSet<AspNetUser>
and your Context inherits from Identity<IdentityUser>
so there are 2 DbSets
, one where T
is IdentityUser
and another where T
is AspNetUser
. IdentityUser
inherits from AspNetUser
, so EntityFramework
manages inheritance by adding a column called Discriminator.
Edit:
Here is the implementation of the class you're inheriting from.
/// <summary>
/// DbContext which uses a custom user entity with a string primary key
/// </summary>
/// <typeparam name="TUser"></typeparam>
public class IdentityDbContext<TUser>
: IdentityDbContext<
TUser,
IdentityRole,
string,
IdentityUserLogin,
IdentityUserRole,
IdentityUserClaim>
where TUser : IdentityUser
{
/// <summary>
/// Default constructor which uses the DefaultConnection
/// </summary>
public IdentityDbContext()
: this("DefaultConnection")
{ }
/// <summary>
/// Constructor which takes the connection string to use
/// </summary>
/// <param name="nameOrConnectionString"></param>
public IdentityDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{ }
}
As you can see, the class IdentityDbContext<TUser>
inherits from IdentityDbContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim>
. The latter already defines properties of type IDbSet<TRole>
and IDbSet<TUser>
, and already has an implementation of OnModelCreating
that in my opinion satisfies your needs.
/// <summary>
/// Maps table names, and sets up relationships between the various user entities
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (modelBuilder == null)
{
throw new ArgumentNullException("modelBuilder");
}
// Needed to ensure subclasses share the same table
var user = modelBuilder.Entity<TUser>().ToTable("AspNetUsers");
user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
user.Property(u => u.UserName).IsRequired();
// CONSIDER: u.Email is Required if set on options?
// Look into IValidatable...
// Try this (Configure Join table name HasMany.WithMany.Map(To match 1.0 tables)
modelBuilder.Entity<TUserRole>()
.HasKey(r => new { r.UserId, r.RoleId })
.ToTable("AspNetUserRoles");
modelBuilder.Entity<TUserLogin>()
.HasKey(l => new { l.LoginProvider, l.ProviderKey, l.UserId })
.ToTable("AspNetUserLogins");
modelBuilder.Entity<TUserClaim>()
.ToTable("AspNetUserClaims");
var role = modelBuilder.Entity<TRole>().ToTable("AspNetRoles");
role.Property(r => r.Name).IsRequired();
role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);
}
If you want to use your own AspNetRole
, AspNetUserClaim
, and AspNetUserLogin
, you should inherit from IdentityDbContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim>
and pass your custom entities, or if you want to really have control of your database you should simply inherit from DbContext
and do all the setup yourself (thing you're already doing).
I hope I was clear, if you have any doubt don't hesitate in posting a comment.
Upvotes: 2