简体   繁体   English

使用 Entity Framework Fluent API 的一对一可选关系

[英]One to one optional relationship using Entity Framework Fluent API

We want to use one to one optional relationship using Entity Framework Code First.我们希望使用 Entity Framework Code First 来使用一对一的可选关系。 We have two entities.我们有两个实体。

public class PIIUser
{
    public int Id { get; set; }

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

PIIUser may have a LoyaltyUserDetail but LoyaltyUserDetail must have a PIIUser . PIIUser可能有一个LoyaltyUserDetailLoyaltyUserDetail必须有一个PIIUser We tried these fluent approach techniques.我们尝试了这些流畅的方法技巧。

modelBuilder.Entity<PIIUser>()
            .HasOptional(t => t.LoyaltyUserDetail)
            .WithOptionalPrincipal(t => t.PIIUser)
            .WillCascadeOnDelete(true);

This approach didn't create LoyaltyUserDetailId foreign key in PIIUsers table.这种方法没有在PIIUsers表中创建LoyaltyUserDetailId外键。

After that we tried the following code.之后我们尝试了以下代码。

modelBuilder.Entity<LoyaltyUserDetail>()
            .HasRequired(t => t.PIIUser)
            .WithRequiredDependent(t => t.LoyaltyUserDetail);

But this time EF didn't create any foreign keys in these 2 tables.但是这次EF没有在这两个表中创建任何外键。

Do you have any ideas for this issue?你对这个问题有什么想法吗? How can we create one to one optional relationship using entity framework fluent api?我们如何使用实体框架 fluent api 创建一对一的可选关系?

EF Code First supports 1:1 and 1:0..1 relationships. EF Code First 支持1:11:0..1关系。 The latter is what you are looking for ("one to zero-or-one").后者是您正在寻找的(“一到零或一”)。

Your attempts at fluent are saying required on both ends in one case and optional on both ends in the other.您对 fluent 的尝试是说在一种情况下两端都是必需的,而在另一种情况下两端都是可选的。

What you need is optional on one end and required on the other.您需要的一方面是可选的,另一方面是必需的。

Here's an example from the Programming EF Code First book这是编程 EF Code First 一书中的示例

modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);

The PersonPhoto entity has a navigation property called PhotoOf that points to a Person type. PersonPhoto实体有一个名为PhotoOf的导航属性,它指向一个Person类型。 The Person type has a navigation property called Photo that points to the PersonPhoto type. Person类型有一个名为Photo的导航属性,它指向PersonPhoto类型。

In the two related classes, you use each type's primary key , not foreign keys .在两个相关的类中,您使用每种类型的主键,而不是外键 ie, you won't use the LoyaltyUserDetailId or PIIUserId properties.即,您不会使用LoyaltyUserDetailIdPIIUserId属性。 Instead, the relationship depends on the Id fields of both types.相反,这种关系取决于两种类型的Id字段。

If you are using the fluent API as above, you do not need to specify LoyaltyUser.Id as a foreign key, EF will figure it out.如果您使用的是上述 fluent API,则无需指定LoyaltyUser.Id作为外键,EF 会计算出来。

So without having your code to test myself (I hate doing this from my head)... I would translate this into your code as所以没有你的代码来测试自己(我讨厌从我的脑海里做这个)​​......我会把它翻译成你的代码

public class PIIUser
{
    public int Id { get; set; }    
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }    
    public PIIUser PIIUser { get; set; }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<LoyaltyUserDetail>()
  .HasRequired(lu => lu.PIIUser )
  .WithOptional(pi => pi.LoyaltyUserDetail );
}

That's saying LoyaltyUserDetails PIIUser property is required and PIIUser's LoyaltyUserDetail property is optional.也就是说,LoyaltyUserDetails PIIUser属性是必需的,而 PIIUser 的LoyaltyUserDetail属性是可选的。

You could start from the other end:你可以从另一端开始:

modelBuilder.Entity<PIIUser>()
.HasOptional(pi => pi.LoyaltyUserDetail)
.WithRequired(lu => lu.PIIUser);

which now says PIIUser's LoyaltyUserDetail property is optional and LoyaltyUser's PIIUser property is required.现在说 PIIUser 的LoyaltyUserDetail属性是可选的,而 LoyaltyUser 的PIIUser属性是必需的。

You always have to use the pattern HAS/WITH.你总是必须使用模式 HAS/WITH。

HTH and FWIW, one to one (or one to zero/one) relationships are one of the most confusing relationships to configure in code first so you are not alone! HTH 和 FWIW,一对一(或一对零/一)关系是首先在代码中配置的最令人困惑的关系之一,因此您并不孤单! :) :)

Just do like if you have one-to-many relationship between LoyaltyUserDetail and PIIUser so you mapping should be就像LoyaltyUserDetailPIIUser之间有一对多关系LoyaltyUserDetail ,所以你的映射应该是

modelBuilder.Entity<LoyaltyUserDetail>()
       .HasRequired(m => m.PIIUser )
       .WithMany()
       .HasForeignKey(c => c.LoyaltyUserDetailId);

EF should create all foreign key you need and just don't care about WithMany ! EF 应该创建您需要的所有外键,并且不关心 WithMany

There are several things wrong with your code.您的代码有几处错误。

