简体   繁体   English

首先了解实体框架代码中的ForeignKey属性

[英]Understanding ForeignKey attribute in entity framework code first

See the following post for some background: 有关背景信息,请参阅以下帖子:

Entity framework one to zero or one relationship without navigation property 实体框架一到零或一个没有导航属性的关系

I had always thought that ForeignKey was used to show which property in a class held the ForeignKey that determined the navigation property eg 我一直认为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; }
}

However, I discovered on the linked post that this is not right and that as DeferredData's primary key was called Id I actually needed: 但是,我在链接的帖子上发现这是不对的,因为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; }
}

ie ForeignKey is used to point to the other class. ForeignKey用于指向另一个类。

I then proceeded to change some of the other references: 然后我继续改变其他一些参考文献:

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; }
}

However, this failed. 但是,这失败了。 Turned out on this one the ForeignKey needed to point to the Id on MemberDataSet class. 事实证明, 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; }
}

I presume this is because this second relationship is one to many whereas the first was one to zero or one, and that effectively the principal end of the relationship differs, but I would appreciate some clarity on this/references to good articles, so I can understand what is happening and exactly what ForeignKey is doing. 我认为这是因为这第二个关系是一对多,而第一个关系是一对零或一,有效的关系的主要结束不同,但我希望有一些明确的这个/参考好文章,所以我可以了解正在发生的事情以及ForeignKey正在做什么。

I was also looking for clarity in the example above of how public int? DeferredDataId { get; set; } 我也在寻找上面的public int? DeferredDataId { get; set; }如何清晰public int? DeferredDataId { get; set; } public int? DeferredDataId { get; set; } public int? DeferredDataId { get; set; } fits into the equation given it is not explicitly linked to DeferredData . public int? DeferredDataId { get; set; }适合给它没有明确地链接到方程式DeferredData I am happy this will match up by convention but how would I explicitly tell it this eg if it had a different name? 我很高兴这将符合惯例,但我如何明确告诉它,例如它是否有不同的名称? Al the examples I have seen on this talk about using the ForeignKey attribute but this can't be the answer in all cases per above! 我在这个关于使用ForeignKey属性的讨论中看到过的例子,但这并不是所有情况下的答案!

All help greatly appreciated - looking to understand the issue rather than fix a specific problem as I have lots of references in my model so need to establish what approach to take with each. 所有的帮助非常感谢 - 希望了解问题而不是修复特定问题,因为我在模型中有很多引用,因此需要建立采用每种方法的方法。

Thanks. 谢谢。

Edit 编辑

Added other classes to help: 添加了其他类来帮助:

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
}

The required side of the 1..0 relationship MemberDataSet should not have a FK to DeferredData . 1..0关系MemberDataSet的必需端不应具有到DeferredData的FK。 Instead, DeferredData 's PK should also be a FK to MemberDataSet (known as shared primary key) 相反, DeferredData的PK也应该是FK到MemberDataSet (称为共享主键)

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; }
}

Fluent API: 流畅的API:

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

I think you were right (provided that I understand you right). 我认为你是对的(前提是我理解你的权利)。 [ForeignKeyAttribute] is used on the coresponding foreign key. [ForeignKeyAttribute]用于相应的外键。 Not on the primary key of your linked object. 不在链接对象的主键上。

This is my object, and the foreign key is DeferredDataId

Neither the Id within your object is the foreign key (it's primary key) nor the ID of the linked object is the foreign key (it's the primary key of the other side of the relation) 对象中的Id既不是外键(它是主键),也不是链接对象的ID是外键(它是关系另一端的主键)

Hope I understood you correctly :) Because I'm not sure. 希望我理解正确:)因为我不确定。

I think that your original idea was correct, with one slight exception. 我认为你原来的想法是正确的,只有一个例外。 By putting the foreign key on the MemberDataSet you are implying that you would like a zero-or-one-to-many relationship. 通过将外键放在MemberDataSet上,您暗示您想要一个零或一对多的关系。

In your example, MemberDataSet.DeferredData is optional, and DeferredData can be referred to by many MemberDataSet instances. 在你的榜样, MemberDataSet.DeferredData是可选的, DeferredData可以被很多人称为MemberDataSet实例。

In fluent syntax this would be expressed by: 在流利的语法中,这将表示为:

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

In order to make this a one-to-zero-or-one property you can put a unique (where not null) key constraint on MemberDataSet's DeferredDataId column. 为了使它成为一对多或一个属性,您可以在MemberDataSet的DeferredDataId列上放置一个唯一的(where not null)键约束。 This would mean that a DeferredData entity could only be referred to by a single MemberDataSet entity. 这意味着DeferredData实体只能由单个MemberDataSet实体引用。

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

Note: This type of filtered key is only available in SQL Server 2008 and up. 注意: 此类型的筛选密钥仅在SQL Server 2008及更高版本中可用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM