Независимый от схемы модуль Entity Framework First Migrations

У меня возникают проблемы с использованием миграции Entity Framework, ориентированной на базы данных Oracle, поскольку имя схемы включено в код миграции, а для Oracle имя схемы также является именем пользователя. Моя цель - иметь независимые от схемы кодовые первые миграции (чтобы иметь возможность иметь один набор миграций для тестирования и производства).

Я уже пробовал этот подход (используя Entity Framework 6.1.3):

1) У меня есть имя схемы в Web.config:

<add key="SchemaName" value="IPR_TEST" />

2) My DbContext принимает имя схемы как параметр конструктора:

public EdistributionDbContext(string schemaName) 
    : base("EdistributionConnection")
{
    _schemaName = schemaName;
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.HasDefaultSchema(_schemaName);
}

3) Мне пришлось реализовать IDbContextFactory для Entity Framework Migrations, чтобы иметь возможность создавать мой DbContext, у которого нет конструктора без параметров:

public class MigrationsContextFactory : IDbContextFactory<EdistributionDbContext>
{
    public EdistributionDbContext Create()
    {
        return new EdistributionDbContext(GetSchemaName());
    }
}

4) Я также настроил таблицу истории миграции, которая будет помещена в правильную схему:

public class EdistributionDbConfiguration : DbConfiguration
{
    public EdistributionDbConfiguration()
    {
        SetDefaultHistoryContext((connection, defaultSchema) 
            => new HistoryContext(connection, GetSchemaName()));
    }
}

5) Я изменил код, сгенерированный для миграции, чтобы заменить имя жестко запрограммированной схемы. Например. Я заменил CreateTable("IPR_TEST.Users") на CreateTable($"{_schema}.Users"). (_schema установлено в соответствии со значением в Web.config).

6) Я использую инициализатор базы данных MigrateDatabaseToLatestVersion<EdistributionDbContext, MigrationsConfiguration>().

У меня все еще есть проблемы, когда я переключаюсь на другую схему (например, с помощью преобразования web.config) - возникает исключение, говорящее, что база данных не соответствует моей модели, и AutomaticMigrations отключены (что желательно). Когда я пытаюсь выполнить add-migration, создается новая миграция, где все объекты должны быть перемещены в другую схему (например: MoveTable(name: "IPR_TEST.DistSetGroups", newSchema: "IPR");, что определенно не нужно.

Мне кажется, что имя схемы жестко связано где-то в модели string-hash в классе миграции (например, 201509080802305_InitialCreate.resx), то есть:

<data name="Target" xml:space="preserve">
    <value>H4sIAAAAAAAEAO09227jO... </value>
</data> 

Это там способ, как сообщить Code First Migrations игнорировать имя схемы?

Ответы

Ответ 1

Вы можете создать производные DbContext и "override" modelBuilder.HasDefaultSchema(...) в OnModelCreating:

public class TestDbContext : ProductionDbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.HasDefaultSchema("TestSchema");
    }
}

Затем вы можете создавать миграции для обоих контекстов. См. этот вопрос о том, как создать две миграции в одном проекте.

Недостатком этого подхода является то, что вы должны поддерживать две отдельные миграции. Но это дает вам возможность настроить конфигурацию вашего TestDbContext.

Ответ 2

Я столкнулся с той же проблемой, и благодаря вашему aproach я наконец нашел решение, которое, кажется, работает очень хорошо:

1) У меня есть имя схемы в настройках приложения Web.config:

<add key="Schema" value="TEST" />

2) У меня есть контекст истории:

public class HistoryDbContext : HistoryContext
{
    internal static readonly string SCHEMA;

    static HistoryDbContext()
    {
        SCHEMA = ConfigurationManager.AppSettings["Schema"];
    }

    public HistoryDbContext(DbConnection dbConnection, string defaultSchema)
            : base(dbConnection, defaultSchema)
    { }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.HasDefaultSchema(SCHEMA);
    }
}

3) У меня есть конфигурация db, которая ссылается на мой контекст db истории:

public class MyDbConfiguration : DbConfiguration
{
    public MyDbConfiguration()
    {
        SetDefaultHistoryContext((connection, defaultSchema) => new HistoryDbContext(connection, defaultSchema));
    }
}

4) И это мой контекст db:

public partial class MyDbContext : DbContext
{
    public MyDbContext()
        : base("name=MyOracleDbContext")
    { }

    public static void Initialize()
    {
        DbConfiguration.SetConfiguration(new MyDbConfiguration());
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Migrations.Configuration>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema(string.Empty);
    }
}

5) Наконец, я вызываю метод Initialize из global.asax

protected void Application_Start()
{
    MyDbContext.Initialize();
}

Ключ должен установить стандартную схему контекста db в String.Empty и схему контекста истории к правильной. Поэтому, когда вы создаете свои миграции, они независимы от схемы: переменная DefaultSchema resx миграции будет пустой. Но схема контекста db истории по-прежнему правильна, чтобы пропускать проверки миграции.

Я использую следующие пакеты nugets:

<package id="EntityFramework" version="6.2.0" targetFramework="net452" />
<package id="Oracle.ManagedDataAccess" version="12.2.1100" targetFramework="net452" />
<package id="Oracle.ManagedDataAccess.EntityFramework" version="12.2.1100" targetFramework="net452" />

Вы можете успешно использовать миграции Oracle в разных базах данных.