如何使用Code-First注释父子关系

Ral*_*ton 16 entity-framework code-first

当使用实体框架代码优先库的CTP 5(如宣布在这里)我试图创建一个映射到一个非常简单的层次结构表中的类.

这是构建表的SQL:

CREATE TABLE [dbo].[People]
(
 Id  uniqueidentifier not null primary key rowguidcol,
 Name  nvarchar(50) not null,
 Parent  uniqueidentifier null
)
ALTER TABLE [dbo].[People]
 ADD CONSTRAINT [ParentOfPerson] 
 FOREIGN KEY (Parent)
 REFERENCES People (Id)
Run Code Online (Sandbox Code Playgroud)

这是我希望自动映射回该表的代码:

class Person
{
    public Guid Id { get; set; }
    public String Name { get; set; }
    public virtual Person Parent { get; set; }
    public virtual ICollection<Person> Children { get; set; }
}

class FamilyContext : DbContext
{
    public DbSet<Person> People { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我在app.config文件中设置了connectionstring,如下所示:

<configuration>
  <connectionStrings>
    <add name="FamilyContext" connectionString="server=(local); database=CodeFirstTrial; trusted_connection=true" providerName="System.Data.SqlClient"/>
  </connectionStrings>
</configuration>
Run Code Online (Sandbox Code Playgroud)

最后我试图使用类添加父和子实体,如下所示:

static void Main(string[] args)
{
    using (FamilyContext context = new FamilyContext())
    {
        var fred = new Person
        {
            Id = Guid.NewGuid(),
            Name = "Fred"
        };
        var pebbles = new Person
        {
            Id = Guid.NewGuid(),
            Name = "Pebbles",
            Parent = fred
        };
        context.People.Add(fred);
        var rowCount = context.SaveChanges();
        Console.WriteLine("rows added: {0}", rowCount);
        var population = from p in context.People select new { p.Name };
        foreach (var person in population)
            Console.WriteLine(person);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里显然缺少一些东西.我得到的例外是:

列名称'PersonId'无效.

我理解约定对配置的价值,我和我的团队对放弃edmx /设计师噩梦的前景感到非常兴奋 - 但似乎没有关于约定的明确文件.(我们只是幸运地参与了多个表名的概念,对于单数类名)

关于如何使这个非常简单的例子落实到位的一些指导将不胜感激.

更新:将People表中的列名从Parent更改为PersonId允许Add fredto继续.Howerver你会注意到这pebbles是一个添加到Children系列中的fred,因此我希望在添加Fred时将鹅卵石添加到数据库中,但事实并非如此.这是一个非常简单的模型,所以我有点沮丧的是,在将几行输入数据库时​​应该有这么多的猜测工作.

Mor*_*avi 14

您需要下拉到流畅的API以实现所需的架构(数据注释不会这样做).确切地说,您还有一个Independent One-to-Many Self Reference Association外键列(People.Parent)的自定义名称.以下是如何使用EF Code First完成的:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
                .HasOptional(p => p.Parent)
                .WithMany(p => p.Children)
                .IsIndependent()
                .Map(m => m.MapKey(p => p.Id, "ParentID"));
}
Run Code Online (Sandbox Code Playgroud)

但是,这会抛出一条InvalidOperationException带有此消息的Sequence包含多个匹配元素.根据Steven在他的回答中提到的链接,这听起来像是一个CTP5错误.

您可以使用解决方法,直到在RTM中修复此错误,并接受FK列的默认名称PersonID.为此,您需要稍微更改架构:

CREATE TABLE [dbo].[People]
(
     Id  uniqueidentifier not null primary key rowguidcol,
     Name  nvarchar(50) not null,
     PersonId  uniqueidentifier null
)
ALTER TABLE [dbo].[People] ADD CONSTRAINT [ParentOfPerson] 
FOREIGN KEY (PersonId) REFERENCES People (Id)
GO
ALTER TABLE [dbo].[People] CHECK CONSTRAINT [ParentOfPerson]
GO
Run Code Online (Sandbox Code Playgroud)

然后使用这个流畅的API将您的数据模型与DB Schema匹配:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
                .HasOptional(p => p.Parent)
                .WithMany(p => p.Children)
                .IsIndependent();
}
Run Code Online (Sandbox Code Playgroud)

添加包含子项的新父记录:

using (FamilyContext context = new FamilyContext())
{
    var pebbles = new Person
    {
        Id = Guid.NewGuid(),
        Name = "Pebbles",                    
    };
    var fred = new Person
    {
        Id = Guid.NewGuid(),
        Name = "Fred",
        Children = new List<Person>() 
        { 
            pebbles
        }
    };                
    context.People.Add(fred);               
    context.SaveChanges();                                
}
Run Code Online (Sandbox Code Playgroud)

添加包含父级的新子记录:

using (FamilyContext context = new FamilyContext())
{
    var fred = new Person
    {
        Id = Guid.NewGuid(),
        Name = "Fred",                
    };
    var pebbles = new Person
    {
        Id = Guid.NewGuid(),
        Name = "Pebbles",
        Parent = fred
    };
    context.People.Add(pebbles);
    var rowCount = context.SaveChanges();                                
}
Run Code Online (Sandbox Code Playgroud)

这两个代码具有相同的效果,即添加带有子项(Pebbles)的新父(Fred).