到目前为止,我的印象是DbContext是用来表示数据库的,因此,如果您的应用程序使用一个数据库,那么您只需要一个DbContext。
然而,一些同事希望将功能区域分解为单独的DbContext类。
我相信这是出于一个好的原因——希望保持代码更干净——但它似乎不稳定。我的直觉告诉我这是个坏主意,但不幸的是,我的直觉并不是设计决策的充分条件。
所以我在寻找:
A)为什么这可能是一个坏主意的具体例子;
B)保证这一切都会顺利解决。
到目前为止,我的印象是DbContext是用来表示数据库的,因此,如果您的应用程序使用一个数据库,那么您只需要一个DbContext。
然而,一些同事希望将功能区域分解为单独的DbContext类。
我相信这是出于一个好的原因——希望保持代码更干净——但它似乎不稳定。我的直觉告诉我这是个坏主意,但不幸的是,我的直觉并不是设计决策的充分条件。
所以我在寻找:
A)为什么这可能是一个坏主意的具体例子;
B)保证这一切都会顺利解决。
当前回答
单个数据库可以有多个上下文。例如,如果您的数据库包含多个数据库模式,并且您希望将每个模式作为独立的自包含区域来处理,那么它就很有用。
问题是,当您想首先使用代码来创建数据库时,只有应用程序中的单个上下文可以做到这一点。这样做的技巧通常是一个额外的上下文,其中包含仅用于数据库创建的所有实体。仅包含实体子集的实际应用程序上下文必须将数据库初始化式设置为null。
在使用多种上下文类型时,还会遇到其他问题——例如共享实体类型及其从一个上下文传递到另一个上下文等等。一般来说,这是可能的,它可以使你的设计更干净,并分离不同的功能区域,但它的代价是额外的复杂性。
其他回答
另一点“智慧”。我有一个面向互联网和内部应用程序的数据库。每一个面向都有一个上下文。这有助于我保持纪律严明的隔离。
首先,在代码中,可以有多个DBContext和一个数据库。您只需在构造函数中指定连接字符串。
public class MovieDBContext : DbContext
{
public MovieDBContext()
: base("DefaultConnection")
{
}
public DbSet<Movie> Movies { get; set; }
}
嗯,花了相当多的时间在每个DB模式的独立DB上下文的问题上,希望它能帮助其他人…
我最近开始从事一个项目,其中一个数据库有3个模式(DB优先方法),其中一个用于用户管理。每个单独的模式都有一个DB上下文。当然,用户也与其他模式相关,例如。schema KB有一个表Topic,它有“由谁创建的”,“最后修改的是谁”等。FK到标识模式,表appuser。
这些对象是在c#中单独加载的,首先,topic是从一个上下文加载的,然后用户是通过用户id从另一个db上下文加载的-不太好,必须修复这个! (类似于使用EF 6在同一个数据库中使用多个dbcontext)
首先,我尝试将身份模式中缺失的FK指令添加到KB模式中,添加到KB DB上下文中的EF modelBuilder中。就像只有1个上下文一样,但我把它分离成2个。
modelBuilder.Entity<Topic>(entity =>
{
entity.HasOne(d => d.Creator)
.WithMany(p => p.TopicCreator)
.HasForeignKey(d => d.CreatorId)
.HasConstraintName("fk_topic_app_users");
它没有工作,因为kb db context没有任何关于用户对象的信息,postgres返回错误关系“AppUsers”不存在。选择语句没有正确的模式,字段名等信息。
我几乎放弃了,但后来我注意到一个开关“-d”运行dotnet ef dbcontext脚手架。它是-data-annotations的缩写-使用属性配置模型(在可能的情况下)。如果省略,则只使用fluent API。 通过指定这个开关,对象属性不是在db上下文OnModelCreating()中定义的,而是在对象本身上定义的,带有属性。
通过这种方式,EF获得了足够的信息,可以用正确的字段名和模式生成正确的SQL语句。
TL;DR:单独的DB上下文不能很好地处理它们之间的关系(FKs),每个上下文只有关于自己实体的信息。 当在dotnet ef dbcontext scaffold上指定“-data-annotations”开关时,这些信息不是存储在每个单独的上下文中,而是存储在DB对象本身上。
单个数据库可以有多个上下文。例如,如果您的数据库包含多个数据库模式,并且您希望将每个模式作为独立的自包含区域来处理,那么它就很有用。
问题是,当您想首先使用代码来创建数据库时,只有应用程序中的单个上下文可以做到这一点。这样做的技巧通常是一个额外的上下文,其中包含仅用于数据库创建的所有实体。仅包含实体子集的实际应用程序上下文必须将数据库初始化式设置为null。
在使用多种上下文类型时,还会遇到其他问题——例如共享实体类型及其从一个上下文传递到另一个上下文等等。一般来说,这是可能的,它可以使你的设计更干净,并分离不同的功能区域,但它的代价是额外的复杂性。
实现以下简单示例:
ApplicationDbContext forumDB = new ApplicationDbContext();
MonitorDbContext monitor = new MonitorDbContext();
只在main上下文中设置属性范围:(用于创建和维护DB) 注意:只使用protected:(这里没有显示实体)
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中将所有实体标记为受保护的,然后根据需要为每个模式分离创建额外的上下文。
它们都使用相同的连接字符串,但是它们使用不同的连接,因此不要交叉事务并注意锁定问题。一般来说,你的设计是分离的,所以这种情况不应该发生。