Entity Framework: Una Base de datos, Múltiples DbContexts. ¿Es una mala idea?


Mi impresión hasta la fecha ha sido que un DbContext está destinado a representar su base de datos, y por lo tanto, si su aplicación utiliza una base de datos, usted querría solo un DbContext. Sin embargo, algunos colegas quieren dividir las áreas funcionales en clases DbContext separadas. Creo que esto viene de un buen lugar desire un deseo de mantener el código más limpio but pero parece volátil. Mi instinto me dice que es una mala idea, pero desafortunadamente mi instinto no es una condición suficiente para un diseño decisión.

Así que estoy buscando A) ejemplos concretos de por qué esto podría ser una mala idea, o B) garantías de que todo esto funcionará bien.

Author: Josh Schultz, 2012-06-26

11 answers

Puede tener múltiples contextos para una sola base de datos. Puede ser útil, por ejemplo, si su base de datos contiene varios esquemas de base de datos y desea manejar cada uno de ellos como un área independiente.

El problema es cuando desea usar código primero para crear su base de datos: solo un contexto en su aplicación puede hacer eso. El truco para esto es generalmente un contexto adicional que contiene todas sus entidades que se utiliza solo para la creación de bases de datos. Su aplicación real los contextos que solo contengan subconjuntos de sus entidades deben tener el inicializador de base de datos establecido en null.

Hay otros problemas que verá al usar varios tipos de contexto, por ejemplo, tipos de entidades compartidas y su paso de un contexto a otro, etc. Generalmente es posible, puede hacer su diseño mucho más limpio y separar diferentes áreas funcionales, pero tiene sus costos en complejidad adicional.

 138
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-25 22:35:30

Este hilo acaba de burbujear en StackOverflow y así que quería ofrecer otra "B) garantía de que todo esto estará bien":)

Estoy haciendo exactamente esto a través del patrón de Contexto Delimitado DDD. He escrito sobre ello en mi libro, Programming Entity Framework: DbContext y es el foco de un módulo de 50 minutos dentro de uno de mis cursos sobre Pluralsight - > http://pluralsight.com/training/Courses/TableOfContents/efarchitecture

 47
Author: Julie Lerman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-18 22:31:25

Distiguishing contextos estableciendo el schmema por defecto

En EF6 puede tener múltiples contextos, solo especifique el nombre para el esquema de base de datos predeterminado en el método OnModelCreating de su clase derivada DbContext (donde está la configuración de Fluent-API). Esto funcionará en EF6:

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

        // Fluent API configuration
    }   
}

Este ejemplo usará "Cliente" como prefijo para sus tablas de base de datos (en lugar de "dbo"). Lo más importante es que también prefijará la(s) tabla (s) __MigrationHistory, por ejemplo Customer.__MigrationHistory. Así que usted puede tener más de uno __MigrationHistory tabla en una sola base de datos, una para cada contexto. Así que los cambios que hagas para un contexto no se meterán con el otro.

Al agregar la migración, especifique el nombre completo de su clase de configuración (derivado de DbMigrationsConfiguration) como parámetro en el comando add-migration:

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


Una palabra corta sobre la clave de contexto

De acuerdo con este artículo de MSDN "Capítulo-Múltiples Modelos Dirigidos a la Misma Base de datos " EF 6 probablemente maneje la situación incluso si solo existiera una tabla MigrationHistory, porque en la tabla hay una columna ContextKey para distinguir las migraciones.

Sin embargo, prefiero tener más de una tabla MigrationHistory especificando el esquema predeterminado como se explicó anteriormente.


Usar carpetas de migración separadas

En tal escenario, es posible que también desee trabajar con diferentes carpetas de "migración" en su proyecto. Puede configurar su DbMigrationsConfiguration derivado class accordingly using the MigrationsDirectory property:

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";
    }
}


Resumen

Con todo, se puede decir que todo está claramente separado: Contextos, carpetas de migración en el proyecto y tablas en la base de datos.

