4

概述

我首先使用实体​​框架 4.3 代码和流利的界面来设置我的 DbContext。我有一个基Item类,其中包含其他类型,例如EventBlogPostForumThreadWikiPage等。

这些继承的类型与我认为实体框架称为 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 无法翻译的查询。有解决方法吗?

4

1 回答 1

3

有没有办法构造一个查询来强制 EF 只返回基类型而不是多态类型,这会导致过多的连接和可怕的性能?

一般没有。您已经映射了继承,如果您想返回 的实例Item,EF 必须始终返回正确的类型 => 它需要这些连接。EF 也不允许多次映射同一个表,因此您不能在同一个Item映射中再次映射为另一个 POCO。

从理论上讲,您应该能够仅从基类中查询Items和投影到您的非映射POCO 类的属性。不幸的是,这在 .NET 4.0 中不起作用 - EF 仍然执行 joins。您可以尝试使用 .NET 4.5 和 EF 5.0来解决此问题。

于 2012-12-05T13:10:07.340 回答