Overview
我首先使用实体框架 4.3 代码和流畅的界面来设置我的 DbContext。我有一个基地Item具有继承此类型的其他类型的类,例如Event, BlogPost, 论坛主题, WikiPage等等。
这些继承的类型与我认为实体框架所指的 TPT 继承进行映射。在查询“事件”或“博客文章”等单一类型时,这非常有效,但在尝试跨所有类型进行查询时,由于为了实现 EF 提供的开箱即用的多态行为而需要连接,因此会构造非常复杂的查询,并且性能很差。
问题背景
我想构建一个全局搜索功能,我只需要访问基本的“Item”实体,而不是继承的实例。我希望能够按名称、标签等查询基本项目类。执行任何类型的 LINQ 查询,即使在请求基本项类型时,仍然会导致多态行为,从而降低性能。
代码优先模型
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Body { get; set; }
public DateTime Created { get; set; }
public int? CreatedBy { get; set; }
public int? LastModifiedBy { get; set; }
public DateTime? LastModified { get; set; }
public virtual User Author { get; set; }
public bool IsDeleted { get; set; }
public string ImageUri { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Event : Item
{
// Additional properties
}
public class BlogPost : Item
{
// Additional properties
}
我希望能够做的是将另一个 POCO 映射到同一个基表,这样当我在其上构造查询时,就不会涉及继承问题。不过EF似乎不喜欢这样。我目前手头上没有错误,但我尝试简单的映射失败了。
替代解决方案?
我的首选解决方案是代码中的解决方案,而不是数据库触发器/存储过程中的解决方案。
有没有办法构造一个查询来强制 EF 仅返回基本类型而不是多态类型,从而导致过多的连接和糟糕的性能?或者还有其他聪明的方法来解决这个问题吗?
Update
通过 Nuget 更新到 EntityFramework 5(针对 .Net 4.0)后,我已经能够通过项目的标签查询项目并将其投影到新的项目中搜索项目这会产生相当干净的 SQL,无需连接到 TPT 类型。
var x = from item in repository.FindAll<Item>()
where item.Tags.Any(t => t.Name == "test")
select new SearchItem
{
Id = item.Id,
Name = item.Name,
Body = item.Body,
Created = item.Created,
CreatedBy = item.CreatedBy,
IsDeleted = item.IsDeleted,
ImageUri = item.ImageUri,
MembershipEntityId = item.MembershipEntityId,
//Tags = (from t in item.Tags
// select new Tag
// {
// Id = t.Id,
// Name = t.Name,
// MembershipEntityId = t.MembershipEntityId
// })
};
SQL
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Body] AS [Body],
[Extent1].[Created] AS [Created],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[IsDeleted] AS [IsDeleted],
[Extent1].[ImageUri] AS [ImageUri],
[Extent1].[MembershipEntityId] AS [MembershipEntityId]
FROM [dbo].[Item] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[ItemTag] AS [Extent2]
INNER JOIN [dbo].[Tag] AS [Extent3] ON [Extent3].[Id] = [Extent2].[Tag_Id]
WHERE ([Extent1].[Id] = [Extent2].[Item_Id]) AND (N'test' = [Extent3].[Name])
)
这解决了我的一半问题,因为我现在可以按标签搜索基本类型。然而,我希望能够使用新的投影返回标签。包含注释掉的代码位会导致 EF 无法转换的查询。有解决方法吗?