Entity Framework и миграция с несколькими схемами в одном контексте

Я разрабатываю мультитенантное приложение с Entity Framework Code First. У каждого арендатора будет своя схема в базе данных, но приложение будет иметь один контекст и модель для всех арендаторов.

Entity Framwork 6 может использовать несколько схем с несколькими контекстами в одной базе данных, но я не нашел способа использовать несколько схем с одним контекстом.

Я сгенерировал миграции (с помощью командной строки) к схеме «dbo» по умолчанию. Я хотел бы обновить другие схемы, используя эти миграции.


person Bruno Albano de Souza    schedule 18.03.2013    source источник
comment
просто любопытно, почему вы должны использовать один контекст? почему не множественный контекст?   -  person cuongle    schedule 18.03.2013
comment
Продукт будет продаваться как SaaS. У всех клиентов будет одна и та же модель. Мне нужен способ создавать и поддерживать схему клиентов без изменения кода.   -  person Bruno Albano de Souza    schedule 18.03.2013
comment
@BrunoAlbanodeSouza: насколько мне известно, это одна схема для каждого контекста, но вы можете заставить контекст принимать схему в конструкторе, а затем создать метод contextfactory, который подключается к правильной схеме на основе параметров. Кроме того, это не будет универсальной ситуацией. DbContext связан с одной конкретной схемой.   -  person Brad Christie    schedule 20.03.2013
comment
@BradChristie Мне не удалось найти способ использовать мультисхему с миграциями ... Я попробую другой подход. Спасибо!   -  person Bruno Albano de Souza    schedule 25.03.2013


Ответы (4)


Хотя я согласен с тем, что несколько контекстов — это абсолютно лучший способ (и именно так я настраиваю свои собственные проекты), я хотел ответить на ваш первоначальный вопрос о том, как использовать несколько схем в одном контексте:

Внутри вашей конфигурации сопоставления для каждой модели вы можете вызвать «ToTable (myTableName, mySchema)», чтобы изменить схему, к которой принадлежит таблица:

public class MyEntityMap : EntityTypeConfiguration<MyEntity>
{
    public MyEntityMap ()
    {
        HasKey(t => t.MyId);
        Property(t => t.MyId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        ToTable("MyEntity", "MySchema");
    }
}

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

Поскольку вы заявили, что хотите использовать одну и ту же модель в разных схемах, это немного усложняет задачу, не зная больше о вашей настройке. Если вы имеете дело только с несколькими клиентами и не возражаете против поддержки их схем в коде, вы можете просто создать карту для каждой схемы (как указано выше), а затем добавить новый DbSet для каждого клиента. Если вы пытаетесь сделать это масштабируемым для большого количества клиентов, я настоятельно рекомендую изучить другой подход, потому что ваш администратор базы данных может закричать, когда увидит более 100 идентичных таблиц в разных схемах, а не использовать столбец идентификатора клиента в каждой таблице. .

person Robert Petz    schedule 16.07.2013

Глядя на эти посты

http://thedatafarm.com/data-access/digging-in-to-multi-tenant-migrations-with-ef6-alpha/

http://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/

Я придумал этот контекст

public class DataLayerBuilder : DbContext
{
    private static  string conStr = string.Empty ;
    private DataLayerBuilder(DbConnection connection, DbCompiledModel model)
    : base(connection, model, contextOwnsConnection: false){ }
    public DbSet<Person> People { get; set; }

    private static ConcurrentDictionary<Tuple<string, string>, DbCompiledModel> modelCache
        = new ConcurrentDictionary<Tuple<string, string>, DbCompiledModel>();

    /// <summary>
    /// Creates a context that will access the specified tenant
    /// </summary>
    public static DataLayerBuilder Create(string tenantSchema)
    {
        conStr = ConfigurationManager.ConnectionStrings["ConnSTRName"].ConnectionString;
        var connection = new SqlConnection(conStr);
        var compiledModel = modelCache.GetOrAdd(
            Tuple.Create(conStr, tenantSchema),
            t =>
            {

                var builder = new DbModelBuilder();
                builder.HasDefaultSchema(tenantSchema);
                builder.Entity<Person>().ToTable("People");                   
                builder.Entity<Contact>().ToTable("Contacts");
                var model = builder.Build(connection);
                return model.Compile();
            });

        return new DataLayerBuilder(connection, compiledModel);
    }

    /// <summary>
    /// Creates the database and/or tables for a new tenant
    /// </summary>
    public static void ProvisionTenant(string tenantSchema)
    {
        try
        {
            using (var ctx = Create(tenantSchema))
            {
                if (!ctx.Database.Exists())
                {
                    ctx.Database.Create();
                }
                else
                {
                    ctx.Database.Initialize(true);

                }
            }
        }
        catch (Exception)
        {

            throw;
        }
    }
}

До сих пор мне удалось добавить несколько арендаторов, используя следующий код

 public void ProvisionTest()
    {
        //Arrange
        var tenant = "test2";

        //Act
        DataLayerBuilder.ProvisionTenant(tenant);

    }
}

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

надеюсь, это поможет

person alfkonee    schedule 28.10.2014

Решение, вероятно, лучше всего рассматривать как комбинацию ответов Роберта Петца и Альфконне с несколькими дополнительными инструментами для управления соединениями и управления миграцией. Я предпочитаю уровень БД, а не схему для каждого клиента для конкретных целей резервного копирования клиента. Вы можете правильно выполнить настройку резервного копирования/восстановления IF для конкретной схемы. Но убедитесь, что все задействованные внешние инструменты имеют дело с восстановлением на основе схемы.

person phil soady    schedule 28.10.2014

Вы можете использовать несколько схем в одном контексте. В каждую из сущностей или классов вы должны добавить следующие аннотации данных:

[Table("TableName", Shema = "ShemaName")]

public class Entity
{

}
person Vantroy Sánchez    schedule 14.06.2018