Entity Framework: One Database, Multiple DbContexts. Is this a bad idea?

entity-framework ef-code-first entity-framework-4.3 dbcontext

89272 просмотра

11 ответа

My impression to date has been that a DbContext is meant to represent your database, and thus, if your application uses one database, you'd want only one DbContext. However, some colleagues want to break functional areas out into separate DbContext classes. I believe this comes from a good place -- a desire to keep the code cleaner -- but it seems volatile. My gut's telling me it's a bad idea, but unfortunately my gut feeling is not a sufficient condition for a design decision.

So I'm looking for A) concrete examples of why this might be a bad idea, or B) assurances that this'll all work out just fine.

Автор: Josh Schultz Источник Размещён: 15.07.2019 10:31

Ответы (11)


154 плюса

Решение

You can have multiple contexts for single database. It can be useful for example if your database contains multiple database schemas and you want to handle each of them as separate self contained area.

The problem is when you want to use code first to create your database - only single context in your application can do that. The trick for this is usually one additional context containing all your entities which is used only for database creation. Your real application contexts containing only subsets of your entities must have database initializer set to null.

There are other issues you will see when using multiple context types - for example shared entity types and their passing from one context to another, etc. Generally it is possible, it can make your design much cleaner and separate different functional areas but it has its costs in additional complexity.

Автор: Ladislav Mrnka Размещён: 25.06.2012 10:35

49 плюса

This thread just bubbled up on StackOverflow and so I wanted to offer another "B) assurance that this will all be fine" :)

I'm doing exactly this by way of the DDD Bounded Context pattern. I've written about it in my book, Programming Entity Framework: DbContext and it is the focus of a 50 minute module within one of my courses on Pluralsight -> http://pluralsight.com/training/Courses/TableOfContents/efarchitecture

Автор: Julie Lerman Размещён: 27.09.2012 04:26

43 плюса

I wrote this answer about four years ago and my opinion hasn't changed. But since then there have been significant developments on the micro-services front. I added micro-services specific notes at the end...

I'll weigh in against the idea, with real-world experience to back up my vote.

I was brought on to a large application that had five contexts for a single database. In the end, we ended up removing all of the contexts except for one - reverting back to a single context.

At first the idea of multiple contexts seems like a good idea. We can separate our data access into domains and provide several clean lightweight contexts. Sounds like DDD, right? This would simplify our data access. Another argument is for performance in that we only access the context that we need.

But in practice, as our application grew, many of our tables shared relationships across our various contexts. For example, queries to table A in context 1 also required joining table B in context 2.

This left us with a couple poor choices. We could duplicate the tables in the various contexts. We tried this. This created several mapping problems including an EF constraint that requires each entity to have a unique name. So we ended up with entities named Person1 and Person2 in the different contexts. One could argue this was poor design on our part, but despite our best efforts, this is how our application actually grew in the real world.

We also tried querying both contexts to get the data we needed. For example, our business logic would query half of what it needed from context 1 and the other half from context 2. This had some major issues. Instead of performing one query against a single context, we had to perform multiple queries across different contexts. This has a real performance penalty.

In the end, the good news is that it was easy to strip out the multiple contexts. The context is intended to be a lightweight object. So I don't think performance is a good argument for multiple contexts. In almost all cases, I believe a single context is simpler, less complex, and will likely perform better, and you won't have to implement a bunch of work-arounds to get it to work.

I thought of one situation where multiple contexts could be useful. A separate context could be used to fix a physical issue with the database in which it actually contains more than one domain. Ideally, a context would be one-to-one to a domain, which would be one-to-one to a database. In other words, if a set of tables are in no way related to the other tables in a given database, they should probably be pulled out into a separate database. I realize this isn't always practical. But if a set of tables are so different that you would feel comfortable separating them into a separate database (but you choose not to) then I could see the case for using a separate context, but only because there are actually two separate domains.

Regarding micro-services, one single context still makes sense. However, for micro-services, each service would have its own context which includes only the database tables relevant to that service. In other words, if service x accesses tables 1 and 2, and service y accesses tables 3 and 4, each service would have its own unique context which includes tables specific to that service.

I'm interested in your thoughts.

