Unexpected alteration to composite primary key order in entity framework migration - entity-framework-6

I am upgrading the security in an xaf application as per DevExpress help
My situation is complicated because I have a mapping from the payroll table to the user table.
I have found in the past that this mapping caused a need for some configuration classes
Thus I have the following in the DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new RoleConfiguration());
modelBuilder.Configurations.Add(new UserConfiguration());
}
public class UserConfiguration : EntityTypeConfiguration<PermissionPolicyUser>
{
public UserConfiguration()
{
HasMany(x => x.Roles).WithMany(y=> y.Users)
.Map(m =>
{
m.MapLeftKey("PermissionPolicyRole_ID");
m.MapRightKey("PermissionPolicyUser_ID");
m.ToTable("PermissionPolicyUserPermissionPolicyRoles");
});
}
}
public class RoleConfiguration : EntityTypeConfiguration<PermissionPolicyRole>
{
public RoleConfiguration()
{
HasMany(x => x.Users).WithMany(y=> y.Roles)
.Map(m =>
{
m.MapLeftKey("PermissionPolicyUser_ID");
m.MapRightKey("PermissionPolicyRole_ID");
m.ToTable("PermissionPolicyUserPermissionPolicyRoles");
});
}
}
When I generate the migration I was surprised to see the primary key columns reordered
DropPrimaryKey("dbo.PermissionPolicyUserPermissionPolicyRoles");
AddPrimaryKey("dbo.PermissionPolicyUserPermissionPolicyRoles", new[] { "PermissionPolicyRole_ID", "PermissionPolicyUser_ID" });
My question is why did this happen and does it matter?

Related

asp.net core authentication with IdentityDbContext<AppUser, AppRole, int, AppUserClaim, AppUserRole, AppUserLogin, AppRoleClaim, AppUserToken>

