Я пытаюсь смоделировать множество ссылок на многие в EF CodeFirst с полиморфной структурой таблиц. Я использую CTP-версию октября 2011 года, которая поддерживает свойства навигации для производных типов (что хорошо работает в других тестах, которые я делал).
Проблема:
Когда я устанавливаю это конкретное отношение «многие ко многим» в отображении базовой (абстрактной) таблицы и пытаюсь получить связанные записи, я получаю SQL-запрос с сотнями K объединений и объединений ... только время, затрачиваемое на генерацию оператор SQL равен 30 секундам по сравнению с голыми миллисекундами для его выполнения. Тем не менее, он возвращает соответствующие результаты. Когда я изменяю многие на многие, чтобы они существовали между двумя производными объектами, производимый запрос является идеальным ... но я не могу снова сопоставить ту же самую связанную таблицу M2M для других производных объектов, не будучи информированным о том, что объединяющая таблица "уже была сопоставлена ».
Особенности:
В существующей структуре базы данных есть базовая таблица - Сторона - которая объединяется 1 ... 1 или 0 с Заказчиком, Продавцом, Пользователем и Отделом (каждая из которых является типом Стороны).
Стороны связаны друг с другом через существующую таблицу соединений PartyRelationship (ID, InternalPartyID, ExternalPartyID). По соглашению, InternalPartyID содержит PartyID пользователя, а ExternalPartyID содержит PartyID клиента, поставщика или отдела, с которым они связаны.
Пытаясь использовать EF CodeFirst в новом проекте (WCF DataServices), я создал класс Party как:
public abstract class Party
{
public Party()
{
this.Addresses = new List<Address>();
this.PhoneNumbers = new List<PhoneNumber>();
this.InternalRelatedParties = new List<Party>();
this.ExternalRelatedParties = new List<Party>();
}
public int PartyID { get; set; }
public short Active { get; set; }
//other fields common to Parties
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
public virtual ICollection<Party> InternalRelatedParties { get; set; }
public virtual ICollection<Party> ExternalRelatedParties { get; set; }
}
Затем, используя наследование TPT, Клиент, Продавец, Отдел и Пользователь подобны:
public class Customer : Party
{
public string TermsCode { get; set; }
public string DefaultFundsCode { get; set; }
//etc
}
public class User : Party
{
public string EmployeeNumber { get; set; }
public string LoginName { get; set; }
//etc
}
Соединительный стол:
public class PartyRelationship
{
public int PartyRelationshipID { get; set; }
public int InternalPartyID { get; set; }
public int ExternalPartyID { get; set; }
//certain other fields specific to the relationship
}
Отображения:
public class PartyMap : EntityTypeConfiguration<Party>
{
public PartyMap()
{
// Primary Key
this.HasKey(t => t.PartyID);
// Properties
this.ToTable("Party");
this.Property(t => t.PartyID).HasColumnName("PartyID");
this.Property(t => t.Active).HasColumnName("Active");
//etc
// Relationships
this.HasMany(p => p.InternalRelatedParties)
.WithMany(rp => rp.ExternalRelatedParties)
.Map(p => p.ToTable("PartyRelationship")
.MapLeftKey("ExternalPartyID")
.MapRightKey("InternalPartyID"));
}
}
public class PartyRelationshipMap : EntityTypeConfiguration<PartyRelationship>
{
public PartyRelationshipMap()
{
// Primary Key
this.HasKey(t => t.PartyRelationshipID);
// Properties
// Table & Column Mappings
//this.ToTable("PartyRelationship"); // Commented out to prevent double-mapping
this.Property(t => t.PartyRelationshipID).HasColumnName("PartyRelationshipID");
this.Property(t => t.InternalPartyID).HasColumnName("InternalPartyID");
this.Property(t => t.ExternalPartyID).HasColumnName("ExternalPartyID");
this.Property(t => t.CreateTime).HasColumnName("CreateTime");
this.Property(t => t.CreateByID).HasColumnName("CreateByID");
this.Property(t => t.ChangeTime).HasColumnName("ChangeTime");
this.Property(t => t.ChangeByID).HasColumnName("ChangeByID");
}
}
Контекст:
public class MyDBContext : DbContext
{
public MyDBContext()
: base("name=MyDBName")
{
Database.SetInitializer<MyDBContext>(null);
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Party> Parties { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Configurations.Add(new PartyMap());
modelBuilder.Configurations.Add(new PartyRelationshipMap());
}
}
URL-адрес, такой как http://localhost:29004/Services/MyDataService.svc/Parties(142173)/SAData.Customer/InternalRelatedParties, в конечном итоге возвращает правильные oData, но занимает 30 секунд, чтобы создать огромный оператор SQL (189 КБ), который выполняется за 600 мс.
Я также пытался сопоставить таблицу PartyRelationship с двунаправленной таблицей один ко многим (обе для Party как «одна» таблица), но с аналогичным результатом.
Нужны ли отдельные таблицы соединений для клиентов-пользователей, поставщиков-пользователей и пользователей-отделов? Стоит ли смотреть на разделение вертикальной таблицы или представления базы данных, которые разделяют PartyRelationship на отдельные логические объекты (чтобы я мог переназначить одну и ту же таблицу)? Есть ли другой способ настройки модели EF в этом сценарии?