Автор: Francisco d'Anconia Размещён: 15.01.2015 10:02

42 плюса

Distinguishing contexts by setting the default schema

In EF6 you can have multiple contexts, just specify the name for the default database schema in the OnModelCreating method of you DbContext derived class (where the Fluent-API configuration is). This will work in EF6:

public partial class CustomerModel : DbContext
{   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("Customer");

        // Fluent API configuration
    }   
}

В этом примере будет использоваться «Клиент» в качестве префикса для таблиц вашей базы данных (вместо «dbo»). Что еще более важно, это также префикс __MigrationHistoryтаблицы (таблиц), например Customer.__MigrationHistory. Таким образом, вы можете иметь более одной __MigrationHistoryтаблицы в одной базе данных, по одной для каждого контекста. Таким образом, изменения, которые вы делаете для одного контекста, не будут мешать другому.

При добавлении миграции укажите полное имя вашего класса конфигурации (производного от DbMigrationsConfiguration) в качестве параметра в add-migrationкоманде:

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


Короткое слово о ключе контекста

Согласно этой статье MSDN « Глава - Несколько моделей, нацеленных на одну и ту же базу данных », EF 6, вероятно, справится с ситуацией, даже если существует только одна MigrationHistoryтаблица, потому что в таблице есть столбец ContextKey, чтобы различать миграции.

Однако я предпочитаю иметь более одной MigrationHistoryтаблицы, указав схему по умолчанию, как описано выше.


Использование отдельных папок миграции

В таком случае вы также можете работать с разными папками «Миграция» в своем проекте. Вы можете настроить свой DbMigrationsConfigurationпроизводный класс соответственно, используя MigrationsDirectoryсвойство:

internal sealed class ConfigurationA : DbMigrationsConfiguration<ModelA>
{
    public ConfigurationA()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelA";
    }
}

internal sealed class ConfigurationB : DbMigrationsConfiguration<ModelB>
{
    public ConfigurationB()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelB";
    }
}


Резюме

В общем, вы можете сказать, что все четко разделено: контексты, папки миграции в проекте и таблицы в базе данных.

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

Если группы сущностей не имеют ничего общего друг с другом, я бы создал отдельную базу данных для каждого из них, а также получил бы доступ к ним в разных проектах, возможно, с одним единственным контекстом в каждом проекте.

Автор: Martin Размещён: 26.08.2015 01:44

6 плюса

Простой пример для достижения ниже:

    ApplicationDbContext forumDB = new ApplicationDbContext();
    MonitorDbContext monitor = new MonitorDbContext();

