我正在尝试创建一个包含所有常见实体的基本dbcontext,这些实体将始终在多个项目中重用,例如页面,用户,角色,导航等。
为此,我有一个ContextBase类,该类继承了DbContext并定义了我想要的所有DbSet。然后,我有了一个继承ContextBase的Context类,在其中定义了项目特定的DbSet。这些类的定义如下:
public class ContextBase : DbContext
{
public virtual DbSet<User> Users { get; set; }
//more sets
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UsersConfiguration());
//add more configurations
}
}
public class Context : ContextBase
{
public DbSet<Building> Buildings { get; set; }
//some more project specific sets
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new BuildingsConfiguration());
//add more project specific configs
}
}
在我的global.asax中:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
其中Configuration引用一个类,该类继承DbMigrationsConfiguration并覆盖Seed方法。
这两个上下文类在相同的 namespace 中定义,但交叉组装(以便我可以在多个现有项目中更新基础项目而不用触摸项目特定的代码)-不确定是否相关。
我的问题:
运行此代码时,它可以正常工作,但是在数据库中查找时,它实际上创建了两个不同的数据库!一个包含所有基本实体表,一个包含BOTH基本表和定制表。 CRUD操作仅在自定义版本上执行(这显然是我想要的),但是为什么还要创建另一个版本呢?
任何帮助表示赞赏,谢谢!
更新:
以下代码是我最终得到的结果。这不是理想的方法,但是可以。我仍然很乐意获得改进方法的反馈,但与此同时,我希望这有助于进一步推进这一过程。我真的不建议这样做!它极容易出错,调试起来非常令人沮丧。我只是发布此消息,以查看是否有更好的想法或实现此目的的实现。
一个(但不是唯一的)问题仍然存在,那就是必须将MVC View 手动添加到项目中。我已经将它添加到Nuget包中,但是当VS连接到TFS时,要花2到3个小时才能将具有这么多文件的nuget包应用到该文件中。通过更多的工作和自定义的View引擎,可以预编译 View (http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html)。
该解决方案分为基础框架项目和自定义项目(每个类别包括其自己的模型和存储库模式)。框架项目打包成一个Nuget包,然后安装在任何自定义项目中,从而可以轻松地将任何项目的通用功能(例如用户,角色和权限管理,内容管理等(通常称为Boiler Plate))添加到其中。任何新项目。这允许对样板的任何改进都可以在任何现有的自定义项目中移植。
自定义数据库初始化程序:
public class MyMigrateDatabaseToLatestVersion : IDatabaseInitializer<Context>
{
public void InitializeDatabase(Context context)
{
//create the base migrator
var baseConfig = new FrameworkConfiguration();
var migratorBase = new DbMigrator(baseConfig);
//create the custom migrator
var customConfig = new Configuration();
var migratorCustom = new DbMigrator(customConfig);
//now I need to check what migrations have not yet been applied
//and then run them in the correct order
if (migratorBase.GetPendingMigrations().Count() > 0)
{
try
{
migratorBase.Update();
}
catch (System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
{
//if an error occured, the seed would not have run, so we run it again.
baseConfig.RunSeed(context);
}
}
if (migratorCustom.GetPendingMigrations().Count() > 0)
{
try
{
migratorCustom.Update();
}
catch (System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
{
//if an error occured, the seed would not have run, so we run it again.
customConfig.RunSeed(context);
}
}
}
}
框架的数据库迁移配置:
public class FrameworkConfiguration: DbMigrationsConfiguration<Repository.ContextBase>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
public void RunSeed(Repository.ContextBase context)
{
Seed(context);
}
protected override void Seed(Repository.ContextBase context)
{
// This method will be called at every app start so it should use the AddOrUpdate method rather than just Add.
FrameworkDatabaseSeed.Seed(context);
}
}
自定义项目的数据库迁移配置:
public class Configuration : DbMigrationsConfiguration<Repository.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
public void RunSeed(Repository.Context context)
{
Seed(context);
}
protected override void Seed(Repository.Context context)
{
// This method will be called at every app start so it should use the AddOrUpdate method rather than just Add.
CustomDatabaseSeed.Seed(context);
}
}
自定义DbContext
//nothing special here, simply inherit ContextBase, IContext interface is purely for DI
public class Context : ContextBase, IContext
{
//Add the custom DBsets, i.e.
public DbSet<Chart> Charts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//Assign the model configs, i.e.
modelBuilder.Configurations.Add(new ChartConfiguration());
}
}
框架DbContext:
//again nothing special
public class ContextBase: DbContext
{
//example DbSet's
public virtual DbSet<Models.User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder);
}
在global.asax AppStart中:
//first remove the base context initialiser
Database.SetInitializer<ContextBase>(null);
//set the inherited context initializer
Database.SetInitializer(new MyMigrateDatabaseToLatestVersion());
在web.config中:
<connectionStrings>
<!--put the exact same connection string twice here and name it the same as the base and overridden context. That way they point to the same database. -->
<add name="Context" connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=CMS2013; Integrated Security=SSPI;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient"/>
<add name="ContextBase" connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=CMS2013; Integrated Security=SSPI;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient"/>
</connectionStrings>
最佳答案
(摘自评论)
您正在直接创建ContextBase
对象,显然是在使用new T()
作为通用类型参数的通用方法中将其作为ContextBase
创建的,因此ContextBase
的所有初始化程序都将运行。为了防止创建ContextBase
对象(如果永远不要直接实例化它,如果应该始终使用派生的上下文),则可以将该类标记为abstract
。
关于c# - EF代码优先: inherited dbcontext creates two databases,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12816792/