Entity Framework Code Первое сопоставление отношений «один ко многим» с таблицей ссылок - PullRequest
4 голосов
/ 15 августа 2011

У меня вопрос об отношении «один ко многим» между двумя таблицами, когда между ними существует таблица связей (соединений).

Примеры таблиц:

ChildTable:
ID int NOT NULL PK
Relation int NOT NULL

ParentTable:
ID int NOT NULL PK
Name nvarchar(50) NOT NULL

ParentChildren:
ParentTable_ID int NOT NULL PFK
ChildTable_ID int NOT NULL PFK

Объекты:

public class ChildTable
{
    public ChildTable()
    {
        this.ParentTables = new List<ParentTable>();
    }

    public int ID { get; set; }       
    public int Relation { get; set; }
    public virtual ICollection<ParentTable> ParentTables { get; set; }
}

public class ParentTable
{
    public ParentTable()
    {
        this.ChildTables = new List<ChildTable>();
    }

    public int ID { get; set; }   
    public string Name { get; set; }
    public virtual ICollection<ChildTable> ChildTables { get; set; }
}

Отображение:

public class ChildTableMap : EntityTypeConfiguration<ChildTable>
{
    public ChildTableMap()
    {
        // Primary Key
        this.HasKey(t => t.ID);

        // Properties
        // Table & Column Mappings
        this.ToTable("ChildTable");
        this.Property(t => t.ID).HasColumnName("ID");
        this.Property(t => t.Relation).HasColumnName("Relation");
    }
}
public class ParentTableMap : EntityTypeConfiguration<ParentTable>
{
    public ParentTableMap()
    {
        // Primary Key
        this.HasKey(t => t.ID);

        // Properties
        this.Property(t => t.Name)
            .IsRequired()
            .HasMaxLength(50);

        // Table & Column Mappings
        this.ToTable("ParentTable");
        this.Property(t => t.ID).HasColumnName("ID");
        this.Property(t => t.Name).HasColumnName("Name");

        // Relationships
        this.HasMany(t => t.ChildTables)
            .WithMany(t => t.ParentTables)
            .Map(m =>
                {
                    m.ToTable("ParentChildren");
                    m.MapLeftKey("ParentTable_ID");
                    m.MapRightKey("ChildTable_ID");
                });

    }
}

Контекст:

public class TestContext : DbContext
{
    static TestContext()
    { 
        Database.SetInitializer<TestContext>(null);
    }

    public DbSet<ChildTable> ChildTables { get; set; }
    public DbSet<ParentTable> ParentTables { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
        modelBuilder.Configurations.Add(new ChildTableMap());
        modelBuilder.Configurations.Add(new ParentTableMap());
    }
}

Код для добавления ребенка и родителя:

using (var context = new TestContext())
        {
                var parent = new ParentTable { Name = "Mother Goose" };                   
                var child = new ChildTable { Relation = 1 };                   
                context.ParentTables.Add(parent);
                context.ChildTables.Add(child);
                parent.ChildTables.Add(child);
                context.SaveChanges();
        }

И все работает как положено, но у меня действительно только один родитель и много детей.

Как выполнить сопоставление, включающее запись в таблицу ссылок (с измененным классом ChildTable, у которого нет ICollection of ParentTable)?

1 Ответ

7 голосов
/ 23 августа 2011

Невозможно определить такое отображение.Соединительные таблицы предназначены только для отношений «многие ко многим».Вы должны иметь в виду, что ваша схема базы данных (которую вы не можете изменить, как вы сказали в комментариях к вашему вопросу) уже определяет отношение многие ко многим.Например, эти две записи ...

ParentTable_ID:  1    2
ChildTable_ID:   1    1

... являются действительными записями в таблице ParentChildren, они не нарушают ограничение PK и говорят, что у ребенка 1 есть два родителя 1 и2.

Если ваша бизнес-логика допускает, что у ребенка может быть только один единственный родитель, схема базы данных смоделирована неправильно.Вы не можете исправить этот недостаток с помощью EF.

Возможный обходной путь может заключаться в том, чтобы обеспечить в вашей бизнес-логике в коде, что коллекция ParentTables вашей сущности ChildTable никогда не имеет более одного элемента, возможно, путем введениявспомогательное свойство, не отображенное на карту:

public class ChildTable
{
    public ChildTable()
    {
        this.ParentTables = new List<ParentTable>();
    }

    public int ID { get; set; }       
    public int Relation { get; set; }
    public virtual ICollection<ParentTable> ParentTables { get; set; }

    [NotMapped] // or use .Ignore(c => c.ParentTable) in Fluent API
    public ParentTable ParentTable
    {
        get
        {
            return ParentTables.SingleOrDefault();
            // let is crash if there is more than one element in the list
        }

        set
        {
            var oldParent = ParentTables.SingleOrDefault();
            if (oldParent != value)
            {
                ParentTables.Clear();
                if (value != null)
                    ParentTables.Add(value);
            }
        }
    }
}

Это только небольшая помощь, если вы используете только свойство ParentTable.Вы должны избегать использования коллекции и добавлять более одного родителя.Вы не можете сделать это private, потому что это должно быть сопоставлено.Это также не безопасно на другой стороне отношений.Вы можете написать: parent1.ChildTables.Add(child1); parent2.ChildTables.Add(child1);, что приведет к двум родителям для child1 в базе данных.Вы можете уловить это, написав вспомогательные методы для добавления дочернего элемента в сущности ParentTable, которая проверяет, есть ли у добавленного дочернего элемента родительский объект, отличный от this parent.

...