【问题标题】:EF Code First 0..1 to 0..1 relationshipEF Code First 0..1 到 0..1 的关系
【发布时间】:2013-07-23 12:37:30
【问题描述】:

我正在尝试在与实体之间建立可选关系,但我无法找到正确的语法。实际上我需要一个 0..1 到 0..2 的关系,但我猜一旦我找到了实现 0..1 到 0..1 的方法,这将是微不足道的。

我所拥有的简化是这样的:

class Foo
{
    int Id { get; set; }
    //navigation property
    virtual Bar Bar { get; private set; }
}

class Bar
{
    int Id { get; set; }

    int? LeftFooId { get; set; }
    [ForeignKey("LeftFooId")]
    Foo LeftFoo { get; set; }

    int? RightFooId{ get; set; } 
    [ForeignKey("RightFooId")]
    Foo RightFoo { get; set; }
}

一个 Foo 可以连接到零个或一个 Bar,永远不会更多。一个 Bar 可以有一个 LeftFoo 和一个 RightFoo,或者一个或一个都没有。如果 Foo 的 Bar 属性没有被 Bar 引用,它应该为 null,并且它应该包含被 Bar 引用时引用它的 Bar。

上面的代码 Bar 正确引用了 Foo,但是 EF 给 Foo 表一个 Bar_Id 并且 Bar 属性始终为空。

我尝试了几种不同的方法来设置这些实体类,并使用了不同的 Fluent API 调用来完成这项工作,但我没有得到想要的结果。

【问题讨论】:

    标签: ef-code-first entity-framework-5


    【解决方案1】:

    试试这个:

    public class Foo
    {
        public int Id { get; set; }
        public virtual Bar Bar { get; set; }
    }
    
    public class Bar
    {
        public int Id {get; set;}
        public virtual Foo LeftFoo {get;set;}
        public virtual Foo RightFoo {get;set;}
    }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Bar>()
                    .HasOptiona(b => b.LeftFoo)
                    .WithOptionalDependent()
                    .Map(k = k.MapKey("LeftFooId"));
    
        modelBuilder.Entity<Bar>()
                    .HasOptional(b => b.RightFoo)
                    .WithOptionalDependent()
                    .Map(k => k.MapKey("RightFooId"));
        ...
    }
    

    结果

    编辑播种

    我会做以下,没有测试代码,但它应该可以工作:

    var bars = new List<Bar>();
       bars.Add(new Bar());
       bars.Add(new Bar());
    ...//this means as many as you need/want
       bars.ForEach(b => context.Bars.AddOrUpdate(b));
    context.SaveChanges();
    
    var leftFoos = new List<Foo>();
       leftFoos.Add(new Foo());
       leftFoos.Add(new Foo());
    ...//this means as many as you need/want
       leftFoos.ForEach(f => context.Foos.AddOrUpdate(f));
    context.SaveChanges();
    
    var rightFoos = new List<Foo>();
       rightFoos.Add(new Foo());
       rightFoos.Add(new Foo());
    ...//this means as many as you need/want
       rightFoos.Foreach(f => context.Foos.AddOrUpdate(f));
    context.SaveChanges();
    
    int i=0;
    foreach(var bar in bars)
    {
       bar.LeftFoo = leftFoos.ElementAt(i);
       bar.RightFoo = rightFoos.ElementAt(i);
       i++;
    }
    context.SaveChanges();
    

    为简单起见,bars、leftFoos 和 rightFoos 具有相同数量的元素。

    【讨论】:

    • 这正是我已经拥有的。 FK 在那里,但是当我分配 LeftFoo 和 RightFoo 时,那些 Foo 的 Bar_Id 仍然为空。我试图通过添加到 OptionalDependent(f => f.Bar) 来解决这个问题,但这给了我错误:指定的架构无效。错误:(85,6):错误 0040:类型 Bar_LeftFoo 未在命名空间中定义(Alias=Self)。
    • 你确定你没有做其他事情吗?当我在本地对此进行测试时,它可以工作。您如何将left 和right 分配给bar?可以发一下吗?
    • 我目前在我的 Seed 中做的是制作一堆 Bar,所有 fk 都为 null,然后保存更改,然后循环遍历所有 Bar,并为每个空的 leftfoo 或 rightfoo 创建一个新的 foo ,将 Bar 属性保留为空,然后我执行另一个保存。然后我将新的 foo 分配给 bar (bar.leftfoo = foo)。然后更新和保存。
    【解决方案2】:

    我这样修改代码:

    class Foo
    {
        int Id { get; set; }
        int? BarId { get; set; }
        [ForeignKey("BarId")]
        virtual Bar Bar { get; set; }
    }
    
    class Bar
    {
        int Id { get; set; }
    
        int? LeftFooId { get; set; }
        [ForeignKey("LeftFooId")]
        Foo LeftFoo { get; set; }
    
        int? RightFooId{ get; set; } 
        [ForeignKey("RightFooId")]
        Foo RightFoo { get; set; }
    }
    

    我可能是错的,但是自动分配一对 FK 是不可能的,至少从 Foo 到 Bar:EF 怎么知道它应该将 FK 分配给 LeftFoo 还是 RightFoo。所以现在我只是手动完成这两项任务:

    Foo myFirstFoo = new Foo();
    Foo mySecondFoo = new Foo();
    Bar myBar = new Bar();
    context.Add(myFirstFoo); context.Add(mySecondFoo); context.Add(myBar);
    context.SaveChanges();
    myFirstFoo.BarId = myBar.Id;
    myBar.LeftFooId = myFirstFoo.Id;
    mySecondFoo.BarId = myBar.Id;
    myBar.RightFooId = mySecondFoo.Id;
    context.Update(myFirstFoo); context.Update(mySecondFoo); context.Update(myBar);
    context.SaveChanges();
    

    这似乎可行,但我确实希望它可以做得更好。

    【讨论】:

      【解决方案3】:

      尝试使您的属性虚拟化并使用 POCO 代理(使用 Create 而不是 new ...)。这将确保侧面属性的自动更新。即,当将 Foo 分配给 Bar 时,POCO 代理会将相应的 Bar 分配给 Foo,但是,...这应该是主要问题:如果将 Bar 分配给 Foo,您期望什么?您希望自动分配哪个属性,从左到右?注释 Bar 属性 [InverseProperty("...")] 以表明这一点,并考虑您可能需要 Foo 中的另一个 Bar 类型的属性用于其他关系。 希望这会有所帮助

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-26
        • 1970-01-01
        • 2013-10-28
        • 2013-09-11
        相关资源
        最近更新 更多