繁体   English   中英

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

[英]how to annotate a parent-child relationship with 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)

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

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; }
}

我在 app.config 文件中设置了连接字符串,如下所示:

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

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

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);
    }
}

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

列名“PersonId”无效。

我理解约定优于配置的价值,我和我的团队对抛弃 edmx / 设计师噩梦的前景感到兴奋——但似乎没有关于约定是什么的明确文件。 (我们只是幸运地想到了复数表名的概念,对于单数类名)

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

更新:将 People 表中的列名从 Parent 更改为 PersonId 允许继续添加fred Howerver你会发现, pebbles是加入的儿童收集fred ,所以我本来期望加入弗雷德鹅卵石时要添加到数据库中为好,但这种情况并非如此。 这是一个非常简单的模型,因此我对将几行输入数据库中涉及这么多猜测工作感到非常沮丧。

您需要使用 fluent API 来实现您想要的模式(数据注释不会这样做)。 确切地说,您有一个Independent One-to-Many Self Reference Association ,该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"));
}

但是,这会引发InvalidOperationException并带有此消息Sequence contains more than one matching element 根据史蒂文在他的回答中提到的链接,这听起来是一个 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

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

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

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

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();                                
}

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

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();                                
}

这两个代码具有相同的效果,即添加一个新的父级 (Fred) 和一个子级 (Pebbles)。

在 Entity Framework 6 中你可以这样做,注意public Guid? ParentId { get; set; } public Guid? ParentId { get; set; } public Guid? ParentId { get; set; } . 外键必须可以为空才能工作。

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

https://stackoverflow.com/a/5668835/3850405

它应该使用如下映射来工作:

class FamilyContext : DbContext
{
    public DbSet<Person> People { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<Person>().HasMany(x => x.Children).WithMany().Map(y =>
            {
                y.MapLeftKey((x => x.Id), "ParentID");
                y.MapRightKey((x => x.Id), "ChildID");

            });
    }
}

但是,这会引发异常:序列包含多个匹配元素。 显然这是一个错误。

请参阅此线程和@shichao 问题的答案: http : //blogs.msdn.com/b/adonet/archive/2010/12/06/ef-feature-ctp5-fluent-api-samples.aspx#10102970

    class Person
{ 
    [key()]
    public Guid Id { get; set; }
    public String Name { get; set; }
    [ForeignKey("Children")]
    public int? PersonId {get; set;} //Add ForeignKey
    public virtual Person Parent { get; set; }
    public virtual ICollection<Person> Children { get; set; }
}

builder.Entity<Menu>().HasMany(m => m.Children)
                        .WithOne(m => m.Parent)
                        .HasForeignKey(m => m.PersonId);

暂无
暂无

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

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