I am trying to create database from Code first model. When I run dotnet ef migrations add or debug application I get error:
TypeLoadException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type parameter 'TUser'.
System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, int numGenericArgs, ObjectHandleOnStack type)
in code:
.AddEntityFrameworkStores<AppWebContext, int>()
My code in ConfigureServices is:
var res = services
.AddIdentity<AppUser, AppRole>(config =>
{
config.User.RequireUniqueEmail = true;
config.Password.RequireNonAlphanumeric = false;
config.Cookies.ApplicationCookie.AutomaticChallenge = false;
})
.AddEntityFrameworkStores<AppWebContext, int>()
.AddDefaultTokenProviders();
and my EF models are:
[Table("Roles")]
public partial class AppRole : IdentityRole<int, AppUserRole, AppRoleClaim>
{
}
[Table("RoleClaims")]
public partial class AppRoleClaim : IdentityRoleClaim<int>
{
}
[Table("Users")]
public partial class AppUser : IdentityUser<int, AppUserClaim, AppUserRole, AppUserLogin>
{
}
//same for UserClaim, UserLogin, UserRole, UserToken
and my DBContext:
public partial class AppWebContext : IdentityDbContext<AppUser, AppRole, int, AppUserClaim, AppUserRole, AppUserLogin, AppRoleClaim, AppUserToken>
{
public AppWebContext() : base() {
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=...");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AppUser>(entity =>
{
entity
.HasKey(u => u.Id);
entity.Property(p => p.Id)
.ValueGeneratedOnAdd();
});
modelBuilder.Entity<AppRole>(entity =>
{
entity
.HasKey(u => u.Id);
entity.Property(p => p.Id)
.ValueGeneratedOnAdd();
});
modelBuilder.Entity<AppUserClaim>(entity =>
{
entity
.HasKey(u => u.Id);
entity.Property(p => p.Id)
.ValueGeneratedOnAdd();
});
modelBuilder.Entity<AppUserRole>(entity =>
{
entity
.HasKey(u => new { u.RoleId, u.UserId });
});
modelBuilder.Entity<AppRoleClaim>(entity =>
{
entity
.HasKey(u => u.Id);
entity.Property(p => p.Id)
.ValueGeneratedOnAdd();
});
}
}
What did I miss? I am new in ASP.net and ASP.NET Core.
I am using version 1.1.
Complete error:
An error occurred while starting the application.
TypeLoadException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type parameter 'TUser'.
System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, int numGenericArgs, ObjectHandleOnStack type)
ArgumentException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'.
System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
TypeLoadException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type parameter 'TUser'.
System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, int numGenericArgs, ObjectHandleOnStack type)
System.RuntimeTypeHandle.Instantiate(Type[] inst)
System.RuntimeType.MakeGenericType(Type[] instantiation)
Show raw exception details
ArgumentException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'.
System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
System.RuntimeType.MakeGenericType(Type[] instantiation)
Microsoft.Extensions.DependencyInjection.IdentityEntityFrameworkBuilderExtensions.GetDefaultServices(Type userType, Type roleType, Type contextType, Type keyType)
Microsoft.Extensions.DependencyInjection.IdentityEntityFrameworkBuilderExtensions.AddEntityFrameworkStores<TContext, TKey>(IdentityBuilder builder)
OpPISWeb.Startup.ConfigureServices(IServiceCollection services)
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
Show raw exception details
.NET Core X64 v4.1.1.0 | Microsoft.AspNetCore.Hosting version 1.1.0-rtm-22752 | Microsoft Windows 10.0.14393 | Need help? - Yes I do. Please.
Your problem is caused by .AddEntityFrameworkStores<AppWebContext, int>(). Take a look at HaoK's responses toward the end of this post:
https://github.com/aspnet/Identity/issues/1001.
Specially these 2
"AddEntityFrameworkStores can only be called with a user that derives from IdentityUser. If you are specifying more generic arguments, use IdentityBuilder.AddUserStore() instead."
"AddEntityFrameworkStores can only be called with a role that derives from IdentityRole. If you are specifying more generic arguments, use IdentityBuilder.AddRoleStore() instead."
So if you want to override UserClaim, UserLogin and all others, you need to implement your own UserStore and RoleStore, and use AddUserStore<T>() and AddRoleStore<T>() instead of AddEntityFrameworkStores.
AppUserStore
public class AppUserStore : UserStore<AppUser, AppRole, AppDbContext, int, AppUserClaim, AppUserRole, AppUserLogin, AppUserToken, AppRoleClaim>
{
public AppUserStore(AppDbContext dbContext)
: base(dbContext)
{
}
protected override AppUserToken CreateUserToken(AppUser user, string loginProvider, string name, string value)
{
throw new NotImplementedException();
}
protected override AppUserLogin CreateUserLogin(AppUser user, UserLoginInfo login)
{
throw new NotImplementedException();
}
protected override AppUserRole CreateUserRole(AppUser user, AppRole role)
{
throw new NotImplementedException();
}
protected override AppUserClaim CreateUserClaim(AppUser user, Claim claim)
{
throw new NotImplementedException();
}
}
AppRoleStore
public class AppRoleStore : RoleStore<AppRole, AppDbContext, int, AppUserRole, AppRoleClaim>
{
public AppRoleStore(AppDbContext dbContext)
: base(dbContext)
{
}
protected override AppRoleClaim CreateRoleClaim(AppRole role, Claim claim)
{
throw new NotImplementedException();
}
}
And in your Startup.cs:
services.AddIdentity<AppUser, AppRole>()
.AddUserStore<AppUserStore>()
.AddRoleStore<AppRoleStore>()
// Components that generate the confirm email and reset password tokens
.AddDefaultTokenProviders();
Now I do think you need to create your own version of UserManager, RoleManager and SignInManager as well. I will update with more codes here later on after I figure everything out.
When you are customizing ASP.NET Core Identity, you should not use
AddEntityFrameworkStores anymore. Because it will override all of your
previous settings and customization to default Identity services.
Quote from Github answer here.

ASP.NET-Identity OnModelCreating Modelbuilder Generating Duplicate Foreign Keys