Elegiría tal solución, si hay grupos de entidades que son parte de un tema más grande, pero no están relacionados (a través de claves foráneas) entre sí.

Si los grupos de entidades no tienen nada que ver que cada uno otros, crearía una base de datos separada para cada uno de ellos y también accedería a ellos en diferentes proyectos, probablemente con un solo contexto en cada proyecto.

 38
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-22 13:18:48

Voy a opinar en contra de la idea, con la experiencia del mundo real para respaldar mi voto.

Fui llevado a una gran aplicación que tenía cinco contextos para una sola base de datos. Al final, terminamos eliminando todos los contextos excepto uno, volviendo a un solo contexto.

Al principio la idea de múltiples contextos parece una buena idea. Podemos separar nuestro acceso a los datos en dominios y proporcionar varios contextos ligeros y limpios. Suena como DDD, ¿verdad? Esto sería simplifique nuestro acceso a los datos. Otro argumento es para el rendimiento en que solo accedemos al contexto que necesitamos.

Pero en la práctica, a medida que nuestra aplicación creció, muchas de nuestras tablas compartieron relaciones a través de nuestros diversos contextos. Por ejemplo, las consultas a la tabla A en el contexto 1 también requerían unir la tabla B en el contexto 2.

Esto nos dejó con un par de malas elecciones. Podríamos duplicar las tablas en los diversos contextos. Lo intentamos. Esto creó varios problemas de mapeo, incluyendo un EF restricción que requiere que cada entidad tenga un nombre único. Así que terminamos con entidades llamadas Person1 y Person2 en los diferentes contextos. Se podría argumentar que esto fue un diseño pobre de nuestra parte, pero a pesar de nuestros mejores esfuerzos, así es como nuestra aplicación realmente creció en el mundo real.

También intentamos consultar ambos contextos para obtener los datos que necesitábamos. Por ejemplo, nuestra lógica de negocio consultaría la mitad de lo que necesitaba del contexto 1 y la otra mitad del contexto 2. Esto tuvo algunos importantes cuestión. En lugar de realizar una consulta en un solo contexto, tuvimos que realizar varias consultas en diferentes contextos. Esto tiene una penalización de rendimiento real.

Al final, la buena noticia es que fue fácil eliminar los múltiples contextos. El contexto pretende ser un objeto ligero. Así que no creo que el rendimiento sea un buen argumento para múltiples contextos. En casi todos los casos, creo que un solo contexto es más simple, menos complejo y probablemente funcionará mejor, y no tendrá que implementar un montón de soluciones para que funcione.

Pensé en una situación donde múltiples contextos podrían ser útiles. Se podría usar un contexto separado para solucionar un problema físico con la base de datos en la que realmente contiene más de un dominio. Idealmente, un contexto sería uno-a-uno a un dominio, el que sería uno-a-uno a una base de datos. En otras palabras, si un conjunto de tablas no está relacionado de ninguna manera con las otras tablas en una base de datos dada, probablemente deberían extraerse en una base de datos separada. Me doy cuenta de que esto no siempre es práctico. Pero si un conjunto de tablas son tan diferentes que te sentirías cómodo separándolas en una base de datos separada (pero eliges no hacerlo), entonces podría ver el caso de usar un contexto separado, pero solo porque en realidad hay dos dominios separados.

Estoy interesado en sus pensamientos.

 34
Author: Francisco d'Anconia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-11 00:07:37

Ejemplo simple para lograr lo siguiente:

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

Solo tiene que seleccionar las propiedades en el contexto principal: (se usa para crear y mantener la BD) Nota: Solo use protected: (Entity no está expuesto aquí)

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: Expose separate Entity here

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

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

Modelo de diagnóstico:

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

Si lo desea, puede marcar todas las entidades como protegidas dentro de ApplicationDbContext principal y, a continuación, crear contextos adicionales según sea necesario para cada separación de esquemas.

