与关系播种项目

时间:2014-09-03 20:01:44

标签: entity-framework entity-framework-6

请考虑以下事项:

public class Foo
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public string Something { get; set; }

    public virtual ICollection<Bar> Bars { get; set; }
}

public class Bar
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public string Something { get; set; }

    [ForeignKey("Foo")]
    public int FooId { get; set; }
    public virtual Foo Foo { get; set; }
}

现在,在我的上下文中,我希望为相关Foo s种下一些Bar

context.Foos.AddOrUpdate(
    r => r.Name,
    new Foo
    {
        Name = "Foo 1",
        Something = "Blah",
        Bars = new List<Bar>
        {
            new Bar { Name = "Foo 1 Bar 1", Something = "Blah" },
            new Bar { Name = "Foo 1 Bar 2", Something = "Blah" },
            new Bar { Name = "Foo 1 Bar 3", Something = "Blah" },
        }
    },
    ...

如果我运行Update-Database,一切都会按照您的预期进行,并且我会为每个Bar添加相关的Foo

现在,如果我将种子数据更改为:

context.Foos.AddOrUpdate(
    r => r.Name,
    new Foo
    {
        Name = "Foo 1",
        Something = "BlahBlahBlah",
        Bars = new List<Bar>
        {
            new Bar { Name = "Foo 1 Bar 1", Something = "BlahBlahBlah" },
            new Bar { Name = "Foo 1 Bar 2", Something = "BlahBlahBlah" },
            new Bar { Name = "Foo 1 Bar 3", Something = "BlahBlahBlah" },
        }
    },
    ...

再次运行Update-DatabaseFoo会更新,但所有Bar仍然会有&#34; Blah&#34;他们的Something属性。这是有道理的,因为实体框架没有逻辑方法知道应该更新哪个Bar,但有没有解决方法?也许我可以通过其他方式添加Bar并将它们与Foo s相关联?我知道我可以做到:

context.Bars.AddOrUpdate(
    r = r.Name,
    new Bar
    {
        ...
    },
    ...

但鉴于我使用标识列作为键,我无法知道将Bar添加到哪个Foo。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

第一种方法(问题)和第二种方法(答案)都不适用于第二次执行Seed方法。

第一种方法

Bars = new List<Bar>
{
    new Bar { Name = "Foo 1 Bar 1", Something = "Blah" },
    // new Bar { Name = "Foo 1 Bar 4", Something = "Blah" },
}

如果您已知第二次执行Seed方法,如果将Something修改为BlahBlahBlah,结果将不会保存修改后的Bars集合,因为它只更新{的标量属性{1}}

首次执行Foo时,Seed是一个新实体,此图表下方的所有对象也会被标记为Foo

此外,如果在第二次执行Seed方法时,您添加了引用现有Added的新Bar(注释代码),则新Foo也不会被添加。

第二种方法

Bar

这比第一种方法更糟糕,第二次执行Seed方法会抛出异常,因为在更新任何var bar1 = new Bar { Name = "Foo 1 Bar 1", Something = "Blah" }; context.Bars.AddOrUpdate( r => r.Name, bar1 ); context.Foos.AddOrUpdate( r => r.Name, new Foo { Name = "Foo 1", Something = "Blah", Bars = new List<Bar> { bar1 } } ); 时,它会尝试更新Bar与未找到{{}的关系1}},Bar未指定,表示其值为0. Foo,其中Id等于0未找到。

<强>解决方案

您需要定义临时密钥才能正确执行“添加或更新”。

Bar's FooId

第二次执行种子方法会将Foo更新为var bar1 = new Bar { Id = 1, Name = "Foo 1 Bar 1", Something = "Blah", FooId = 1 }; var bar2 = new Bar { Id = 2, Name = "Foo 1 Bar 2", Something = "Blah", FooId = 1 }; var bar3 = new Bar { Id = 3, Name = "Foo 1 Bar 3", Something = "Blah", FooId = 1 }; // var bar4 = // new Bar { Id = 4, Name = "Foo 1 Bar 4", Something = "Blah", FooId = 1 }; db.Bars.AddOrUpdate( r => r.Name, bar1, bar2, bar3 // bar4 ); db.Foos.AddOrUpdate( r => r.Name, new Foo { Id = 1, Name = "Foo 1", Something = "Blah" } ); 。新的foo's and Bars' Something(评论代码)将成功添加,并引用现有的BlahBlahBlah

更多

答案 1 :(得分:0)

当我输入问题时,我发现我可以这样做:

var bar1 = new Bar { Name = "Foo 1 Bar 1", Something = "Blah" };
var bar2 = new Bar { Name = "Foo 1 Bar 2", Something = "Blah" };
var bar3 = new Bar { Name = "Foo 1 Bar 3", Something = "Blah" };
...

context.Bars.AddOrUpdate(
    r => r.Name,
    bar1,
    bar2,
    bar3,
    ...
);

context.Foos.AddOrUpdate(
    r => r.Name,
    new Foo
    {
        Name = "Foo 1",
        Something = "Blah",
        Bars = new List<Bar>
        {
            bar1,
            bar2,
            bar3
        }
    },
    ...
);

然而,我还没有机会对此进行测试,而且无论如何,我仍然对其他想法持开放态度。