I have an ASP MVC 5 application but the model builder is creating duplicate foreign keys for the IdentityUserRole, IdentityUserClaim and IdentityUserLogin tables.
E.g in the generated migration table below has RoleId as well as IdentityRole_ID
CreateTable(
"dbo.UserRole",
c => new
{
RoleId = c.String(nullable: false, maxLength: 128),
UserId = c.String(nullable: false, maxLength: 128),
IdentityRole_Id = c.String(maxLength: 128),
ApplicationUser_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => new { t.RoleId, t.UserId })
.ForeignKey("dbo.Role", t => t.IdentityRole_Id)
.ForeignKey("dbo.User", t => t.ApplicationUser_Id)
.Index(t => t.IdentityRole_Id)
.Index(t => t.ApplicationUser_Id);
In my fluent api I have this defined as follows:
public IdentityUserRoleConfiguration()
{
HasKey(x => new { x.RoleId, x.UserId });
ToTable("UserRole");
}
My modelbuilder class:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
// Add some DBSets
public ApplicationDbContext()
: base("ApplicationDbContext", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
// base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new IdentityUserRoleConfiguration());
modelBuilder.Configurations.Add(new IdentityUserLoginConfiguration());
modelBuilder.Configurations.Add(new IdentityRoleConfiguration());
modelBuilder.Configurations.Add(new IdentityUserClaimConfiguration());
modelBuilder.Configurations.Add(new ApplicationUserConfiguration());
}
}
I've commented out the base.OnModelCreating(modelBuilder); statement becuase it throws the following error when I run a migration:
A configuration for type 'Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole' has already been added. To reference the existing configuration use the Entity<T>() or ComplexType<T>() methods.
The configuration can be implemented as follows,
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityRole>().ToTable("Roles");
modelBuilder.Entity<IdentityUserRole>().ToTable("UserRoles");
modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaims");
modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLoginProviders");
modelBuilder.Configurations.Add(new UserConfiguration());
}
Since the fluent configuration are already implemented for most of IdentityRole, IdentityUserRole, IdentityUserClaim, IdentityUserLogin models in Microsoft.AspNet.Identity.EntityFramework Hence it allows only to override ApplicationUser. Hence the only way to implement his is like Entity<T>()

Seeding with Entity Framwork 6

I am a noob at ASP.NET MVC. In my application I have an entity called Products. I used a code-first approach and Entity Framework fluent api for mapping.
Here is my context class and want to know how I can do seeding for this context class. Thank you.
public class SimpleContext : DbContext, IDbContext
{
public SimpleContext() : base("name = DefaultDbContext")
{
}
static SimpleContext()
{
Database.SetInitializer(new CreateDatabaseIfNotExists<SimpleContext>());
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typeToRegister =
Assembly.GetExecutingAssembly().GetTypes().Where(type => !String.IsNullOrEmpty(type.Namespace)).Where(
type =>
type.BaseType != null && type.BaseType.IsGenericType &&
type.BaseType.GetGenericTypeDefinition() == typeof (EntityTypeConfiguration<>));
foreach (var type in typeToRegister)
{
modelBuilder.Configurations.Add((dynamic)Activator.CreateInstance(type));
}
base.OnModelCreating(modelBuilder);
}
}

Entity Framework Error: Invalid object name 'dbo.XXXXXXX'

