Сначала код EF: унаследованный dbcontext создает две базы данных

Я пытаюсь создать базовый dbcontext, содержащий все общие сущности, которые всегда будут повторно использоваться в нескольких проектах, таких как страницы, пользователи, роли, навигация и т. Д.

При этом у меня есть класс ContextBase, который наследует DbContext и определяет все DbSets, которые мне нужны. Затем у меня есть класс Context, который наследует ContextBase, где я определяю DbSets для конкретного проекта. Классы определены следующим образом:

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, но для применения пакета nuget с таким количеством файлов, когда VS подключен к TFS, требуется от 2 до 3 часов. Приложив дополнительную работу и настроив механизм просмотра, представления можно предварительно скомпилировать (http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html).

Решение разделено на проекты Base Framework и пользовательские проекты (каждая категория включает свои собственные модели и шаблон репозитория). Фреймворк-проекты упаковываются в пакет 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);
            }
        }
    }
}

Конфигурация миграции БД 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);
}

В AppStart global.asax:

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

person hofnarwillie    schedule 10.10.2012    source источник
comment
Попробуйте установить строку подключения в базовом классе (или передать ее от родительского), то есть public ContextBase (): base (MyConnection)   -  person Not loved    schedule 10.10.2012
comment
Вы тоже вызываете базу из ContextBase? Что, если вы добавите больше конфигураций, создаст ли он базу данных для каждой конфигурации? Попробуйте добавить конфигурации, затем вызовите base.OnModelCreating.   -  person MrFox    schedule 10.10.2012
comment
Вы когда-нибудь создавали ContextBase контекст напрямую? (new ContextBase()) Если вы это сделаете, а вы не хотите, вы можете сделать ContextBase abstract, чтобы убедиться, что флаги компилятора попытаются это сделать.   -  person    schedule 10.10.2012
comment
Ага! Спасибо @hvd, я искал все ссылки на базовый контекст раньше и подумал, что все они были рефакторингом, но при пошаговом прохождении кода в отладке я понял, что существуют общие классы, создающие его экземпляры без явной ссылки на базовый класс. Спасибо, вставьте в ответ и я отмечу.   -  person hofnarwillie    schedule 10.10.2012
comment
@hofnarwillie - Как и вы, я изучаю несколько проектов (каждый со своей собственной базой данных), которые используют общие таблицы. Я рассматриваю возможность создания класса Base DbContext в другой сборке, как это сделали вы. Удалось ли вам выполнить миграции как в базовом, так и в производном DbContexts с помощью этого подхода?   -  person Scott    schedule 11.01.2014
comment
@Scott: Я заставил его работать после огромной борьбы. Я бы не рекомендовал это, если это не является абсолютно необходимым, но в остальном это возможно. Вам нужно будет написать свой собственный инициализатор базы данных, который проверяет вручную, нужно ли обновлять базу данных. Также имейте в виду, что вы должны применять обновления в порядке их создания (например, Base 1, Custom 1, Custom 2, Base 2, Base 3 и т. Д.). Надеюсь это поможет. Приведенный выше код нужно было сильно изменить. Личное сообщение мне, если вам нужно больше.   -  person hofnarwillie    schedule 13.01.2014
comment
@hofnarwillie Спасибо за ответ! Мне не удалось найти способ PM, но пытались ли вы скопировать или связать общие базовые файлы миграции с каждой папкой «Миграции» производных проектов для поддержания порядка? Я предполагаю, что есть проблемы с отсутствием записей в базах данных в их таблицах _MigrationHistory. В качестве альтернативы, считаете ли вы, что был бы больший успех, если бы каждый проект полностью управлял своим собственным DbContext, не производя от базового DbContext; тогда в каждый настраиваемый проект просто нужно добавить новую миграцию при изменении таблицы из общей сборки (содержащей классы для общих таблиц).   -  person Scott    schedule 13.01.2014
comment
@Scott Я обновил свой пост, добавив в него код, который у меня получился. Надеюсь, это поможет.   -  person hofnarwillie    schedule 14.01.2014


Ответы (2)


(из комментариев)

Вы создаете ContextBase объекты напрямую, очевидно, как new T() в универсальном методе с ContextBase в качестве аргумента универсального типа, поэтому любые инициализаторы для ContextBase также выполняются. Чтобы предотвратить создание ContextBase объектов (если он никогда не должен создаваться напрямую, если производный контекст должен всегда использоваться), вы можете пометить класс как abstract.

person Community    schedule 10.10.2012

Ваш ContextBase, похоже, тоже имеет инициализатор. Вы можете удалить его,

Database.SetInitializer<ContextBase>(null);
person Stephan Bauer    schedule 10.10.2012
comment
Спасибо, я нашел решение в соответствии с комментарием @ hvd выше, но этот ответ мне тоже помог. +1 - person hofnarwillie; 10.10.2012