У меня есть репродукция того, что, как мне кажется, может быть ошибкой 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?