entity-framework - net - dbcontext entity framework core
Marco de la entidad: una base de datos, múltiples DbContexts. ¿Es una mala idea? (11)
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 usa una base de datos, solo querría un DbContext. Sin embargo, algunos colegas quieren dividir las áreas funcionales en clases separadas de DbContext. Creo que esto proviene de un buen lugar, un deseo de mantener el código más limpio, pero parece volátil. Mi instinto me dice que es una mala idea, pero desafortunadamente mi instinto no es una condición suficiente para una decisión de diseño.
Así que estoy buscando A) ejemplos concretos de por qué esto podría ser una mala idea, o B) garantías de que todo saldrá bien.
Ejemplo simple para lograr lo siguiente:
ApplicationDbContext forumDB = new ApplicationDbContext();
MonitorDbContext monitor = new MonitorDbContext();
Solo alcance las propiedades en el contexto principal: (usado para crear y mantener el DB) 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: Exponer entidad separada aquí
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, luego crear contextos adicionales según sea necesario para cada separación de esquemas.
Todos usan la misma cadena de conexión, sin embargo, usan conexiones separadas, por lo tanto, 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.
Este hilo simplemente surgió en y por eso quería ofrecer otra "B) garantía de que todo estará bien" :)
Estoy haciendo exactamente esto a través del patrón de contexto delimitado de DDD. He escrito sobre esto en mi libro, Programming Entity Framework: DbContext y es el foco de un módulo de 50 minutos dentro de uno de mis cursos en Pluralsight -> http://pluralsight.com/training/Courses/TableOfContents/efarchitecture
Inspirado por [@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")
{}
}
"Si está realizando un nuevo desarrollo y desea permitir que Code First cree o migre su base de datos en función de sus clases, deberá crear un" súper modelo "utilizando un DbContext que incluya todas las clases y relaciones necesarias para construya un modelo completo que represente la base de datos. Sin embargo, este contexto no debe heredar de BaseContext ". JL
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 en una base de datos. 2 de los 3 dbcontextos dependen de la información de 1 dbcontext porque sirve los datos administrativos. Este diseño ha impuesto restricciones sobre cómo puede consultar sus datos. Me encontré con este problema donde no se puede unir a través de dbcontexts. En cambio, lo que debe hacer es consultar los dos dbcontexts por separado, luego hacer una combinación en la 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 la memoria y luego haciendo una combinación con los dos conjuntos de resultados en la memoria. Realmente puede desacelerar las cosas.
Haría la pregunta " solo porque puedes, ¿deberías? "
Vea este artículo por el problema que encontré relacionado con este diseño. La expresión LINQ especificada contiene referencias a consultas que están asociadas a contextos diferentes
Otro poco de "sabiduría". Tengo una base de datos frente a Internet, y una aplicación interna. Tengo un contexto para cada cara. Eso me ayuda a mantener una segregación disciplinada y segura.
Primero en el código, 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; }
}
Puede tener múltiples contextos para una sola base de datos. Puede ser útil, por ejemplo, si su base de datos contiene múltiples esquemas de base de datos y desea manejar cada uno de ellos como un área independiente separada.
El problema es cuando desea usar código primero para crear su base de datos; solo un contexto único en su aplicación puede hacerlo. El truco para esto suele ser un contexto adicional que contiene todas las entidades que se usa solo para la creación de la base de datos. Sus contextos reales de aplicación que contienen solo subconjuntos de sus entidades deben tener el inicializador de base de datos establecido en nulo.
Hay otros problemas que verá al usar múltiples 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 su costos en complejidad adicional.
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 datos de 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 permanezcan 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 el marco de AspNet Identity. El otro es nuestra implementación de cualquier otra cosa relacionada con el usuario. Prefiero esta solución que extender el esquema de ApsNet, porque puedo manejar fácilmente los cambios futuros en AspNet Identity y al mismo tiempo la separación deja en claro a los programadores, que "nuestra propia información de usuario" va en el esquema de usuario específico que hemos definido .
Recordatorio: si combina varios contextos, asegúrese de cortar y pegar todas las funciones en sus diversos RealContexts.OnModelCreating()
en su único CombinedContext.OnModelCreating()
.
Simplemente perdí el tiempo buscando por qué mis relaciones de eliminación en cascada no se preservaban solo para descubrir que no había portado el modelBuilder.Entity<T>()....WillCascadeOnDelete();
código de mi contexto real en mi contexto combinado.
Sopesaré la idea, con la experiencia del mundo real para respaldar mi voto.
Me llevaron a una aplicación grande 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 contextos múltiples parece una buena idea. Podemos separar nuestro acceso a datos en dominios y proporcionar varios contextos ligeros y limpios. Suena como DDD, ¿verdad? Esto simplificaría nuestro acceso a los datos. Otro argumento es para el rendimiento en el sentido de 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ó un par de malas elecciones. Podríamos duplicar las tablas en los diversos contextos. Probamos esto. Esto creó varios problemas de mapeo, incluida una restricción EF 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 este 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 tratamos de consultar ambos contextos para obtener los datos que necesitábamos. Por ejemplo, nuestra lógica comercial consultaría la mitad de lo que necesitaba del contexto 1 y la otra mitad del contexto 2. Esto tuvo algunos problemas importantes. En lugar de realizar una consulta en un solo contexto, tuvimos que realizar múltiples consultas en diferentes contextos. Esto tiene una penalización de rendimiento real.
Al final, la buena noticia es que fue fácil quitar los múltiples contextos. El contexto está destinado a ser un objeto ligero. Entonces, no creo que el rendimiento sea un buen argumento para contextos múltiples. En casi todos los casos, creo que un solo contexto es más simple, menos complejo y probablemente tenga un mejor rendimiento, y no tendrá que implementar un montón de soluciones alternativas para que funcione.
Pensé en una situación en la que múltiples contextos podrían ser útiles. Se puede 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 para un dominio, que sería uno-a-uno para 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 es tan diferente que se sentiría cómodo separándolas en una base de datos separada (pero elige 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 tus pensamientos
Contextos llamativos estableciendo el schmema predeterminado
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 DbContext
-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 las tablas de su base de datos (en lugar de "dbo"). Lo que es más importante, también incluirá el prefijo __MigrationHistory
table (s), por ejemplo, Customer.__MigrationHistory
. Por lo tanto, puede tener más de una __MigrationHistory
table en una única base de datos, una para cada contexto. Por lo tanto, los cambios que realice en un contexto no interferirá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 breve en la tecla de contexto
Según este artículo de MSDN " Capítulo - Modelos múltiples dirigidos a la misma base de datos " EF 6 probablemente manejaría 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 clase derivada DbMigrationsConfiguration
consecuencia usando la propiedad 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";
}
}
Resumen
Con todo, puede decir que todo está limpiamente separado: Contextos, carpetas de migración en el proyecto y tablas en la base de datos.
Elegiría una solución así, si hay grupos de entidades que son parte de un tema más grande, pero que no están relacionadas (a través de claves externas) entre sí.
Si los grupos de entidades no tienen nada que ver el uno con el otro, 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 único contexto en cada proyecto.