Заявление об ограничении ответственности

Я пытался найти ответ, но решения, которые я нашел, мне не подходят.

Ситуация

У меня есть проект EF Core 2.2 со схемой, определенной в файле конфигурации.

Например, у каждого пользователя должна быть своя собственная среда с таблицами данных, таблицей миграции и т. Д. Итак, существует файл конфигурации (не обязательно под контролем версий), который определяет имя схемы.

Что не удается

При создании миграции инструменты EFCore жестко связывают имя схемы (каким бы оно ни было в момент выполнения) с моментальным снимком и кодом миграции.

Мне удалось создать таблицу миграции в произвольной схеме.

services
   .AddDbContext<AppDbContext>(opt =>
       opt.UseSqlServer(
           cred.Database.ConnectionString,
           opt => opt.MigrationsHistoryTable(
               "__EFMigrationsHistory", cred.Database.Schema)));

Но таблицы данных по-прежнему создаются в схеме, определенной во время запуска инструмента Add-Migration.

Что пробовали

modelBuilder.HasDefaultSchema(mSchema.Schema)

Похоже, не действует.

modelBuilder.Entity<>.ToTable(nameof(...), mSchema.Schema)

Это (вместе с предыдущим кодом) заставляет запрос искать таблицу в новой схеме. Мне не нужно играть с IModelCacheKey, поскольку существует только одна модель, поэтому я не стал.

Remove schema parameters from migration CS file

Я думал, что это заставит EF Core использовать указанную выше схему по умолчанию. Вместо этого таблицы данных были созданы в схеме dbo.

Обновить

Судя по всему, ядро ​​EFCore не имеет проблем с использованием нескольких схем. Это инструмент Add-Migration, который генерирует миграцию с жестко запрограммированной схемой. Ответ по какой-либо причине миграция и определение модели полностью разделены.

Может ли кто-нибудь указать мне правильное направление?

0
Mike 3 Дек 2019 в 17:12

1 ответ

Лучший ответ

На всякий случай - я нашел решение. он включает в себя внедрение некоторого кода в конвейер миграции EF, как это.

В Startup.ConfigureServices:

services
    .AddEntityFrameworkSqlServer()
    .AddScoped<IMigrationsSqlGenerator, SchemaMigrationsSqlGenerator>()
    .AddScoped<MigrationsSqlGenerator, SqlServerMigrationsSqlGenerator>()
    .AddDbContext<AppDbContext>((serviceProvider, sqlOpt) =>
    {
        sqlOpt.UseInternalServiceProvider(serviceProvider)
              .UseSqlServer(
                  connectionString,
                  // Make sure thge migration table is also in that schema
                  opt => opt.MigrationsHistoryTable("__EFMigrationsHistory", yourSchema));
    });

И создайте новый класс:

/// <summary>
/// A class injected into the SQL command generation
/// in order to replace the schema with the one we want.
/// </summary>
public sealed class SchemaMigrationsSqlGenerator : IMigrationsSqlGenerator
{
    #region Fields

    private readonly MigrationsSqlGenerator mOriginal;

    private readonly ISchemaStorage mSchema;

    #endregion


    #region Init aned clean-up

    /// <summary>
    /// Constructor for dependency injection.
    /// </summary>
    /// <param name="original">Previously used SQL generator</param>
    /// <param name="schema">Where the schema name is stored</param>
    public SchemaMigrationsSqlGenerator(MigrationsSqlGenerator original, ISchemaStorage schema)
    {
        mOriginal = original;
        mSchema = schema;
    }

    #endregion


    #region IMigrationsSqlGenerator API

    /// <inheritdoc />
    /// <remarks>
    /// Overwrite the schema generated during Add-Migration,
    /// then call the original SQL generator.
    /// </remarks>
    IReadOnlyList<MigrationCommand> IMigrationsSqlGenerator.Generate(
        IReadOnlyList<MigrationOperation> operations, IModel model)
    {
        foreach (var operation in operations)
        {
            switch (operation)
            {
                case SqlServerCreateDatabaseOperation _:
                    break;

                case EnsureSchemaOperation ensureOperation:
                    ensureOperation.Name = mSchema.Schema;
                    break;

                case CreateTableOperation tblOperation:
                    tblOperation.Schema = mSchema.Schema;
                    break;

                case CreateIndexOperation idxOperation:
                    idxOperation.Schema = mSchema.Schema;
                    break;

                default:
                    throw new NotImplementedException(
                        $"Migration operation of type {operation.GetType().Name} is not supported by SchemaMigrationsSqlGenerator.");
            }
        }

        return mOriginal.Generate(operations, model);
    }

    #endregion
}

Приведенный выше код заставляет выполнять миграции в схеме, внедренной с помощью тривиального интерфейса ISchemaStorage.

1
Mike 5 Дек 2019 в 16:33
Привет, я нашел ваш ответ весьма полезным. Но по какой-то причине SchemaMigrationsSqlGenerator не используется EF / DI, если я не заменю его следующим образом: sqlOpt.ReplaceService () Вы тоже испытывали нечто подобное? Я бы не возражал использовать ReplaceService (), если бы он работал, но по какой-то причине я получаю сообщение об ошибке, что DI не может разрешить одну из других служб, переданных SchemaMigrationsSqlGenerator DI / ctor.
 – 
Michał Górnicki
13 Июл 2020 в 18:28
Вы заметили звонок UseInternalServiceProvider?
 – 
Mike
14 Июл 2020 в 21:59