EF4.1代码首先由一对多自我引用

时间:2011-09-22 04:10:35

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

我正在尝试首先使用EF4.1代码进行一对多的自引用。我的实体看起来像这样

public class Site
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Sitename { get; set; }

    public virtual Site ChildSite { get; set; }
    public virtual ICollection<Site> ChildSites { get; set; }
}

在我的上下文课中,我这样做是为了进行自我引用

modelBuilder.Entity<Site>()
    .HasOptional(s => s.ChildSite)
    .WithMany(s => s.ChildSites)
    .HasForeignKey(s => s.ParentId);

但是当我尝试使用此代码将一些虚拟数据添加到我的数据库时

var sites = new List<Site>
{
    new Site { ParentId = 0, Sitename = "Top 1" },
    new Site { ParentId = 0, Sitename = "Top 2" },
    new Site { ParentId = 0, Sitename = "Top 3" },
    new Site { ParentId = 0, Sitename = "Top 4" },
    new Site { ParentId = 1, Sitename = "Sub 1_5" },
    new Site { ParentId = 1, Sitename = "Sub 1_6" },
    new Site { ParentId = 1, Sitename = "Sub 1_7" },
    new Site { ParentId = 1, Sitename = "Sub 1_8" },
    new Site { ParentId = 2, Sitename = "Sub 2_9" },
    new Site { ParentId = 2, Sitename = "Sub 2_10" },
    new Site { ParentId = 2, Sitename = "Sub 2_11" },
    new Site { ParentId = 2, Sitename = "Sub 2_12" },
    new Site { ParentId = 3, Sitename = "Sub 3_13" },
    new Site { ParentId = 3, Sitename = "Sub 3_14" },
    new Site { ParentId = 3, Sitename = "Sub 3_15" },
    new Site { ParentId = 3, Sitename = "Sub 3_16" },
    new Site { ParentId = 4, Sitename = "Sub 4_17" },
    new Site { ParentId = 4, Sitename = "Sub 4_18" },
    new Site { ParentId = 4, Sitename = "Sub 4_19" },
    new Site { ParentId = 4, Sitename = "Sub 4_20" }
};
sites.ForEach(s => context.Sites.Add(s));
context.SaveChanges();

我收到此错误:

  

无法确定主要结尾   'Cms.Model.Site_ChildSite'的关系。多个添加的实体可能   拥有相同的主键。

我在这里缺少什么?

修改:

这是我的问题的解决方案。我从实体

中删除了public virtual Site ChildSite { get; set; }
public class Site
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Sitename { get; set; }

    public virtual ICollection<Site> ChildSites { get; set; }
}

然后我将modelBuilder更改为此

modelBuilder.Entity<Site>()
    .HasMany(s => s.ChildSites)
    .WithOptional()
    .HasForeignKey(s => s.ParentId);

由于数据库的自动生成似乎不起作用,我继续像这样自己创建表

int Id, not null, primary key
int ParentId, null
string Sitename, null

一切都按照我的意愿行事。

第二次修改

让自动生成虚拟数据的最后一步

var parent1 = new Site
{
    Sitename = "Top 1",
    ChildSites = new List<Site>
        {
            new Site {Sitename = "Sub 1_5"},
            new Site {Sitename = "Sub 1_6"},
            new Site {Sitename = "Sub 1_7"},
            new Site {Sitename = "Sub 1_8"}
        }
};

ect . . . 

context.Sites.Add(parent1);
context.SaveChanges();

请参阅下面的Slauma答案

4 个答案:

答案 0 :(得分:3)

我有一个不同的模型,但我会在我的情况下发布必要的内容,并且在MVC 3上使用EF4.1可以正常工作:

型号

public class Page
{
    public int Id { get; set; }
    public virtual string Title { get; set; }

    public int? ParentId { get; set; }
    public virtual Page Parent { get; set; }
    public virtual ICollection<Page> Children { get; set; }
}

数据库(使用流畅的API)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     modelBuilder.Entity<Page>()
                 .HasOptional(s => s.Parent)
                 .WithMany(s => s.Children)
                 .HasForeignKey(s => s.ParentId);
}

<强>初始化程序

var page1 = new Page()
{
     Title = "title"
};

context.Pages.Add(page1);

var page2 = new Page()
{
      Parent = page1,
      Title = "title2",
};

context.Pages.Add(page2);

答案 1 :(得分:2)

您的映射是正确的,您无需删除ChildSite属性。您只是在创建实体时无法使用外键属性,并“猜测”这些自动生成的数字在存储实体后将如何使用这些数字并在存储的相同实体中使用这些数字。

以下代码应该工作并在数据库中创建预期的行:

