使用Entity Framework Code First 4.3.1时,可以创建多对多1对1的关系。也就是说,关系的每一端都有一个实体。
可以将1对1关系配置为必需或必需 - 可选 ^。但是,当我在两者之间切换时,我看不出任何差异:
因此,我可以创建 RequiredPrincipalAs 记录,但没有相应的 RequiredDependentAs 记录,尽管关系配置为 required-required 。这似乎与 HasRequired(...):
的文档相矛盾从此实体类型配置所需的关系。除非指定了此关系,否则无法将实体类型的实例保存到数据库。数据库中的外键将是不可为空的。
必需的关系实体:
public class RequiredPrincipalA
{
public int Id { get; set; }
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
必需 - 可选关系实体:
public class RequiredPrincipalB
{
public int Id { get; set; }
public virtual OptionalDependentB DependentB { get; set; }
}
public class OptionalDependentB
{
public int Id { get; set; }
public virtual RequiredPrincipalB PrincipalB { get; set; }
}
DbContext和模型配置:
public class AppContext : DbContext
{
public DbSet<RequiredPrincipalA> PrincipalAs { get; set; }
public DbSet<RequiredDependentA> DependentAs { get; set; }
public DbSet<RequiredPrincipalB> PrincipalBs { get; set; }
public DbSet<OptionalDependentB> DependentBs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RequiredPrincipalA>()
.HasRequired(o => o.DependentA)
.WithRequiredPrincipal(o => o.PrincipalA);
modelBuilder.Entity<RequiredPrincipalB>()
.HasOptional(o => o.DependentB)
.WithRequired(o => o.PrincipalB);
}
}
测试代码:
Database.SetInitializer(new DropCreateDatabaseAlways<AppContext>());
using (var ctx = new AppContext())
{
ctx.Database.Initialize(force: false);
ctx.PrincipalAs.Add(new RequiredPrincipalA());
ctx.PrincipalBs.Add(new RequiredPrincipalB());
ctx.SaveChanges();
}
我知道我可以在 RequiredPrincipalA.DependentA 和 RequiredDependentA.PrincipalA 的导航属性中添加 [必需] 数据属性。这将导致EF验证以防止上述情况。但是,我不想这样做,因为它还验证了在更新现有实体时填充的导航属性。这意味着应用程序必须为每次更新预先获取关系另一端的实体。
为什么在改变 required-required 和 required-optional 之间的关系时,我看不出EF的行为有任何差异?
^ 请注意,也支持optional-optional,但这不是我的问题的一部分。配置可选 - 可选关系时,生成的数据库模式和运行时行为存在明显差异。
答案 0 :(得分:13)
我不知道为什么在这种情况下允许必需 - 但它不能存在于数据库中,因为关系是在主键上构建的。必需 - 表示如果相关B不存在则不能插入A,如果相关A不存在则不能插入B =&gt;既不能插入A也不能插入B.
数据库关系始终是主体和依赖实体 - 主体可以始终存在而不依赖。
只有当A和B都映射到同一个表(table splitting)时,才能实现EF中所需的实际要求,因为在这种情况下,它们都插入了单个插入命令。
答案 1 :(得分:5)
不是一个真正的答案,但我还有更多的话要说而不是评论。但是你知道,我写了900页的书......这就是我的翻版方式。 :)
奇怪的是,我希望流畅的配置行为与数据注释的行为方式相同,并且我很困惑它没有这样做。 (我已经给Rowan Miller写了一个链接到这个帖子以获得他的反馈。)我的意思是:在SaveChanges期间验证约束。
在数据库方面,我与Ladislav合作。在模型中,EF使用相关实体的键定义1:1。但是在数据库中,你不能在两个表中都有FK,所以只有数据库中的依赖表才需要它的PK映射到主表中现有PK的约束。
最后,如果你不打算总是处理完整的图表,我理解你不希望EF强制执行这种关系的原因。我认为1:1的关系是EF关系映射中最容易混淆的,我总是发现自己不得不回过头来提醒规则以及事情应该如何运作。
答案 2 :(得分:0)
老问题。但由于 EF6 仍在使用,甚至可用于 .Net 标准和这个问题可能是一个真正的麻烦,我认为值得一提的是我在其他答案中找不到的东西。
HasRequired - WithRequiredPrincipal
和 HasOptional - WithRequired
确实产生相同的数据库架构和相同的运行时行为。也就是说,通过这两种映射,可以在没有依赖实体的情况下保存主体,并在以后删除依赖实体。 HasRequired
就这么多。
但是有一种方法可以让 EF 在创建实体时验证所需的关系,只需添加一个 [Required]
属性:
public class RequiredPrincipalA
{
public int Id { get; set; }
[Required] // <== here
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
如前所述,仅在创建实体时。仍然可以设置 RequiredPrincipalA.RequiredDependentA = null
并成功保存。但我认为,幸运的是,在代码中发生这种情况的可能性远低于忘记设置所需的依赖项。