【问题标题】:Issue with EF CF and Existing One-to-One RelationshipEF CF 问题和现有的一对一关系
【发布时间】:2012-07-23 13:10:35
【问题描述】:

我正在使用带有 EF Code First 的现有数据库并使用 modelBuilder 进行配置。我有两个表,其中一个会话可以有一个主题,类是这样的:

public class SessionItem {
   [Key]
   public int SessionId { get;set; }
   // Other Values
   public int Subject_ID { get;set; }

   public virtual Subject Subject { get;set; }
}

public class SubjectItem {
   [Key]
   public int Subject_ID { get;set; }
   // Other Values

   public virtual SessionItem Session { get;set; }
}

然后modelBuilder的代码是:

modelBuilder.Entity<SessionItem>().ToTable("tblTblSessions");
modelBuilder.Entity<Subject>().ToTable("tblTblSubjects");
modelBuilder.Entity<SessionItem>()
   .HasOptional<Subject>(u => u.Subject)                     
   .WithOptionalDependent(c => c.Session).Map(p => p.MapKey("Subject_ID"));

这首先失败,直到我从 SessionItem 类中删除了 Subject_ID,然后我收到错误:发生关系多重性约束违规:一个 EntityReference 不能有多个相关对象,但查询返回了多个相关对象。这是一个不可恢复的错误。

知道我哪里出错了吗?

【问题讨论】:

  • Subject_ID 列在数据库中不是唯一的,或者为什么查询返回“多个相关对象”?你真的想要一对一的关系还是一对多的关系?
  • 它是独一无二的,基本上有一个主题列表,每个会话可以有一个主题。我的模型构建器代码可能完全错误,我是新手!

标签: asp.net-mvc entity-framework ef-code-first one-to-one


【解决方案1】:

不幸的是,实体框架不支持 one-to-one foreign key associations,因为 EF 不知道唯一键约束是什么(会话表中的 Subject_ID 列显然具有)。

您必须通过将关系映射为一对多来解决此问题。按照@flem 对SessionItem 实体和SubjectItem 实体的回答中的映射,要么完全删除public virtual SessionItem Session { get;set; },要么将其替换为

public virtual ICollection<SessionItem> Sessions { get; set; }

对于这种关系,您不再需要使用 Fluent API 进行映射,或者如果您愿意,它应该是:

modelBuilder.Entity<SessionItem>()
    .HasOptional(se => se.Subject)                     
    .WithMany() // or WithMany(su => su.Sessions)
    .HasForeignKey(se => se.Subject_ID);

当您向该集合添加项目时,您必须确保在您的业务逻辑中不要添加多个项目,因为您的数据库中不能有超过一行具有相同的 Subject_ID,因为唯一的关键约束。当您从包括会话在内的数据库中加载主题时,会话集合要么是空的,要么只有一个元素,但不会更多。

【讨论】:

  • 这是我为使其工作所做的,我删除了主题中的导航属性,因为不需要此链接。不知道为什么在我把我的修复放在上面之前没有出现。
  • 这个答案在断言实体框架不可能建立一对一关系时是不正确的:stackoverflow.com/a/10837964/176877
  • @ChrisMoschini:我说不支持一对一外键关联,并不是说不支持一对一关联支持的。 Mortezza 您在自己的答案中链接的博客文章也这么说:“Code First(以及一般的 EF)本身并不支持一对一的外键关联”。支持的是您在答案中配置的一对一共享主键关联。您可能想再次阅读他的帖子和第二个:weblogs.asp.net/manavi/archive/2011/04/14/…
【解决方案2】:

试试这个:

public class SessionItem 
{
   [Key]
   public int SessionId { get;set; }
   // Other Values
   [ForeignKey("Subject")]
   public int? Subject_ID { get;set; }

   [ForeignKey("Subject_ID")]    
   public virtual SubjectItem Subject { get;set; }
}

您需要使 Subject_ID 外键可以为空。

【讨论】:

  • 我还需要modelBuilder中的链接还是删除它?
  • @user1166905。使用任何一个。重要的一点是可以为空的 id public int? Subject_ID
  • 删除试图建立链接的模型构建器代码并使用上面我得到了这个:多重性在关系'SessionItem_Subject'中的角色'SessionItem_Subject_Source'中无效。因为从属角色属性不是关键属性,所以从属角色的多重性的上限必须是“*”。
  • 我更改了 Subject 类以使 Sessions 成为 ICollection,然后将以下内容添加到 modelBuilder:modelBuilder.Entity() .HasOptional(u => u.Subject) .WithMany(s = > s.Sessions) .HasForeignKey(e => e.Subject_ID);这现在通过了先前的错误,但在主题上加载会话时出错(可能是因为有 0000)
  • +1 表示公平,因为您的代码已经是解决方案的 50%。
【解决方案3】:
[Table("tblTblSessions")]
public class SessionItem {
    [Key]
    public int SessionId { get; set; }

    public virtual SubjectItem Subject { get; set; }
}

[Table("tblTblSubjects")]
public class SubjectItem {
    [Key, ForeignKey("Session")]
    public int Subject_ID { get; set; }

    public virtual SessionItem Session { get; set; }
}

SubjectItem 和 SessionItem 之间的一对一关系,您可以摆脱所有模型构建器代码 - 您所做的所有表命名和一对一映射都由上面的属性和属性处理。

编辑:修正了一个错字并标记了一对一的依赖方。

【讨论】:

  • 你测试了吗?我做到了。第一:它不编译。第二:如果您修复明显的错误以使其编译(public virtual SubjectItem Subject { get; set; }),它会抛出一个异常,即一端的多重性必须是“*”(很多)。原因如我所说:EF 不支持一对一的外键关联。
  • @Slauma 我有自己的代码,具有相似的域模型,但名称不同,所以我为错字道歉(也许你应该为你的语气道歉)。我已将上述类添加到测试项目并更新了上面的示例。
  • +1 因为它在更正后现在可以工作了:) 但它是一个共享的主键关联。谁知道这对于 OP 是否足够。共享主键比 FK 一对一关联更受限制(例如,您不能将 Subject 分配给另一个 Session,只能分配具有相同键的那个)以及他似乎在问题中尝试的内容是一对一的FK。关于语气:你期待什么?你在我的回答下写了一个错误的陈述,基于此发表了反对票,然后写了一个错误的答案。它没有编译是迂腐的,只有第 2 点很重要。我很生气,对不起...
  • 顺便说一句:EF 不支持外键一对一关联的事实只是它不支持唯一键约束这一事实的结果(在 EF 中仍然不支持6)这确实是一个很大的限制,在顶部功能请求列表中排名第三:data.uservoice.com/forums/…
  • @Slauma 太好了,感谢您帮助我改进答案。我在这里有点像白色城堡,因为我们从头开始构建应用程序,我们不需要包含现有的数据库,所以不支持这种处理一对一的特定方式不会影响我们。也就是说,通过将数据复制到适合 EF 的表而不会丢失数据,即使对现有数据库进行操作也应该是可能的,所以我可以理解为什么该功能在 Microsoft 中可能一直处于低优先级。
猜你喜欢
  • 1970-01-01
  • 2011-07-30
  • 1970-01-01
  • 2010-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多