A 1:1 relationship is either: PK<-PK , where one PK side is also an FK, or PK<-FK+UC , where the FK side is a non-PK and has a UC. 1:1 的关系是: PK<-PK ,其中一个 PK 侧也是 FK,或PK<-FK+UC ,其中 FK 侧是非 PK 并具有 UC。 Your code shows you have FK<-FK , as you define both sides to have an FK but that's wrong.您的代码显示您有FK<-FK ,因为您定义双方都有 FK 但这是错误的。 I recon PIIUser is the PK side and LoyaltyUserDetail is the FK side.PIIUser是 PK 端, LoyaltyUserDetail是 FK 端。 This means PIIUser doesn't have an FK field, but LoyaltyUserDetail does.这意味着PIIUser没有 FK 字段,但LoyaltyUserDetail有。

If the 1:1 relationship is optional, the FK side has to have at least 1 nullable field.如果1:1关系是可选的,则 FK 端必须至少有 1 个可空字段。

pswg above did answer your question but made a mistake that s/he also defined an FK in PIIUser, which is of course wrong as I described above.上面的 pswg 确实回答了您的问题,但犯了一个错误,即他/她还在 PIIUser 中定义了 FK,这当然是我上面描述的错误。 So define the nullable FK field in LoyaltyUserDetail , define the attribute in LoyaltyUserDetail to mark it the FK field, but don't specify an FK field in PIIUser .因此,在界定为空的FK场LoyaltyUserDetail ,在定义属性LoyaltyUserDetail以纪念它的FK领域,但不指定FK场PIIUser

You get the exception you describe above below pswg's post, because no side is the PK side (principle end).你会得到你在 pswg 的帖子下面描述的例外,因为没有一方是 PK 方(原则结束)。

EF isn't very good at 1:1's as it's not able to handle unique constraints. EF 不太擅长 1:1,因为它无法处理唯一约束。 I'm no expert on Code first, so I don't know whether it is able to create a UC or not.首先我不是Code专家,所以我不知道它是否能够创建一个UC。

(edit) btw: A 1:1 B (FK) means there's just 1 FK constraint created, on B's target pointing to A's PK, not 2. (编辑)顺便说一句:A 1:1 B (FK) 意味着只创建了 1 个 FK 约束,在 B 的目标上指向 A 的 PK,而不是 2。

public class User
{
    public int Id { get; set; }
    public int? LoyaltyUserId { get; set; }
    public virtual LoyaltyUser LoyaltyUser { get; set; }
}

public class LoyaltyUser
{
    public int Id { get; set; }
    public virtual User MainUser { get; set; }
}

        modelBuilder.Entity<User>()
            .HasOptional(x => x.LoyaltyUser)
            .WithOptionalDependent(c => c.MainUser)
            .WillCascadeOnDelete(false);

this will solve the problem on REFERENCE and FOREIGN KEYS这将解决REFERENCEFOREIGN KEYS上的问题

when UPDATING or DELETING a record更新删除记录时

Try adding the ForeignKey attribute to the LoyaltyUserDetail property:尝试将ForeignKey属性添加到LoyaltyUserDetail属性:

public class PIIUser
{
    ...
    public int? LoyaltyUserDetailId { get; set; }
    [ForeignKey("LoyaltyUserDetailId")]
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
    ...
}

And the PIIUser property:PIIUser属性:

public class LoyaltyUserDetail
{
    ...
    public int PIIUserId { get; set; }
    [ForeignKey("PIIUserId")]
    public PIIUser PIIUser { get; set; }
    ...
}

The one thing that is confusing with above solutions is that the Primary Key is defined as " Id" in both tables and if you have primary key based on the table name it wouldn't work, I have modified the classes to illustrate the same, ie the optional table shouldn't define it's own primary key instead should use the same key name from main table.与上述解决方案混淆的一件事是,主键在两个表中都定义为“ Id” ,如果您有基于表名的主键,它将不起作用,我已经修改了类来说明相同的情况,即可选表不应该定义它自己的主键,而是应该使用与主表相同的键名。

public class PIIUser
{
    // For illustration purpose I have named the PK as PIIUserId instead of Id
    // public int Id { get; set; }
    public int PIIUserId { get; set; }

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    // Note: You cannot define a new Primary key separately as it would create one to many relationship
    // public int LoyaltyUserDetailId { get; set; }

    // Instead you would reuse the PIIUserId from the primary table, and you can mark this as Primary Key as well as foreign key to PIIUser table
    public int PIIUserId { get; set; } 
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

And then followed by然后紧随其后

modelBuilder.Entity<PIIUser>()
.HasOptional(pi => pi.LoyaltyUserDetail)
.WithRequired(lu => lu.PIIUser);

Would do the trick, the accepted solution fails to clearly explain this, and it threw me off for few hours to find the cause可以解决问题,公认的解决方案未能清楚地解释这一点,这让我花了几个小时才找到原因

This is of no use to the original poster, but for anyone still on EF6 who needs the foreign key to be different from the primary key, here's how to do it:这对原始海报没有用,但对于需要外键与主键不同的仍在使用 EF6 的任何人,这里是如何做到的:

public class PIIUser
{
    public int Id { get; set; }

    //public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

modelBuilder.Entity<PIIUser>()
    .HasRequired(t => t.LoyaltyUserDetail)
    .WithOptional(t => t.PIIUser)
    .Map(m => m.MapKey("LoyaltyUserDetailId"));

Note that you can't use the LoyaltyUserDetailId field because, as far as I can tell, it can only be specified using the fluent API.请注意,您不能使用LoyaltyUserDetailId字段,因为据我所知,它只能使用 fluent API 指定。 (I've tried three ways of doing it using the ForeignKey attribute and none of them worked). (我已经尝试了三种使用ForeignKey属性的方法,但都没有奏效)。

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

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