I have the following data table:
CREATE TABLE [dbo].[AuthorizeAttrib]
(
[Id] [UNIQUEIDENTIFIER] NOT NULL
, [ControllerName] [VARCHAR](100) NOT NULL
, [ActionName] [VARCHAR](100) NULL
, CONSTRAINT [PK_AuthorizeAttrib] PRIMARY KEY CLUSTERED ( [Id] ASC )
)
ON [PRIMARY];
In code I have the following:
public class StsDatabase : MembershipRebootDbContext<StsUser, StsGroup>
{
public StsDatabase() : this("name=MembershipReboot") { }
public StsDatabase(string name) : base(name) { this.RegisterUserAccountChildTablesForDelete<StsUser>(); }
public DbSet<AuthorizeAttrib> AuthorizeAttribs { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<RuleSet> RuleSets { get; set; }
public DbSet<RuleSetRole> RuleSetRoles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.ConfigureMembershipRebootUserAccounts<StsUser>();
modelBuilder.ConfigureMembershipRebootGroups<StsGroup>();
modelBuilder.Configurations.Add(new AuthorizeAttribMap());
modelBuilder.Configurations.Add(new RoleMap());
modelBuilder.Configurations.Add(new RuleSetMap());
modelBuilder.Configurations.Add(new RuleSetRoleMap());
}
}
And the AuthorizeAttribMap looks like:
public class AuthorizeAttribMap : EntityTypeConfiguration<AuthorizeAttrib>
{
public AuthorizeAttribMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.ControllerName)
.IsRequired()
.HasMaxLength(100);
this.Property(t => t.ActionName)
.HasMaxLength(100);
// Table & Column Mappings
this.ToTable("AuthorizeAttrib");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.ControllerName).HasColumnName("ControllerName");
this.Property(t => t.ActionName).HasColumnName("ActionName");
}
}
I've even tried running with AND without the following:
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
Nothing seems to be working. When I break on the code before it executes and copy the SQL then run it in SQL Mangler it runs fine. The SQL produced by LINQ:
SELECT [Extent1].[Id] AS [Id]
, [Extent1].[ControllerName] AS [ControllerName]
, [Extent1].[ActionName] AS [ActionName]
FROM [dbo].[AuthorizeAttrib] AS [Extent1];
I am scratching my head as to what else would cause this challenge!
Any ideas?
When the build/publish script ran, the wrong web.config was pulled in. Still pointing to the Dev DB instead of the Security Dev DB. Note to self: ALWAYS check the web.configs. Too many moving parts!

Database Initializer not being called in EF 5 + MVC 3

I know this question might be duplicated, but I haven't found any solution for my problem yet.
I have:
protected void Application_Start()
{
Database.SetInitializer(new DatabaseSeeder());
DatabaseContext c = new DatabaseContext();
c.Database.Initialize(true);
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
my database seeder is:
public class DatabaseSeeder : DropCreateDatabaseAlways<DatabaseContextBase>
{
protected override void Seed(DatabaseContextBase context)
{
base.Seed(context);
var roles = new[] {"Admin", "User"};
foreach (var role in roles)
{
context.Roles.Add(new UserRole {FullName = role});
}
context.SaveChanges();
}
}
and my context is:
public abstract class DatabaseContextBase : DbContext
{
public DbSet<UserRole> Roles { get; set; }
public DbSet<UserAndPermissionInGroup> UserAndPermissions { get; set; }
public DbSet<GeneralUser> Users { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Forum> Forums { get; set; }
public DatabaseContextBase() :base("ForumDb")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Comment>()
.HasRequired(e => e.Owner)
.WithMany(t => t.Comments)
.HasForeignKey(e => e.OwnerId)
.WillCascadeOnDelete(false);
}
}
And finally:
public class DatabaseContext : DatabaseContextBase
{
}
The problem is that, even with this initializer, I get the following error:
The model backing the 'DatabaseContext' context has changed since the
database was created. Consider using Code First Migrations to update
the database
I am using EF 5 code first in an ASP.NET MVC 3 project. Any suggestions?
It looks like Database.SetInitializer does not work for derived types. I changed the initializer from
public class DatabaseSeeder : DropCreateDatabaseAlways<DatabaseContextBase>
to
public class DatabaseSeeder : DropCreateDatabaseAlways<DatabaseContext>
and now the code:
Database.SetInitializer(new DatabaseSeeder());
DatabaseContext c = new DatabaseContext();
c.Database.Initialize(true);
works pretty well. Thanks for all answers.
If you are good with losing your data, when you make changes to your models, try following.
DatabaseSeeder : DropCreateDatabaseIfModelChanges<DatabaseContextBase>
Instead of
DatabaseSeeder : DropCreateDatabaseAlways<DatabaseContextBase>
That way, when you make changes to your model, your database will be recreated using your up to date models and seed method will be called.
If you want to preserve your data, you should start reading about migrations.
Code-First Migrations
Automatic Migrations
Something like the following?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions
.Remove<System.Data.Entity.Database.IncludeMetadataConvention>()
.Entity<Comment>()
.HasRequired(e => e.Owner)
.WithMany(t => t.Comments)
.HasForeignKey(e => e.OwnerId)
.WillCascadeOnDelete(false);
}
I'm not sure if this would work, but could you do something like this?
DatabaseSeeder<TContextType> : DropCreateDatabaseAlways<TContextType> where TContextType : DatabaseContextBase

Resources