我首先使用实体框架4.3代码和流畅的界面来设置我的DbContext.我有一个基类Item类,其他类型继承了它,如Event,BlogPost,ForumThread,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似乎并不喜欢这样.我目前没有手头的错误,但我对简单映射的尝试失败了.
替代解决方案?
>我曾考虑过实现一个’index’表,它看起来类似于’Item’表,并在创建新项类型时将记录插入到该表中.但是,每当事件,博客文章数据发生变化等时,这个索引数据也需要更新.这会因标签等外键而变得更加复杂.每当事件上的标签被更改时,我都必须确保这些更改在匹配的索引表上同步.在考虑所有不同的项目类型时,这将成为一个管理的噩梦,坦率地说,似乎不是一个非常优雅的解决方案.
>数据库触发器
我在这里的首选解决方案是代码而不是数据库触发器/存储过程.
有没有办法构建一个查询来强制EF只返回基类型而不是多态类型,这导致了太多的连接和可怕的性能?或者还有其他一些聪明的方法吗?
更新
通过Nuget(目标.Net 4.0)更新到EntityFramework 5之后,我已经能够通过标签查询项目并将项目投射到新的SearchItem中,这样可以在不加入TPT类型的情况下生成相当干净的SQL.
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无法翻译的查询.这有解决方法吗?
Is there a way to construct a query to force EF to only return the
base type instead of the polymorphic type which results in too many
joins and horrible performance?
一般没有.您已映射继承,如果要返回Item的实例,则EF必须始终返回正确的type =>它需要那些连接. EF也不允许多次映射同一个表,因此您不能在同一映射中将Item映射为另一个POCO.
从理论上讲,您应该能够从基类中查询项目和项目到您希望的非映射POCO类属性.不幸的是,这在.NET 4.0 – EF still performed joins中不起作用.您可以尝试使用.NET 4.5和EF 5.0,本期为should be solved.