Просто поместите свойства в основной контекст: (используется для создания и обслуживания БД). Примечание. Просто используйте защищенный: (Сущность здесь не раскрывается).

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("QAForum", throwIfV1Schema: false)
    {

    }
    protected DbSet<Diagnostic> Diagnostics { get; set; }
    public DbSet<Forum> Forums { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Thread> Threads { get; set; }
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

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

MonitorContext: выставить отдельную сущность здесь

public class MonitorDbContext: DbContext
{
    public MonitorDbContext()
        : base("QAForum")
    {

    }
    public DbSet<Diagnostic> Diagnostics { get; set; }
    // add more here
}

Модель диагностики:

public class Diagnostic
{
    [Key]
    public Guid DiagnosticID { get; set; }
    public string ApplicationName { get; set; }
    public DateTime DiagnosticTime { get; set; }
    public string Data { get; set; }
}

Если вы хотите, вы можете пометить все сущности как защищенные внутри основного ApplicationDbContext, а затем создать дополнительные контексты по мере необходимости для каждого разделения схем.

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

Автор: Choco Размещён: 08.10.2014 01:30

6 плюса

Напоминание: если вы объединяете несколько контекстов, убедитесь, что вы вырезали и вставили все функциональные возможности RealContexts.OnModelCreating()в свой сингл CombinedContext.OnModelCreating().

Я просто потратил время на поиски, почему мои отношения каскадного удаления не были сохранены, только чтобы обнаружить, что я не перенес modelBuilder.Entity<T>()....WillCascadeOnDelete();код из моего реального контекста в мой комбинированный контекст.

Автор: Ilan Размещён: 10.09.2012 12:34

4 плюса

Мой кишечник сказал мне то же самое, когда я наткнулся на этот дизайн.

Я работаю над базой кода, где есть три dbContexts для одной базы данных. 2 из 3 dbcontext зависят от информации из 1 dbcontext, поскольку она обслуживает административные данные. Этот дизайн наложил ограничения на то, как вы можете запрашивать ваши данные. Я столкнулся с этой проблемой, когда вы не можете объединиться через dbcontexts. Вместо этого вам необходимо запросить два отдельных dbcontexts, затем выполнить объединение в памяти или выполнить итерацию по обоим, чтобы получить комбинацию двух в качестве результирующего набора. Проблема в том, что вместо того, чтобы запрашивать конкретный набор результатов, вы теперь загружаете все свои записи в память, а затем выполняете соединение с двумя наборами результатов в памяти. Это действительно может замедлить ход событий.

Я бы задала вопростолько потому, что ты можешь, не так ли? «

См. Эту статью о проблеме, с которой я столкнулся, связанной с этим дизайном. Указанное выражение LINQ содержит ссылки на запросы, связанные с различными контекстами

Автор: Victor J. Garcia Размещён: 13.03.2013 09:16

2 плюса

В коде сначала вы можете иметь несколько DBContext и только одну базу данных. Вам просто нужно указать строку подключения в конструкторе.

public class MovieDBContext : DbContext
{
    public MovieDBContext()
        : base("DefaultConnection")
    {

    }
    public DbSet<Movie> Movies { get; set; }
}
Автор: Daniel Размещён: 10.12.2013 09:27

2 плюса

Еще немного «мудрости». У меня есть база данных, как Интернет, так и внутреннее приложение. У меня есть контекст для каждого лица. Это помогает мне сохранять дисциплинированную, защищенную сегрегацию.

Автор: Miguel Delgado Размещён: 10.02.2015 05:24

1 плюс

Вдохновленный [@JulieLerman's DDD MSDN Mag Article 2013] [1]

    public class ShippingContext : BaseContext<ShippingContext>
{
  public DbSet<Shipment> Shipments { get; set; }
  public DbSet<Shipper> Shippers { get; set; }
  public DbSet<OrderShippingDetail> Order { get; set; } //Orders table
  public DbSet<ItemToBeShipped> ItemsToBeShipped { get; set; }
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Ignore<LineItem>();
    modelBuilder.Ignore<Order>();
    modelBuilder.Configurations.Add(new ShippingAddressMap());
  }
}

public class BaseContext<TContext>
  DbContext where TContext : DbContext
{
  static BaseContext()
  {
    Database.SetInitializer<TContext>(null);
  }
  protected BaseContext() : base("DPSalesDatabase")
  {}
}   

«Если вы делаете новую разработку и хотите, чтобы Code First создавал или переносил вашу базу данных на основе ваших классов, вам нужно будет создать« uber-модель »с использованием DbContext, который включает в себя все классы и отношения, необходимые для создать полную модель, представляющую базу данных. Однако этот контекст не должен наследоваться от BaseContext. " JL

Автор: OzBob Размещён: 22.04.2016 09:06

1 плюс

Я хочу поделиться случаем, когда я думаю, что возможность иметь несколько DBContexts в одной базе данных имеет смысл.

У меня есть решение с двумя базами данных. Один для данных домена, кроме информации о пользователе. Другой предназначен исключительно для информации пользователя. Это разделение главным образом обусловлено Общим регламентом ЕС о защите данных . Имея две базы данных, я могу свободно перемещать данные домена (например, из Azure в мою среду разработки), пока пользовательские данные остаются в одном безопасном месте.

Теперь для пользовательской базы данных я реализовал две схемы через EF. Один из них по умолчанию предоставляется платформой AspNet Identity. Другой - наша собственная реализация чего-либо еще, связанного с пользователем. Я предпочитаю это решение расширению схемы ApsNet, потому что я могу легко обрабатывать будущие изменения в AspNet Identity, и в то же время разделение дает понять программистам, что «наша собственная пользовательская информация» идет в определенной пользовательской схеме, которую мы определили ,

Автор: freilebt Размещён: 25.02.2018 02:00
Вопросы из категории :
32x32