Entity Framework : 跨不同的 DbContexts 共享实体
Entity Framework : Sharing entities across different DbContexts
我正在使用 EF6 开发插件应用程序,先编写代码。
我有一个主要上下文,其中包含一个名为 User
的实体:
public class MainDataContext : DbContext
{
public MainDataContext(): base("MainDataContextCS") {}
public DbSet<User> Users { get; set; }
}
然后是 PluginX 的另一个上下文,在另一个引用基础项目的项目上:
public class PluginDataContext : DbContext
{
public PluginDataContext () : base("MainDataContextCS") {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.HasDefaultSchema("PluginX");
base.OnModelCreating(modelBuilder);
}
public DbSet<Booking> Bookings { get; set; }
}
这在同一个数据库(同一个连接字符串)上巧妙地创建了 PluginX.Bookings
Table.
这里的问题是 Booking
实体包含对 User
实体的引用:
public class Booking
{
public int Id { get; set;}
public virtual User CreationUser { get; set;}
public BookingStatus Status { get; set; }
}
并且当 运行 Add-Migration
用于插件上下文时,EF 将尝试创建另一个名为 PluginX.User
的 User
实体。
如何解决?有没有办法在另一个 DbContext
?
中共享一个共同的实体
添加 Booking 实体时,不要使用 DbSet.Add()
方法。而是使用 DbSet.Attach()
方法并将预订的 DbContext.Entry(Entity).State
属性 设置为 EntityState.Added
并确保用户的 DbContext.Entry(Entity).State
保持 EntityState.Unchanged
。
例如,不要这样做:
pluginDataContext.dbBooking.Add(myNewBooking);
这样做:
pluginDataContext.dbBooking.Attach(myNewBooking);
pluginDataContext.Entry(myNewBooking).State = EntityState.Added;
这是因为 Add()
方法将对象图中的所有实体标记为 EntityState.Added
,这将导致插入而不检查实体是否已存在于数据库中。 Attach()
方法只是让上下文开始跟踪实体。
这就是为什么我几乎从不使用 DbSet.Add()
的原因。
此解决方案可以帮助您:Entity Framework 6 Code First Migrations with Multiple Data Contexts。但是,在这种情况下,两个上下文都在同一个项目中。我不知道是否适用于两个不同项目中的上下文(如果您使用相同的 class 来映射用户,我认为应该如此)。正如博客所说,当您对 PluginX 上下文执行 运行 Add-Migration
命令时,您需要注释与用户 table 相关的生成代码。
当您处理多个上下文时,您有两个选择:
- 将每个上下文视为单独的应用程序。假设您的用户是您从 Web 服务获得的外部资源。您将无法为其添加外键。你要做的是在你的表中只添加 userId,当你需要用户详细信息时调用外部服务来获取它们,或者在 Bookings 上下文中拥有用户的本地轻型副本,你会不时更新来自用户上下文。当您使用大型系统并且想要隔离各个部分时,这种方法很好(阅读有关 DDD 和限界上下文的信息)
- 除了您的 2 个上下文之外,创建包含整个模型(用户、预订等)的第三个上下文。您将使用完整的上下文来创建迁移和维护数据库结构,但在应用程序中您将使用较小的上下文。这是一个非常简单的解决方案。使用单个上下文维护迁移很容易,它仍然允许您在无法访问不相关实体的较小上下文中隔离数据库操作。
您可以尝试使用视图,在 PluginDataContext 中将用户声明为视图,然后在执行迁移时键入方法 "create User view as ...",这样您就可以将图书与用户相关联。
我正在使用 EF6 开发插件应用程序,先编写代码。
我有一个主要上下文,其中包含一个名为 User
的实体:
public class MainDataContext : DbContext
{
public MainDataContext(): base("MainDataContextCS") {}
public DbSet<User> Users { get; set; }
}
然后是 PluginX 的另一个上下文,在另一个引用基础项目的项目上:
public class PluginDataContext : DbContext
{
public PluginDataContext () : base("MainDataContextCS") {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.HasDefaultSchema("PluginX");
base.OnModelCreating(modelBuilder);
}
public DbSet<Booking> Bookings { get; set; }
}
这在同一个数据库(同一个连接字符串)上巧妙地创建了 PluginX.Bookings
Table.
这里的问题是 Booking
实体包含对 User
实体的引用:
public class Booking
{
public int Id { get; set;}
public virtual User CreationUser { get; set;}
public BookingStatus Status { get; set; }
}
并且当 运行 Add-Migration
用于插件上下文时,EF 将尝试创建另一个名为 PluginX.User
的 User
实体。
如何解决?有没有办法在另一个 DbContext
?
添加 Booking 实体时,不要使用 DbSet.Add()
方法。而是使用 DbSet.Attach()
方法并将预订的 DbContext.Entry(Entity).State
属性 设置为 EntityState.Added
并确保用户的 DbContext.Entry(Entity).State
保持 EntityState.Unchanged
。
例如,不要这样做:
pluginDataContext.dbBooking.Add(myNewBooking);
这样做:
pluginDataContext.dbBooking.Attach(myNewBooking);
pluginDataContext.Entry(myNewBooking).State = EntityState.Added;
这是因为 Add()
方法将对象图中的所有实体标记为 EntityState.Added
,这将导致插入而不检查实体是否已存在于数据库中。 Attach()
方法只是让上下文开始跟踪实体。
这就是为什么我几乎从不使用 DbSet.Add()
的原因。
此解决方案可以帮助您:Entity Framework 6 Code First Migrations with Multiple Data Contexts。但是,在这种情况下,两个上下文都在同一个项目中。我不知道是否适用于两个不同项目中的上下文(如果您使用相同的 class 来映射用户,我认为应该如此)。正如博客所说,当您对 PluginX 上下文执行 运行 Add-Migration
命令时,您需要注释与用户 table 相关的生成代码。
当您处理多个上下文时,您有两个选择:
- 将每个上下文视为单独的应用程序。假设您的用户是您从 Web 服务获得的外部资源。您将无法为其添加外键。你要做的是在你的表中只添加 userId,当你需要用户详细信息时调用外部服务来获取它们,或者在 Bookings 上下文中拥有用户的本地轻型副本,你会不时更新来自用户上下文。当您使用大型系统并且想要隔离各个部分时,这种方法很好(阅读有关 DDD 和限界上下文的信息)
- 除了您的 2 个上下文之外,创建包含整个模型(用户、预订等)的第三个上下文。您将使用完整的上下文来创建迁移和维护数据库结构,但在应用程序中您将使用较小的上下文。这是一个非常简单的解决方案。使用单个上下文维护迁移很容易,它仍然允许您在无法访问不相关实体的较小上下文中隔离数据库操作。
您可以尝试使用视图,在 PluginDataContext 中将用户声明为视图,然后在执行迁移时键入方法 "create User view as ...",这样您就可以将图书与用户相关联。