概述
我首先使用实体框架 4.3 代码和流利的界面来设置我的 DbContext。我有一个基Item类,其中包含其他类型,例如Event、BlogPost、ForumThread、WikiPage等。
这些继承的类型与我认为实体框架称为 TPT 继承的映射。这在查询诸如“事件”或“博客文章”之类的单一类型时效果很好,但在尝试跨所有类型查询时构造非常复杂的查询,但由于实现 EF 开箱即用的多态行为所需的连接,因此性能非常糟糕.
问题背景
我想构建一个全局搜索功能,我只需要访问基本的“项目”实体而不是继承的实例。我希望能够通过名称、标签等查询基础项目类。执行任何类型的 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 似乎并不喜欢这样。目前我手头没有错误,但我尝试简单映射失败了。
替代解决方案?
我曾考虑过实现一个类似于“Item”表的“index”表,并在创建新项目类型时将记录插入其中。但是,每当事件、博客文章数据发生变化等时,也需要更新此索引数据。这会因标签等外键而变得更加复杂。每当标记上的事件发生更改时,我都必须确保这些更改也在匹配的索引表上同步。在考虑所有不同的项目类型时,这将成为管理的一场噩梦,坦率地说,这似乎不是一个非常优雅的解决方案。
数据库触发器
我在这里首选的解决方案是代码而不是数据库触发器/存储过程。
有没有办法构造一个查询来强制 EF 只返回基类型而不是多态类型,这会导致过多的连接和可怕的性能?或者还有其他聪明的方法吗?
更新
通过 Nuget(针对 .Net 4.0)更新到 EntityFramework 5 后,我已经能够通过它们的标签查询项目并投影到一个新的SearchItem中,这会产生相当干净的 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 无法翻译的查询。有解决方法吗?