Как комментировать отношения родитель-ребенок с Code-First

При использовании CTP 5 библиотеки кода Entity Framework (как объявлено здесь) я пытаюсь создать класс, который сопоставляется с очень простой иерархической таблицей.

Здесь 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; }
}

У меня есть установка connectionstring в файле 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/designer --- но, похоже, нет четкого документа о том, что такое конвенция. (Нам просто повезло в понятие множественных имен таблиц, для уникальных имен классов)

Было бы полезно оценить некоторые рекомендации о том, как сделать этот очень простой пример.

UPDATE: Изменение имени столбца в таблице "Люди" с "Родитель" на "Личный" позволяет выполнить добавление fred. Howerver, вы заметите, что pebbles добавлен в коллекцию Children fred, и поэтому я бы ожидал добавления гальки в базу данных, когда был добавлен Fred, но это было не так. Это очень простая модель, поэтому я более чем обескуражен тем, что должна быть эта многообещающая работа, связанная с получением пары строк в базе данных.

Ответы

Ответ 1

Вам нужно спуститься до свободного 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"));
}

Однако это вызывает InvalidOperationException с этим сообщением. Sequence содержит более одного соответствующего элемента. который звучит как ошибка 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

И тогда использование этого свободного API будет соответствовать вашей модели данных для схемы БД:

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).

Ответ 2

Он должен работать с использованием следующего вида:

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

            });
    }
}

Однако это порождает исключение: Sequence содержит несколько совпадающих элементов. Явно это ошибка.

Смотрите эту тему и ответ на вопрос @shichao: http://blogs.msdn.com/b/adonet/archive/2010/12/06/ef-feature-ctp5-fluent-api-samples.aspx#10102970