Разделение таблицы EF: InvalidOperationException — последовательность содержит более одного совпадающего элемента

У меня есть репродукция того, что, как мне кажется, может быть ошибкой EF. Я хотел бы разделить одну таблицу на два объекта. Каждый из этих объектов содержит ссылку на третий объект. Две ссылки должны предоставляться с одинаковыми именами свойств внешнего ключа. Поскольку они сопоставляются со столбцами в одной и той же таблице, конфигурация (или, в данном примере, атрибут) используется для того, чтобы сделать имена столбцов уникальными.

Когда я пытаюсь загрузить модель, я получаю указанное выше исключение от EF. Если я изменю имя одного из свойств FK, ошибка исчезнет.

Вот моя модель. Код как есть работает. Чтобы воспроизвести проблему, переименуйте Foo2.Foo3Id1 в Foo3Id, это то значение, которое мне нужно.

Почему вы хотите это сделать?

Если вам интересно, почему мне нужно, чтобы два свойства имели одно и то же имя, вот объяснение.

У меня есть таблица, содержащая несколько адресов (например, почтовый адрес и платежный адрес). Это существующая база данных, поэтому я не могу изменить структуру таблицы. Каждый адрес представлен серией стандартных столбцов. Имя каждого столбца имеет префикс, определяющий тип адреса, и суффикс, определяющий часть адреса, например. BillingAddressLine1, BillingAddressZipCode и PostalAddressLine1.

Казалось бы, с этим справится использование сложного типа. Однако есть дополнительная сложность: каждый адрес содержит CityId, который ссылается на таблицу Cities. Сложные типы не поддерживают отношения и свойства навигации. Таким образом, мое предполагаемое решение состоит в том, чтобы вместо этого использовать разделение таблицы и разделить каждый набор адресных свойств на отдельный объект. Каждая сущность, представляющая адрес, либо происходит от базового типа, скажем, Address, либо реализует интерфейс IAddress.

При разделении таблиц я стараюсь соблюдать ограничение: если несколько типов сопоставляются с одной и той же таблицей, все они должны иметь свойства навигации для каждого другое.

В приведенном ниже коде Foo1 и Foo2 являются типами адресов (и реализуют некоторый общий интерфейс). Foo3 это City. Это самое простое воспроизведение проблемы, которое я мог придумать.

Пример кода

class Program
{
    static void Main(string[] args)
    {
        // Use NuGet to import EF 5 into the project.
        // This code is just enough to cause the metadata to be loaded and therefore demo the error.
        using (Context cx = new Context())
        {
            var qq = from f in cx.Foo3s
                     where f.Foo1s.Any()
                     select f;
        }
    }
}

[Table("Foo")]
public class Foo1
{
    [Key]
    public virtual int Id { get; set; }

    [Required]
    public virtual Foo2 Foo2 { get; set; }

    [ForeignKey("Foo3")]
    [Column("Foo1_Foo3Id")]
    public virtual int? Foo3Id { get; set; }

    public virtual Foo3 Foo3 { get; set; }
}

[Table("Foo")]
public class Foo2
{
    [Key]
    public virtual int Id { get; set; }

    [Required]
    public virtual Foo1 Foo1 { get; set; }

    // Re-name the following property to Foo3Id (rather than Foo3Id1) and the model won't load.
    // You get "InvalidOperationException: Sequence contains more than one matching element."
    [ForeignKey("Foo3")]
    [Column("Foo2_Foo3Id")]
    public virtual int? Foo3Id1 { get; set; }

    public virtual Foo3 Foo3 { get; set; }
}

[Table("Foo3")]
public class Foo3
{
    [Key]
    public virtual int Id { get; set; }

    [InverseProperty("Foo3")]
    public virtual ICollection<Foo1> Foo1s { get; set; }

    [InverseProperty("Foo3")]
    public virtual ICollection<Foo2> Foo2s { get; set; }
}

public class Context : DbContext
{
    public DbSet<Foo3> Foo3s { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Don't think we can configure 1:1 relationship using just attributes.
        var foo2 = modelBuilder.Entity<Foo2>();
        foo2.HasRequired(q => q.Foo1)
            .WithRequiredPrincipal(q => q.Foo2);
    }
}

Это ошибка? Я делаю что-то неправильно? Это известное ограничение EF?


person Olly    schedule 03.11.2012    source источник
comment
Можете ли вы сообщить об этом на entityframework.codeplex.com? Я не знаю, поддерживается ли это, но если это не так, вы должны получить лучшее исключение   -  person Pawel    schedule 04.11.2012
comment
@Павел - Спасибо. Я создал entityframework.codeplex.com/workitem/643 и добавил здесь дополнительную информацию о почему я пытаюсь это сделать. Это звучит немного неясно без этой справочной информации.   -  person Olly    schedule 05.11.2012
comment
Брайс нашел обходной путь, который вы могли использовать, пока он не будет исправлен, — он разместил его на сайте codeplex. На всякий случай: изменение одного из имен свойств является допустимым обходным путем, пока это не будет исправлено.   -  person Pawel    schedule 07.11.2012
comment
@Pawel - Спасибо, хотя я объяснил, что в моем сценарии две ссылки должны отображаться с одинаковыми именами свойств внешнего ключа. Похоже, я просто не могу делать то, что хочу прямо сейчас.   -  person Olly    schedule 07.11.2012


Ответы (2)


Это действительно была ошибка, и теперь она исправлена. См. http://entityframework.codeplex.com/workitem/643.

person Olly    schedule 05.11.2013
comment
это было в 6.0.0. Моя ссылка была о 6.1.0, которая, возможно, повторно представила его. но теперь это кажется исправленным (?) - person juFo; 27.04.2014

Ошибка была еще в 6.0. Посмотрите здесь: https://entityframework.codeplex.com/workitem/546 + другие связанные ошибка: https://entityframework.codeplex.com/workitem/2116

person juFo    schedule 23.04.2014