如何避免Entity Framework TPT继承中的多态行为,以便有效地查询基类型

时间:2012-12-05 11:58:23

标签: inheritance entity-framework-4 ef-code-first

概述

我首先使用实体​​框架4.3代码,使用流畅的界面来设置我的DbContext。我有一个基本 Item 类,其他类型继承了这个类,例如事件 BlogPost ForumThread ,< em> 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'表,每当创建一个新的item类型时都会在其中插入一条记录。但是,每当事件,博客文章数据发生变化等时,这个索引数据也需要更新。这会因标签等外键而变得更加复杂。每当事件上的标签被更改时,我都必须确保这些更改在匹配的索引表上同步。在考虑所有不同的项目类型时,这将成为一个管理的噩梦,坦率地说,似乎不是一个非常优雅的解决方案。

  • 数据库触发器

我首选的解决方案是代码而不是数据库触发/存储过程。

有没有办法构建一个查询来强制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无法翻译的查询。有解决方法吗?

1 个答案:

答案 0 :(得分:3)

  

有没有办法构建一个查询来强制EF只返回   基类型而不是导致太多的多态类型   加入和可怕的表现?

一般没有。您已映射继承,如果要返回Item的实例,则EF必须始终返回正确的type =&gt;它需要那些连接。 EF也不允许多次映射同一个表,因此您不能在同一映射中将Item映射为另一个POCO。

理论上,您应该能够查询Items并投影到您希望从基类获得的非映射 POCO类属性。不幸的是,这在.NET 4.0中不起作用 - EF still performed joins。您可以使用.NET 4.5和EF 5.0来解决此问题should be solved