EF 4.1: Отображение отношения «многие ко многим» с помощью составных ключей и общего столбца? - PullRequest
2 голосов
/ 20 января 2012

Я использую EF 4.1 Code First и пытаюсь смоделировать следующее.

public class Apple
{
    public int Id {get;set;}
}

public class Banana
{
    public int AppleId {get;set;}
    public int Id {get;set;}
    public virtual ICollection<Coconut> Coconuts {get;set;}
}

public class Coconuts
{
    public int AppleId {get;set;}
    public int Id {get;set;}
    public virtual ICollection<Banana> Bananas {get;set;}
}

База данных выглядит следующим образом. Fruit

Это существующая схема, которая не соответствует соглашениям EF, поэтому я использую Fluent API для сопоставления сущностей с базой данных. Отображение выглядит следующим образом.

public class BananaMapping : EntityTypeConfiguration<Banana>
{
    public BananaMapping()
    {
        HasKey(e => new { e.AppleId, e.Id })
            .ToTable("Bananas", "fruit");
        Property(e => e.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        HasMany(e => e.Coconuts)
            .WithMany(s => s.Bananas)
            .Map(l =>
                     {
                         l.ToTable("BananaCoconuts", "fruit");
                         l.MapLeftKey("AppleId", "BananaId");
                         l.MapRightKey("AppleId", "CoconutId");
                     });
    }

(Apple и Coconut также отображаются, но здесь для краткости опущены) Если я оставлю это так, он сгенерирует исключение MetadataException из-за общего столбца. "Schema specified is not valid. Errors: (110,6) : error 0019: Each property name in a type must be unique. Property name 'AppleId' was already defined."

Чтобы обойти это, я создал вычисляемый столбец на BananaCoconuts, который просто предоставляет копию с другим именем AppleId и назвал ее BananaAppleId. Я оставил ФК в покое (очевидно) и изменил отображение так, чтобы оно выглядело так ...

HasMany(e => e.Coconuts)
    .WithMany(s => s.Bananas)
    .Map(l =>
        {
            l.ToTable("BananaCoconuts", "fruit");
            l.MapLeftKey("BananaAppleId", "BananaId");
            l.MapRightKey("AppleId", "CoconutId");
        }
    );

Хотя это было несколько вонючим и определенно хакерским, оно помогло мне преодолеть исключение MetadataException, пока я не попытался добавить новую ссылку из кода.

var banana = dataContext.FindBanana(appleId, bananaId);
var coconut = dataContext.FindCoconut(appleId, coconutId);
banana.Coconuts.Add(coconut);
dataContext.SaveChanges();

Сохранение изменений вызывает исключение DbUpdateException

An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

И внутренний (на самом деле пара исключений) ...

{"The column \"BananaAppleId\" cannot be modified because it is either a computed column or is the result of a UNION operator."}

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

1 Ответ

3 голосов
/ 20 января 2012

Таблица соединения должна иметь 4 столбца в качестве первичного ключа, и каждый из них является внешним ключом основной таблицы. Столбец не может быть установлен как вычисленный, потому что EF вставит записи, отправив все 4 значения столбца. Так что в вашем случае

HasMany(e => e.Coconuts)
    .WithMany(s => s.Bananas)
    .Map(l =>
        {
            l.ToTable("BananaCoconuts", "fruit");
            l.MapLeftKey("BananaAppleId", "BananaId");
            l.MapRightKey("CoconutAppleId", "CoconutId");
        }
    );

В противном случае вы должны отобразить таблицу соединений как отдельную сущность.

...