EF code第一:继承的DbContext创建两个数据库两个、数据库、EF、code

2023-09-03 17:44:37 作者:【孤韣菂亼,寂瘼菂杺】

我想创建一个包含所有,将永远在多个项目中重复使用的公共实体,基地的DbContext喜欢的网页,用户,角色,导航等。

在这方面,我有一个继承的DbContext,并定义了所有我想要的DbSets一个ContextBase类。然后,我有一个继承ContextBase,我定义特定的项目DbSets一个Context类。这些类定义如下:

 公共类ContextBase:的DbContext
{
    公共虚拟DbSet<使用者>用户{获得;组; }
    //多套

    保护覆盖无效OnModelCreating(DbModelBuilder模型构建器)
    {
        modelBuilder.Configurations.Add(新UsersConfiguration());
        //添加更多配置
    }
}


公共类语境:ContextBase
{
    公共DbSet<建筑>建筑{获得;组; }
    //一些项目的具体套

    保护覆盖无效OnModelCreating(DbModelBuilder模型构建器)
    {
        base.OnModelCreating(模型构建器);
        modelBuilder.Configurations.Add(新BuildingsConfiguration());
        //添加更多的项目特定的configs
    }
}
 

在我的Global.asax:

  Database.SetInitializer(新MigrateDatabaseToLatestVersion<背景下,结构>());
 
EF应用一 Code First模式

在哪里配置referes一个类继承DbMigrationsConfiguration并覆盖种子的方法。

这两个上下文类在同一个命名空间中定义的,但跨组件(以便我可以不接触该项目的具体code更新多个现有项目的基础工程) - 不知道这是相关的

我的问题: 当运行这个code,它工作正常,但看在数据库时,它实际上创建了两个不同的数据库!其中包含所有的基实体表和一个含有碱和自定义表。只执行CRUD操作上的定制版本(这是obviousely我想要什么),但为什么它创造的另外一个模式呢?

任何帮助是AP preciated,谢谢!

更新:

下面code是什么我结束了。这是不理想的,但它的作品。我仍然爱得就如何改善这种反馈,但在此期间,我希望这有助于进一步的处理。我真的不建议这样做!这是非常容易出错,而且非常令人沮丧的调试。我只是张贴这,看看是否有更好的想法或实现来实现这一目标。

