Entity framework 实体框架:一个数据库,多个DBContext。这是个坏主意吗?
到目前为止,我的印象是Entity framework 实体框架:一个数据库,多个DBContext。这是个坏主意吗?,entity-framework,ef-code-first,entity-framework-4.3,dbcontext,Entity Framework,Ef Code First,Entity Framework 4.3,Dbcontext,到目前为止,我的印象是DbContext表示您的数据库,因此,如果您的应用程序使用一个数据库,您只需要一个DbContext 但是,一些同事希望将功能区域划分为单独的DbContext类 我相信这是出于一个好的地方——一种保持代码更干净的愿望——但它似乎不稳定。我的直觉告诉我这是个坏主意,但不幸的是,我的直觉并不是设计决策的充分条件 所以我在寻找: A) 这可能是一个坏主意的具体例子 B) 保证这一切都会顺利进行。您可以为单个数据库提供多个上下文。例如,如果您的数据库包含多个数据库模式,并且希望
DbContext
表示您的数据库,因此,如果您的应用程序使用一个数据库,您只需要一个DbContext
但是,一些同事希望将功能区域划分为单独的DbContext
类
我相信这是出于一个好的地方——一种保持代码更干净的愿望——但它似乎不稳定。我的直觉告诉我这是个坏主意,但不幸的是,我的直觉并不是设计决策的充分条件
所以我在寻找:
A) 这可能是一个坏主意的具体例子
B) 保证这一切都会顺利进行。您可以为单个数据库提供多个上下文。例如,如果您的数据库包含多个数据库模式,并且希望将它们作为独立的自包含区域来处理,那么它可能非常有用 问题是,当您想首先使用代码来创建数据库时,只有应用程序中的单个上下文才能做到这一点。这方面的诀窍通常是一个额外的上下文,其中包含仅用于创建数据库的所有实体。仅包含实体子集的实际应用程序上下文必须将数据库初始值设定项设置为null
在使用多个上下文类型时,您还会看到其他问题,例如共享实体类型及其从一个上下文到另一个上下文的传递等。通常情况下,它可以使您的设计更加清晰,并分隔不同的功能区域,但它的成本会增加复杂性。提醒:如果要组合多个上下文,请确保剪切并粘贴各种
RealContexts.OnModelCreating()
中的所有功能到单个CombinedContext.OnModelCreating()中
我只是浪费时间寻找我的级联删除关系没有被保留的原因,结果发现我没有移植
modelBuilder.Entity()…WillCascadeOnDelete()代码>从我的真实环境编码到我的组合环境。当我遇到这个设计时,我的直觉告诉我同样的事情。
我正在开发一个代码库,其中一个数据库有三个DBContext。3个dbcontext中有2个依赖于1个dbcontext中的信息,因为它提供管理数据。此设计对如何查询数据设置了限制。我遇到了一个无法跨DBContext连接的问题。相反,您需要做的是查询两个单独的DBContext,然后在内存中进行连接或迭代,以获得这两个的组合作为结果集。这样做的问题不是查询特定的结果集,而是将所有记录加载到内存中,然后对内存中的两个结果集进行联接。它真的能让事情变慢。
我会问这样一个问题“仅仅因为你可以,是吗?”
有关我遇到的与此设计相关的问题,请参阅本文。
在代码优先中,可以有多个DBContext和一个数据库。您只需在构造函数中指定连接字符串
public class MovieDBContext : DbContext
{
public MovieDBContext()
: base("DefaultConnection")
{
}
public DbSet<Movie> Movies { get; set; }
}
公共类MovieDBContext:DbContext
{
公共电影背景()
:base(“默认连接”)
{
}
公共DbSet电影{get;set;}
}
实现以下功能的简单示例:
ApplicationDbContext forumDB = new ApplicationDbContext();
MonitorDbContext monitor = new MonitorDbContext();
只需在主上下文中定义属性的范围:(用于创建和维护数据库)
注意:仅使用受保护的:(此处未显示实体)
如果愿意,可以在主ApplicationDbContext中将所有实体标记为受保护,然后根据需要为每个模式分离创建其他上下文
它们都使用相同的连接字符串,但是它们使用单独的连接,因此不要交叉事务,并注意锁定问题。一般来说,你设计的分离是不应该发生的。我大约四年前写了这个答案,我的观点没有改变。但从那时起,微服务领域出现了重大发展。我在最后添加了微服务特定的注释
我会用现实世界的经验来支持我的投票,我会反对这个想法
我被带到一个大型应用程序中,该应用程序对一个数据库有五个上下文。最后,我们删除了除一个上下文之外的所有上下文-恢复到单个上下文
起初,多个上下文的想法似乎是个好主意。我们可以将数据访问划分为多个域,并提供几个干净的轻量级上下文。听起来像DDD,对吗?这将简化我们的数据访问。另一个论点是性能,因为我们只访问我们需要的上下文
但在实践中,随着应用程序的增长,许多表在不同的上下文中共享关系。例如,对上下文1中的表A的查询还需要连接上下文2中的表B
这给我们留下了几个糟糕的选择。我们可以在不同的上下文中复制这些表。我们试过这个。这造成了几个映射问题,包括要求每个实体具有唯一名称的EF约束。因此,我们在不同的上下文中得到了名为Person1和Person2的实体。有人可能会说这是我们糟糕的设计,但尽管我们尽了最大的努力,这就是我们的应用程序在现实世界中实际增长的方式
我们还尝试查询这两个上下文以获取所需的数据。例如,我们的业务逻辑将从上下文1查询所需内容的一半,从上下文2查询另一半。这有一些重大问题。我们必须跨不同的上下文执行多个查询,而不是针对单个上下文执行一个查询。这是一个真正的性能惩罚
最后,好消息是,可以很容易地去掉多个上下文。上下文旨在成为轻量级对象。所以我不瘦
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);
}
}
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; }
}
public partial class CustomerModel : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("Customer");
// Fluent API configuration
}
}
add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS
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";
}
}
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")
{}
}
modelBuilder.Entity<Topic>(entity =>
{
entity.HasOne(d => d.Creator)
.WithMany(p => p.TopicCreator)
.HasForeignKey(d => d.CreatorId)
.HasConstraintName("fk_topic_app_users");