【问题标题】:Code First Foreign Keys between two entities两个实体之间的代码优先外键
【发布时间】:2015-09-30 14:24:12
【问题描述】:

好的,我在这里遗漏了什么,或者这只能通过数据注释来完成?

我有一个文档实体模型,它有一个外键指向添加文档的用户(一对一关系):

[Table("Documents", Schema = "Configuration")]
public class Document : IPrimaryKey {
    [Key]
    public long Id { get; set; }

    [Required]
    public string OrginalName { get; set; }

    [Required]
    public DocumentTypes DocumentType { get; set; }

    [Required]
    public MIMETypes MIMEType { get; set; }

    [Required]
    public byte[] Data { get; set; }

    [DefaultValue(false)]
    public bool IsPublic { get; set; }

    [Required]
    public DateTimeOffset DateTimeAdded { get; set; }

    [Required]
    public long AddedByUser { get; set; }

    [ForeignKey("AddedByUser")]
    public virtual Details Details { get; set; }
}

然后我有一个用户(详细信息)实体,它可以有一个图像文件(存储在文档实体模型中(无|一对一关系):

[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
    [Key]
    public long Id { get; set; }

    [Required]
    public string UserId { get; set; }

    [ForeignKey("UserId")]
    public AppUser User { get; set; }

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Address> Addresses { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Email> Emails { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<PhoneNumber> PhoneNumbers { get; set; }

    public ICollection<NotificationHistory> NotificationHistory { get; set; }
    public long TimeZoneId { get; set; }

    public long? ImageId { get; set; }

    [ForeignKey("ImageId")]
    public virtual Document Document { get; set; }

    [ForeignKey("TimeZoneId")]
    public virtual TimeZone TimeZone { get; set; }
}

当我尝试创建迁移时出现此错误:

无法确定之间关联的主体端 类型“StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details” 和 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'。 此关联的主体端必须显式配置 使用关系流式 API 或数据注释。

更新:

虽然仍在研究此问题,但我进行了两项更改并能够绕过错误,但这在我的数据库中产生了意外的结果。

在我添加的文档实体中:

public virtual ICollection<Details> Details { get; set; }

在我添加的详细信息(用户)实体中:

puflic virtual ICollection<Document> Documents { get; set; }

在我的数据库表中,我现在在我想要的字段上有一个外键,但我分别为每个字段设置了一个辅助外键。

我尝试只删除单个虚拟引用,只留下 ICollection 虚拟引用,现在我根本没有外键。

更新 (基于 Akash Kava 建议)

我进行了以下更改 [表(“文档”,架构=“配置”)] 公共类文档:IPrimaryKey { [必需的] 公共字符串原始名称 { 获取;放; }

    [Required]
    public DocumentTypes DocumentType { get; set; }

    [Required]
    public MIMETypes MIMEType { get; set; }

    [Required]
    public byte[] DocumentData { get; set; }

    [DefaultValue(false)]
    public bool IsPublic { get; set; }

    [Required]
    public DateTimeOffset DateTimeAdded { get; set; }

    [Required]
    public long AddedByUser { get; set; }

    [Key]
    public long Id { get; set; }

    [ForeignKey("AddedByUser")]

    [InverseProperty("Image")]

    public virtual Details User { get; set; }
}

[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
    [Required]
    public string UserId { get; set; }

    [ForeignKey("UserId")]
    public AppUser User { get; set; }

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Address> Addresses { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<Email> Emails { get; set; }

    [CollectionRequired(MinimumCollectionCount = 1)]
    public ICollection<PhoneNumber> PhoneNumbers { get; set; }

    public ICollection<NotificationHistory> NotificationHistory { get; set; }
    public long TimeZoneId { get; set; }
    public long? ImageId { get; set; }

    [ForeignKey("ImageId")]
    [InverseProperty("User")]
    public Document Image { get; set; } 

    [ForeignKey("TimeZoneId")]
    public virtual TimeZone TimeZone { get; set; }


    [Key]
    public long Id { get; set; }
}

我已经注释掉了 Fluent API 代码

无法确定之间关联的主体端 类型“StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details” 和 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'。 此关联的主体端必须显式配置 使用关系流式 API 或数据注释。

【问题讨论】:

    标签: c# entity-framework data-annotations ef-fluent-api


    【解决方案1】:

    您也可以使用数据注释实现相同的目的,您缺少InverseProperty 属性,这可以解决这种情况下的歧义。从概念上讲,每个导航属性都有 Inverse Navigation 属性,EF 会根据类型自动检测并假定 inverse 属性,但是如果两个实体通过多个 FK 属性相互关联,则必须在相应的导航属性上显式指定 InverseProperty 属性。

    我建议将 InverseProperty 放在每个导航属性上,这有助于减少 EF 的启动时间,因为 EF 不必确定和验证模型。

    例子,

    public class AccountEmail {
    
        public long AccountID {get;set;}
    
        // Inverse property inside Account class
        // which corresponds to other end of this
        // relation
        [InverseProperty("AccountEmails")]
        [ForeignKey("AccountID")]
        public Account Account {get;set;}
    
    }
    
    public class Account{
    
        // Inverse property inside AccountEmail class
        // which corresponds to other end of this
        // relation
        [InverseProperty("Account")]
        public ICollection<AccountEmail> AccountEmails {get;set;}
    }
    

    我编写了一个文本模板,它根据当前架构生成所有这些导航属性。从https://github.com/neurospeech/atoms-mvc.net/tree/master/db-context-tt 下载所有三个文件,您可能需要自定义它,因为它基于我们的框架添加了一些东西,但它确实直接从您的数据库生成纯代码模型。

    【讨论】:

    • 我试过这个并得到错误。如果你能提供一个有效的例子,我会接受你的回答。
    • 您的 T4 模板是必需的吗?如果是这样,那么这会使您的答案无效,因为数据注释不适用于开箱即用。
    • 您可以使用我的T4模板生成代码,然后您可以检查您正在接吻的内容。
    • 您必须在每个导航属性上指定 InverseProperty。
    • 我编辑了您的答案,使用您的建议使我的代码正常工作,加上我缺少的一些东西,额外的导航属性,但似乎同行评审已经拒绝/删除了我的编辑等等。我已经决定我的课程中的数据注释过程变得过于混乱,所以我将只坚持使用 Fluent API。我会投票赞成并将您的答案标记为已接受,因为它在技术上确实有效。
    【解决方案2】:

    好的,我终于想通了。可悲的是,这不是很直接,因为我认为数据注释应该可以工作,但事实并非如此。

    您必须使用 Fluent API:

            modelBuilder.Entity<Details>()
                        .HasOptional(x => x.Document)
                        .WithMany()
                        .HasForeignKey(x => x.ImageId);
    
            modelBuilder.Entity<Document>()
                        .HasRequired(x => x.User)
                        .WithMany()
                        .HasForeignKey(x => x.AddedByUser);
    

    【讨论】:

    • 感谢您分享您的见解和解决方案!
    猜你喜欢
    • 2018-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 2011-07-30
    • 2012-05-10
    • 2015-02-24
    相关资源
    最近更新 更多