一(但不是唯一的)的问题依然存在是MVC的观点,必须手动添加到项目中。我已经将其添加到的NuGet包,但需要2〜3小时,有这么多的文件应用的NuGet包时VS连接到TFS。随着更多的工作和一个自定义视图引擎的视图可以是precompiled(的 http://blog.davidebbo.com/2011/06/$p$pcompile-your-mvc-views-using.html )。

将溶液分裂成相应框架项目和自定义项目(每个类别包括其自己的模型和信息库模式)。框架项目被包装在一个的NuGet包,然后安装在任何自定义项目允许像用户,角色和权限管理,内容管理,等等任何项目的共同功能(通常称为锅炉板),以很容易地添加到任何新的项目。这使得样板的任何改善,在任何现有的自定义项目进行迁移。

自定义数据库初始化器:

 公共类MyMigrateDatabaseToLatestVersion:IDatabaseInitializer<语境GT;
{
    公共无效InitializeDatabase(上下文的背景下)
    {
        //创建基地迁移
        VAR baseConfig =新FrameworkConfiguration();
        VAR migratorBase =新DbMigrator(baseConfig);
        //创建自定义的迁移
        VAR customConfig =新配置();
        VAR migratorCustom =新DbMigrator(customConfig);

        //现在我需要检查什么的迁移​​还没有被应用
        //然后按照正确的顺序运行它们
        如果(migratorBase.GetPendingMigrations()COUNT()> 0)
        {
            尝试
            {
                migratorBase.Update();
            }
            赶上(System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
            {
                //如果发生错误,种子就不会跑,所以我们再次运行。
                baseConfig.RunSeed(上下文);
            }
        }
        如果(migratorCustom.GetPendingMigrations()COUNT()> 0)
        {
            尝试
            {
                migratorCustom.Update();
            }
            赶上(System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
            {
                //如果发生错误,种子就不会跑,所以我们再次运行。
                customConfig.RunSeed(上下文);
            }
        }
    }
}
 

框架的数据库迁移配置:

 公共类FrameworkConfiguration:DbMigrationsConfiguration< Repository.ContextBase>
{
    公共配置()
    {
        AutomaticMigrationsEnabled = FALSE;
    }

    公共无效RunSeed(Repository.ContextBase上下文)
    {
        种子(上下文);
    }

    保护覆盖无效的种子(Repository.ContextBase上下文)
    {
        //此方法被调用每一个应用程序的开始,所以应该使用AddOrUpdate方法,而不是仅仅添加。

        FrameworkDatabaseSeed.Seed(上下文);
    }
}
 

自定义项目的数据库迁移配置:

 公共类配置:DbMigrationsConfiguration< Repository.Context>
{
    公共配置()
    {
        AutomaticMigrationsEnabled = FALSE;
    }

    公共无效RunSeed(Repository.Context上下文)
    {
        种子(上下文);
    }

    保护覆盖无效的种子(Repository.Context上下文)
    {
        //此方法被调用每一个应用程序的开始,所以应该使用AddOrUpdate方法,而不是仅仅添加。

        CustomDatabaseSeed.Seed(上下文);
    }
}
 

自定义的DbContext

  //这里没有什么特别,只是继承ContextBase,IContext接口是清一色的DI
公共类语境:ContextBase,IContext
{
    //添加自定义DBsets,即
    公共DbSet<表>图表{获得;组; }

    保护覆盖无效OnModelCreating(DbModelBuilder模型构建器)
    {
        base.OnModelCreating(模型构建器);

        //指定型号的configs,即
        modelBuilder.Configurations.Add(新ChartConfiguration());
    }
}
 

框架的DbContext:

  //,没有什么特别的
公共类ContextBase:的DbContext
{
    //例如DbSet的
    公共虚拟DbSet< Models.User>用户{获得;组; }
    保护覆盖无效OnModelCreating(DbModelBuilder模型构建器);
}
 

在Global.asax AppStart的:

  //先卸下基本上下文初始化器
        Database.SetInitializer< ContextBase>(NULL);
        //设置继承的上下文初始化
        Database.SetInitializer(新MyMigrateDatabaseToLatestVersion());
 

在web.config中:

 <的ConnectionStrings>
    <! - 把完全相同的连接字符串来过两次,并将其命名一样的基础和覆盖的范围内。这样,它们指向同一个数据库。 - >
    <添加名称=语境的connectionString =数据源= \ SQLEX $ P $干燥综合征;初始目录= CMS2013;集成安全性= SSPI; MultipleActiveResultSets = TRUE;的providerName =System.Data.SqlClient的/>
    <添加名称=ContextBase的connectionString =数据源= \ SQLEX $ P $干燥综合征;初始目录= CMS2013;集成安全性= SSPI; MultipleActiveResultSets = TRUE;的providerName =System.Data.SqlClient的/>
< /的ConnectionStrings>
 

解决方案

(从评论)

您正在创建 ContextBase 的物品直接,显然是新T()在一个通用的方法 ContextBase 作为泛型类型参数,所以任何initialisers为 ContextBase 也跑。为prevent创建 ContextBase 的对象(如果它不应该被直接实例化,如果一定要使用派生上下文中),您可以将类标记为摘要

I'm trying to create a base dbcontext that contains all the common entities that will always be reused in multiple projects, like pages, users, roles, navigation etc.

In doing so I have a ContextBase class that inherits DbContext and defines all the DbSets that I want. Then I have a Context class that inherits ContextBase where I define project specific DbSets. The classes are defined as follows:

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

In my global.asax:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());

where Configuration referes to a class inheriting DbMigrationsConfiguration and overriding the Seed method.

The two context classes are defined in the same namespace, but cross assembly (in order that I may update the base project in multiple existing projects without touching the project specific code) - not sure if this is relevant.

MY PROBLEM: When running this code, it works fine, but when looking in the Database, it actually creates two different databases!! One containing all the base entity tables and one containing BOTH base and custom tables. CRUD operations are only performed on the custom version (which is obviousely what I want), but why does it create the schema of the other one as well?

Any help is appreciated, thanks!

UPDATE:

The following code is what I ended up with. It isn't ideal, but it works. I would still love to get feedback on ways to improve this, but in the meantime I hope this helps further the process. I REALLY DO NOT RECOMMEND DOING THIS! It is extremely error prone and very frustrating to debug. I'm merely posting this to see if there is any better ideas or implementations to achieve this.

One (but not the only) issue still existing is that the MVC views have to be manually added to projects. I've added it to the Nuget package, but it takes 2 to 3 hours to apply a nuget package with so many files when VS is connected to TFS. With some more work and a custom View engine the views can be precompiled (http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html).

The solution is split into the Base Framework projects and the Custom projects (each category includes its own models and repository pattern). The framework projects are packaged up in a Nuget package and then installed in any custom projects allowing the common functionality of any project like user, role and permission management, content management, etc (often referred to as the Boiler Plate) to be easily added to any new projects. This allows any improvements of the boilerplate to be migrated in any existing custom projects.

Custom Database Initializer:

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's DB Migrations Configuration:

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

Custom Project's DB Migrations Configuration:

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

The custom 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());
    }
}

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

In the global.asax AppStart:

        //first remove the base context initialiser
        Database.SetInitializer<ContextBase>(null);
        //set the inherited context initializer
        Database.SetInitializer(new MyMigrateDatabaseToLatestVersion());

In the 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>

解决方案

(from the comments)

You're creating ContextBase objects directly, apparently as new T() in a generic method with ContextBase as a generic type argument, so any initialisers for ContextBase also run. To prevent creating ContextBase objects (if it should never be instantiated directly, if the derived context should always be used), you can mark the class as abstract.