我们使用与单个表格使用少量M:M关系的查询遇到性能问题。
表格
目前,EF生成一个MONSTROUS查询,该查询使用UNION ALL 3次到Contact表。 Pastebin of SQL每次加入一次。我们的预测是这样的
var contacts = query.Select(contact => new Contact
{
Brands = contact.Brands.Select(b => new Brand
{
Id = b.Id,
Name = b.Name,
Aliases = b.Aliases.Select(a => a.Value).ToList()
}).ToList(),
Categories = contact.Categories.Select(category => new Category
{
Id = category.Id
}).ToList(),
ContactTypes = contact.ContactTypes.Select(ct => new ContactType
{
Id = ct.Id
}).ToList(),
Disabled = contact.Disabled,
Email = contact.Email,
Fax = contact.Fax,
Id = contact.Id,
Latitude = contact.Coordinates != null ? contact.Coordinates.Latitude : 0,
Longitude = contact.Coordinates != null ? contact.Coordinates.Longitude : 0,
Distance = searchLocation != null && contact.Coordinates != null ? searchLocation.Distance(contact.Coordinates) * conversionFactor : 0,
PostalCode = contact.PostalCode,
PhoneSupport = contact.PhoneSupport,
PhoneSales = contact.PhoneSales,
PhoneAfterHoursSupport = contact.PhoneAfterHoursSupport,
Phone = contact.Phone,
Preferred = contact.Preferred,
ShopOnlineImageUrl = contact.ShopOnlineImageUrl,
ContactTranslations = contact.NameContent.TranslatedContents.Where(tc => tc.IsoLanguageCode == searchModel.IsoLanguageCode ||
tc.IsoLanguageCode == SearchContactsHelpers.ISO_LANGUAGE_CODE_ENGLISH)
.Select(tc => new ContactTranslationImpl
{
Name = tc.Value,
IsoLanguageCode = tc.IsoLanguageCode,
NameId = tc.MasterContentId
}).ToList(),
AdditionalInformationContentId = contact.AdditionalInformationContentId,
AddressLine1ContentId = contact.AddressLine1ContentId,
AddressLine2ContentId = contact.AddressLine2ContentId,
CityContentId = contact.CityContentId,
StateOrProvinceContentId = contact.StateOrProvinceContentId,
CountryContentId = contact.Country.NameContentId,
HoursOfOperationContentId = contact.HoursOfOperationContentId,
WebsiteContentId = contact.WebsiteContentId,
OnlineSellerHomePageUrlContentId = contact.OnlineSellerHomePageUrlContentId,
ContactFormUrlContentId = contact.ContactFormUrlContentId,
RequestAQuoteUrlContentId = contact.RequestAQuoteUrlContentId,
NameContentId = contact.NameContentId
});
从查询中删除M:M关系会产生一个合理的单一查询(尽管有许多子选择)。只要将单个M:M关系添加到投影中,EF就会为结果生成2个带UNION ALL的查询。
以下是我们的配置:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Contact>()
.HasMany(c => c.Brands)
.WithMany(c => c.Contacts)
.Map(mc =>
{
mc.MapRightKey("BrandId");
mc.MapLeftKey("ContactId");
mc.ToTable("ContactBrands");
});
modelBuilder.Entity<Contact>()
.HasMany(c => c.Categories)
.WithMany(c => c.Contacts)
.Map(mc =>
{
mc.MapRightKey("CategoryId");
mc.MapLeftKey("ContactId");
mc.ToTable("ContactCategories");
});
modelBuilder.Entity<Contact>()
.HasMany(c => c.ContactTypes)
.WithMany(c => c.Contacts)
.Map(mc =>
{
mc.MapRightKey("ContactTypeId");
mc.MapLeftKey("ContactId");
mc.ToTable("ContactToContactType");
});
}
我不确定为什么EF会产生如此可怕的查询。任何有关如何改进查询的见解都非常有用。