【问题标题】:EF code first: inherited dbcontext creates two databasesEF代码优先:继承dbcontext创建两个数据库
【发布时间】:2012-10-10 10:06:06
【问题描述】:

我正在尝试创建一个基础 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 方法的类。

这两个上下文类定义在同一个命名空间中,但跨程序集(以便我可以在多个现有项目中更新基础项目而不触及项目特定代码) - 不确定这是否相关。

我的问题: 运行此代码时,它工作正常,但在查看数据库时,它实际上创建了两个不同的数据库!一个包含所有基本实体表,另一个包含基本表和自定义表。 CRUD 操作只在自定义版本上执行(这显然是我想要的),但为什么它也会创建另一个的架构?

感谢任何帮助,谢谢!

更新:

以下代码是我最终得到的。这并不理想,但它确实有效。我仍然希望获得有关改进方法的反馈,但同时我希望这有助于进一步推进这一过程。我真的不建议这样做!调试起来非常容易出错并且非常令人沮丧。我只是发布这个,看看是否有更好的想法或实现来实现这一点。

仍然存在的一个(但不是唯一的)问题是必须手动将 MVC 视图添加到项目中。我已经将它添加到Nuget包中,但是当VS连接到TFS时,应用一个包含这么多文件的nuget包需要2到3个小时。通过更多工作和自定义视图引擎,可以预编译视图 (http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html)。

解决方案分为基本框架项目和自定义项目(每个类别都包含自己的模型和存储库模式)。框架项目打包在 Nuget 包中,然后安装在任何自定义项目中,从而可以轻松添加任何项目的通用功能,如用户、角色和权限管理、内容管理等(通常称为样板)任何新项目。这允许在任何现有的自定义项目中迁移样板的任何改进。

自定义数据库初始化器:

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);
            }
        }
    }
}

Framework 的数据库迁移配置:

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>

【问题讨论】:

  • 尝试在基类中设置连接字符串(或从父类传递)即public ContextBase():base("MyConnection")
  • 你是否也从 ContextBase 调用了基础?如果添加更多配置,它会为每个配置创建一个数据库吗?尝试添加配置,然后调用 base.OnModelCreating。
  • 你有没有直接创建过ContextBase 上下文? (new ContextBase()) 如果你这样做,但你不想这样做,你可以使 ContextBase abstract 确保编译器标志尝试这样做。
  • 啊哈!感谢@hvd,我之前搜索了对基本上下文的所有引用,因此认为所有这些都已被重构,但是在调试中单步执行代码时,我意识到有通用类创建它的实例而没有显式引用基班级。谢谢,把它放在答案中,我会标记它。
  • @hofnarwillie - 和你一样,我正在研究共享公用表的多个项目(每个项目都有自己的数据库)。我正在考虑像您所做的那样在不同的程序集中创建一个“Base DbContext”类。您是否能够使用这种方法成功地在基础和派生 DbContext 中进行迁移?

标签: c# .net entity-framework ef-code-first dbcontext


【解决方案1】:

(来自cmets)

您正在直接创建ContextBase 对象,显然是new T() 在泛型方法中使用ContextBase 作为泛型类型参数,因此ContextBase 的任何初始化程序也运行。为防止创建ContextBase 对象(如果它不应该直接实例化,如果应该始终使用派生上下文),您可以将类标记为abstract

【讨论】:

    【解决方案2】:

    您的 ContextBase 似乎也有一个初始化程序。您可以通过以下方式删除它

    Database.SetInitializer<ContextBase>(null);
    

    【讨论】:

    • 谢谢,我已经根据上面@hvd 的评论找到了解决方案,但这个答案实际上也对我有所帮助。 +1
    猜你喜欢
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    • 1970-01-01
    相关资源
    最近更新 更多