как аннотировать отношения родитель-ребенок с Code-First - PullRequest
15 голосов
/ 16 декабря 2010

При использовании 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; }
}

У меня есть настройка строки подключения в приложении.Конфигурационный файл выглядит так:

<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 --- но, похоже, нет четкого документа о том, что такое конвенция.(Мы просто поверили в понятие имен таблиц во множественном числе, для имен классов в единственном числе)

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

ОБНОВЛЕНИЕ: Изменение имени столбца в таблице People с Parent на PersonId позволяет добавить fred для продолжения.Однако вы заметите, что pebbles - это добавленная в коллекцию Children fred, и поэтому я ожидал, что камешки будут добавлены и в базу данных при добавлении Фреда, но это не так.Это очень простая модель, поэтому я более чем обескуражен тем, что для получения пары строк в базе данных необходимо провести много догадок.

Ответы [ 3 ]

13 голосов
/ 16 декабря 2010

Вам нужно перейти на свободный 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 с этим сообщением Последовательность содержит более одного соответствующего элемента .это звучит как ошибка CTP5 по ссылке, упомянутой Стивеном в его ответе.

Вы можете использовать обходной путь до тех пор, пока эта ошибка не будет исправлена ​​в окончательной первоначальной версии, то есть принять имя по умолчанию для столбца 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) сребенок (камешки).

0 голосов
/ 15 августа 2018

В Entity Framework 6 вы можете сделать это следующим образом: 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

0 голосов
/ 16 декабря 2010

Он должен работать с использованием сопоставления, как показано ниже:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...