Ellos todos usan la misma cadena de conexión, sin embargo, usan conexiones separadas, por lo que no cruce transacciones y tenga en cuenta los problemas de bloqueo. En general, su separación de diseño por lo que esto no debería suceder de todos modos.

 6
Author: Choco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-08 01:30:06

Recordatorio: Si combina múltiples contextos, asegúrese de cortar n pegar toda la funcionalidad de sus varios RealContexts.OnModelCreating() en su único CombinedContext.OnModelCreating().

Simplemente perdí el tiempo buscando por qué mis relaciones de eliminación en cascada no se estaban preservando solo para descubrir que no había portado el código modelBuilder.Entity<T>()....WillCascadeOnDelete(); de mi contexto real a mi contexto combinado.

 6
Author: Ilan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-09 11:08:26

Mi instinto me dijo lo mismo cuando me encontré con este diseño.

Estoy trabajando en una base de código donde hay tres DbContexts a una base de datos. 2 de los 3 dbcontexts dependen de la información de 1 dbcontext porque sirve los datos administrativos. Este diseño ha colocado restricciones sobre cómo puede consultar sus datos. Me encontré con este problema donde no se puede unir a través de dbcontexts. En su lugar, lo que debe hacer es consultar los dos dbcontexts separados y luego hacer una unión en memoria o iterar a través de ambos para obtener la combinación de los dos como un conjunto de resultados. El problema con eso es que en lugar de consultar un conjunto de resultados específico, ahora está cargando todos sus registros en memoria y luego haciendo una combinación contra los dos conjuntos de resultados en memoria. Realmente puede ralentizar las cosas.

Me gustaría hacer la pregunta " solo porque puedes, ¿deberías?"

Vea este artículo para el problema que encontré relacionado con este diseño. La expresión LINQ especificada contiene referencias a consultas que están asociadas con diferentes contextos

 4
Author: Victor J. Garcia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:37

En code first, puede tener múltiples DbContext y solo una base de datos. Solo tiene que especificar la cadena de conexión en el constructor.

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

    }
    public DbSet<Movie> Movies { get; set; }
}
 2
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-10 09:27:10

Otro poco de "sabiduría". Tengo una base de datos frente a ambos, Internet y una aplicación interna. Tengo un contexto para cada cara. Eso me ayuda a mantener una segregación disciplinada y segura.

 2
Author: Miguel Delgado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-10 05:24:52

Inspirado en [@JulieLerman 's DDD MSDN Mag Artículo 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")
  {}
}   

"Si está haciendo un nuevo desarrollo y desea dejar que el código primero cree o migre su base de datos en función de sus clases, deberá crear un "uber-model" utilizando un DbContext que incluya todas las clases y relaciones necesarias para construir un modelo completo que represente la base de datos. Sin embargo, este contexto no debe heredar del contexto base."JL

 1
Author: OzBob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-22 09:06:15

Quiero compartir un caso, donde creo que la posibilidad de tener múltiples DbContexts en la misma base de datos tiene sentido.

Tengo una solución con dos bases de datos. Uno es para los datos del dominio excepto la información del usuario. El otro es únicamente para información del usuario. Esta división está impulsada principalmente por el Reglamento General de Protección de Datos de la UE . Al tener dos bases de datos, puedo mover libremente los datos del dominio (por ejemplo, de Azure a mi entorno de desarrollo) siempre que los datos del usuario se queda en un lugar seguro.

Ahora para la base de datos de usuarios he implementado dos esquemas a través de EF. Uno es el predeterminado proporcionado por AspNet Identity framework. La otra es nuestra propia implementación de cualquier otra cosa relacionada con el usuario. Prefiero esta solución en lugar de extender el esquema ApsNet, porque puedo manejar fácilmente futuros cambios a la identidad AspNet y al mismo tiempo la separación deja claro a los programadores ,que "nuestra propia información de usuario" va en el esquema de usuario específico que tenemos definido.

 0
Author: freilebt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-25 14:00:02