var parent1 = new Site
{
    Sitename = "Top 1",
    ChildSites = new List<Site>
        {
            new Site {Sitename = "Sub 1_5"},
            new Site {Sitename = "Sub 1_6"},
            new Site {Sitename = "Sub 1_7"},
            new Site {Sitename = "Sub 1_8"}
        }
};
var parent2 = new Site
{
    Sitename = "Top 2",
    ChildSites = new List<Site>
        {
            new Site {Sitename = "Sub 2_9"},
            new Site {Sitename = "Sub 2_10"},
            new Site {Sitename = "Sub 2_11"},
            new Site {Sitename = "Sub 2_12"}
        }
};
var parent3 = new Site
{
    Sitename = "Top 3",
    ChildSites = new List<Site>
        {
            new Site {Sitename = "Sub 3_13"},
            new Site {Sitename = "Sub 3_14"},
            new Site {Sitename = "Sub 3_15"},
            new Site {Sitename = "Sub 3_16"}
        }
};
var parent4 = new Site
{
    Sitename = "Top 4",
    ChildSites = new List<Site>
        {
            new Site {Sitename = "Sub 4_17"},
            new Site {Sitename = "Sub 4_18"},
            new Site {Sitename = "Sub 4_19"},
            new Site {Sitename = "Sub 4_20"}
        }
};
context.Sites.Add(parent1);
context.Sites.Add(parent2);
context.Sites.Add(parent3);
context.Sites.Add(parent4);
context.SaveChanges();

修改

如果外键存在的实体存在于DB中,您可以使用它 - 例如:

var site = context.Sites.Single(s => s.Sitename == "Top 1");
site.ParentId = 4; // set a parent for "Top 1"
context.SaveChanges();

添加一个新孩子:

var site = context.Sites.Single(s => s.Sitename == "Top 1");
site.ChildSites.Add(new Site { Sitename = "Sub 1_9" });
context.SaveChanges();

删除一个孩子:

var site = context.Sites.Single(s => s.Sitename == "Top 1");
// works because of lazy loading
var childToRemove = site.ChildSites.Single(s => s.Sitename == "Sub 1_9");
site.ChildSites.Remove(childToRemove);
context.SaveChanges();

答案 2 :(得分:0)

不知道保留一个网站ChildSite和一个ChildSites的集合的目的是什么。试试这个配置。

public class Site
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
     public virtual  Site Parent { get; set; }
    public string Sitename { get; set; }

    public virtual ICollection<Site> ChildSites { get; set; }
}

modelBuilder.Entity<Site>()
    .HasOptional(s => s.Parent)
    .WithMany(s => s.ChildSites)
    .HasForeignKey(s => s.ParentId);

修改 当你插入时,你需要这样做。

 site s1=   new Site { ParentId = 0, Sitename = "Top 1" };
 site s2=   new Site { ParentId = 0, Sitename = "Top 2" };
 site s3=   new Site { ParentId = 0, Sitename = "Top 3" };
//....
 site s4=  new Site { Parent = s1, Sitename = "Sub 1_5"};

答案 3 :(得分:0)

首先 - 不要指定id - 我认为数据库负责向您的实体添加自动编号。

尝试以这种方式填充数据库:

var top1 = new Site { Sitename = "Top 1" };
var sub15 = new Site { ChildSite = top1 , Sitename = "Sub 1_5" };
var sub16 = new Site { ChildSite = top1 , Sitename = "Sub 1_6" };
var sub17 = new Site { ChildSite = top1 , Sitename = "Sub 1_7" };
...
context.Sites.Add(top1);

var top2 = new Site { Sitename = "Top 2" };
var sub29 = new Site { ChildSite = top2 , Sitename = "Sub 2_9" };
var sub210 = new Site { ChildSite = top2 , Sitename = "Sub 2_10" };
var sub211 = new Site { ChildSite = top2 , Sitename = "Sub 2_11" };
....
context.Sites.Add(top2);

....
context.SaveChanges();

下一篇:我认为您应该将您的ChildSite称为ChildSite,以便让代码更容易阅读:

var top1 = new Site { Sitename = "Top 1" };
var sub15 = new Site { ParentSite = top1 , Sitename = "Sub 1_5" };
var sub16 = new Site { ParentSite = top1 , Sitename = "Sub 1_6" };
var sub17 = new Site { ParentSite = top1 , Sitename = "Sub 1_7" };
...
context.Sites.Add(top1);

var top2 = new Site { Sitename = "Top 2" };
var sub29 = new Site { ParentSite = top2 , Sitename = "Sub 2_9" };
var sub210 = new Site { ParentSite = top2 , Sitename = "Sub 2_10" };
var sub211 = new Site { ParentSite = top2 , Sitename = "Sub 2_11" };
....
context.Sites.Add(top2);

....
context.SaveChanges();