概要
DbContext をセットアップするために流暢なインターフェイスで最初にエンティティ フレームワーク 4.3 コードを使用しています。Event、BlogPost、ForumThread、WikiPageなど、これを継承する他のタイプの基本Itemクラスがあります。
これらの継承された型は、エンティティ フレームワークが 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はこれが気に入らないようです。現時点ではエラーは発生していませんが、単純なマッピングの試みは失敗しました。
代替ソリューション?
「アイテム」テーブルに似た「インデックス」テーブルを実装し、新しいアイテム タイプが作成されるたびにそこにレコードを挿入することを考えていました。しかし、イベントやブログ投稿データが変更されるたびに、このインデックス データも更新する必要があります。これは、タグなどの外部キーによってさらに複雑になります。イベントが変更されたというタグの場合は常に、これらの変更が一致するインデックス テーブルでも同期されていることを確認する必要があります。さまざまな種類のアイテムをすべて考慮すると、これを管理するのは少し悪夢のようになり、率直に言って、非常に洗練されたソリューションとは思えません。
データベース トリガー
ここでの私の好ましい解決策は、データベーストリガー/ストアドプロシージャではなく、コード内にあるものです。
結合が多すぎてパフォーマンスが低下するポリモーフィック型ではなく、基本型のみを返すように 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 が変換できないクエリになります。これに対する回避策はありますか?