【问题标题】:Understanding ForeignKey attribute in entity framework code first先了解实体框架代码中的 ForeignKey 属性
【发布时间】:2014-03-18 08:25:20
【问题描述】:

有关一些背景信息,请参阅以下帖子:

Entity framework one to zero or one relationship without navigation property

我一直认为ForeignKey 用于显示类中的哪个属性包含确定导航属性的 ForeignKey,例如

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("DeferredDataId")]
    public virtual DeferredData DeferredData { get; set; }
}

但是,我在链接的帖子中发现这是不对的,因为 DeferredData 的主键被称为 Id 我实际上需要:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }
}

ForeignKey 用于指向其他类。

然后我开始更改其他一些参考:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }

    public int? SignedOffById { get; set; }
    [ForeignKey("UserId")]
    public virtual UserProfile SignedOffBy { get; set; }
}

但是,这失败了。结果发现,ForeignKey 需要指向 MemberDataSet 类上的 Id。

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }

    public int? SignedOffById { get; set; }
    [ForeignKey("SignedOffById")]
    public virtual UserProfile SignedOffBy { get; set; }
}

我认为这是因为第二个关系是一对多,而第一个是一对零或一,并且实际上关系的主要目的不同,但我希望对此/对好文章的引用有所澄清,这样我就可以了解正在发生的事情以及 ForeignKey 在做什么。

在上面的示例中,我还希望清楚地了解public int? DeferredDataId { get; set; } 如何适合等式,因为它没有明确链接到DeferredData。我很高兴这将按照惯例匹配,但我将如何明确地告诉它,例如如果它有不同的名称?我在这篇文章中看到的所有例子都是关于使用 ForeignKey 属性的,但这并不是上述所有情况下的答案!

非常感谢所有帮助 - 希望了解问题而不是解决特定问题,因为我的模型中有很多参考资料,因此需要确定对每个参考资料采取什么方法。

谢谢。

编辑

添加了其他类来帮助:

public class DeferredData
{

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    //other properties
}

public class UserProfile
{

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }

    //other properties
}

【问题讨论】:

    标签: c# entity-framework foreign-keys code-first shared-primary-key


    【解决方案1】:

    1..0 关系 MemberDataSet 的必需侧不应具有到 DeferredData 的 FK。相反,DeferredData 的 PK 也应该是 MemberDataSet 的 FK(称为共享主键)

    public class MemberDataSet
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
    
        public virtual DeferredData DeferredData { get; set; }
    }
    
    public class DeferredData
    {
        // DeferredData.Id is both the PK and a FK to MemberDataSet
        [Key]
        [DatabaseGenerated( DatabaseGeneratedOption.None )]
        [ForeignKey( "MemberDataSet" )]
        public int Id { get; set; }
    
        [Required]
        public virtual MemberDataSet MemberDataSet { get; set; }
    }
    

    流畅的 API:

    modelBuilder.Entity<MemberDataSet>()
        .HasOptional( mds => mds.DeferredData )
        .WithRequired()
        .WillCascadeOnDelete();
    

    【讨论】:

    • 不,您引用的是当前类的 PK 字段,它显然与 Id 同名。 DeferredDataId 不会在这种情况下使用(无论如何,作为 FK)
    • 太棒了!如果没有 DeferredData 中的导航属性,这可能吗?
    • 没有属性,你必须使用流畅的API
    • modelBuilder.Entity&lt;MemberDataSet&gt;().HasOptional(d =&gt; d.DeferredData).WithRequired().WillCascadeOnDelete(true); 然后去掉 ForeignKey 属性?看不到 IsForeignKey 类型选项。
    • virtual nav 属性仅仅意味着代理可以延迟加载属性——它与 FK 无关。这是ForeignKey 属性的标准用法,仅应用于实体的PK 字段以强制1:0 关系
    【解决方案2】:

    我认为您最初的想法是正确的,只有一点例外。通过将外键放在MemberDataSet 上,您暗示您想要一个零或一对多的关系。

    在您的示例中,MemberDataSet.DeferredData 是可选的,DeferredData 可以被许多 MemberDataSet 实例引用。

    在流利的语法中,这将被表达为:

    modelBuilder.Entity<MemberDataSet>()
        .HasOptional(dataSet => dataSet.DeferredData)
        .WithMany()
        .HasForeignKey(deferredData => deferredData.DeferredDataId);
    

    为了使其成为一对零或一对一的属性,您可以在 MemberDataSet 的 DeferredDataId 列上放置一个唯一(非空)键约束。这意味着 DeferredData 实体只能由单个 MemberDataSet 实体引用。

    CREATE UNIQUE INDEX unique_MemberDataSet_DeferredDataId ON MemberDataSet(DeferredDataId) WHERE DeferredDataId IS NOT NULL
    

    注意:This type of filtered key is only available in SQL Server 2008 and up.

    【讨论】:

      【解决方案3】:

      我认为你是对的(前提是我理解你的正确)。 [ForeignKeyAttribute] 用于对应的外键。不在链接对象的主键上。

      This is my object, and the foreign key is DeferredDataId

      对象中的Id 既不是外键(它是主键),链接对象的ID 也不是外键(它是关系另一端的主键)

      希望我正确理解了你 :) 因为我不确定。

      【讨论】:

      • 我喜欢这样的描述“这是我的对象,外键是 DeferredDataId”。这就是我一直以来的想法。但是,这无法构建数据库,解决方案是更改为 Id,即根据链接的帖子引用另一个类。
      • 我检查了我的一个旧项目。我就是这样做的(它确实有效)。环境是带有sql ce 4和实体框架代码的wcf数据服务。但我认为这无关紧要。你有任何错误吗?
      • 错误是“'MemberDataSet' 类型的属性 'DeferredData' 上的 ForeignKeyAttribute 无效。在依赖类型 'DeferredData' 上找不到外键名称 'DeferredDataId'。”我认为@Moho 已经解释了这个问题,它开始变得有意义了!
      • 我将链接到 MSDN 文章。在Relationship Attributes: InverseProperty and ForeignKey 章节中的解释正是如此。另一种方法是使用 virtual 作为关系属性。 EF 代码首先会自己创建外键。 msdn.microsoft.com/en-us/data/gg193958.aspx
      • 感谢早先阅读过,但无法弄清楚如何将其应用于我正在做的事情。现在一切似乎都清楚多了!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-17
      相关资源
      最近更新 更多