2016-01-21 9 views
6

照会のナビゲーションプロパティをIncludeに含めて、後で遅延ロードされないようにします。しかし、私はSelect投影を持つ匿名ラッパーオブジェクトを作成すると動作しません。EF:「選択」投影を使用してラッパーオブジェクトを作成するときにナビゲーションプロパティを含める

簡略化した例を示します。 エンティティ

public class UserEntity { 
    public string Name {get;set;} 
    public virtual ICollection<UserEntity> Friends { get; set; } 
} 

クエリ

var entry = _dbCtx 
    .Users 
    .Include(x => x.Friends) 
    // Select here is simplified, but it shows the wrapping 
    .Select(user => new { 
     User = user 
    }) 
    .First(); 

// Here we have additional lazy loaded DB call 
var friends = entry.User.Friends.Select(x => x.Name).ToList(); 

そして、私は、生成されたSQLからも参照は、そのナビゲーションプロパティが含まれていません。

SELECT 
    [Limit1].[Name] AS [Name], 
    FROM (SELECT TOP (1) 
     [Extent1].[Name] AS [Name] 
     FROM [dbo].[Users] AS [Extent1] 
    ) AS [Limit1] 

はそれが可能ですIncludeへのナビゲーションプロパティFriendsこの場合、Userは遅延読み込みなしでデータを取得するでしょうか?

私はこれが仕事にも期待していた:

var entry = _dbCtx 
    .Users 
    .Select(user => new { 
     User = user 
    }) 
    .Include(x => x.User.Friends) 
    .First(); 

しかし例外取得:

InvalidOperationExceptionがのクエリの結果の型はEntityTypeやエンティティ要素の型とCollectionTypeはいずれでもありません。インクルードパスは、これらの結果タイプのいずれかを持つクエリに対してのみ指定できます。

あり、私はに来て、いくつかの回避策がありますが、それらは何とかトリッキーです:

  1. Selectで私たちの匿名オブジェクトに追加プロパティを追加します。

    var entry = _dbCtx 
        .Users 
        .Select(user => new { 
         User = user, 
         UsersFriends = user.Friends 
        }) 
        .First(); 
    
    // manually copy the navigation property 
    entry.User.Friends = user.UsersFriends; 
    
    // Now we don't have any addition queries 
    var friends = entry.User.Friends.Select(x => x.Name).ToList(); 
    
  2. 地図ものユーザーDBレベルの匿名オブジェクトを作成し、C#のUserEntityにプロパティをマップします。

    var entry = _dbCtx 
        .Users 
        .Select(user => new { 
         User = new { 
          Name = user.Name, 
          Friends = user.Friends 
         } 
        }) 
        .Take(1) 
        // Fetch the DB 
        .ToList() 
        .Select(x => new { 
         User = new UserEntity { 
          Name = x.Name, 
          Friends = x.Friends 
         } 
        }) 
        .First(); 
    
    // Now we don't have any addition queries 
    var friends = entry.User.Friends.Select(x => x.Name).ToList(); 
    

だから今、そこFriendsためLEFT OUTER JOINはあるが、両方の回避策は、非常に良いものではありません。

1)追加のプロパティとコピークリーンな方法ではありません。

2)私のUserEntityは他の多くのプロパティを持っています。さらに、新しいプロパティを追加するたびに、ここでセレクタも変更する必要があります。

最初のサンプルを含むナビゲーションプロパティを実現する方法はありますか?

読んでいただきありがとうございます。私は誰かがこれについての手掛かりを持っていることを願っています。

EDIT:

私は本当のユースケースを示すために、エンティティおよびクエリーを拡張します。

エンティティ

public class UserEntity { 
    public string Name {get;set;} 
    public int Score {get;set;} 
    public virtual ICollection<UserEntity> Friends { get; set; } 
} 

クエリ

var entry = _dbCtx 
    .Users 
    .Include(x => x.Friends) 
    .Select(user => new { 
     User = user, 
     Position = _dbCtx.Users.Count(y => y.Score > user.Score) 
    }) 
    .First(); 

答えて

0

ない_why_するような答えが、良いコードのフォーマットたかった...

私は実際によそんなにうまくいくのは驚いた。おそらくEFはあなたの投影で直接Friendsプロパティを使用していないことを検出しているため、無視しています。

var entry = _dbCtx 
    .Users 
    .Include(x => x.Friends) 
    .Take(1); // replicate "First" inside the EF query to reduce traffic 
    .AsEnumerable() // shift to linq-to-objects 
    // Select here is simplified, but it shows the wrapping 
    .Select(user => new { 
     User = user 
    }) 
    .First() 
+0

我々は(クエリの後) 'C#'側のラッパーオブジェクトを作成してあなたのサンプルが動作しますが、ないsql' 'と:あなたはEFクエリの外のオブジェクト(複数可)をカプセル化した場合にどのような。しかし、私は 'db'レベルでそれを必要としました。理由:私は 'Select'で追加の副問い合わせを実行します。面白いことに、私の最初の回避策を見てください。クエリーで 'user.Friends'を使用していますが、Materialをマテリアライゼーション後にエンティティにコピーしなければなりません。 – tenbits

+0

なぜDBクエリで匿名型に投影する必要がありますか?あなたは私が言うことができる限り、同じ結果をもたらします。あるいは、あなたはSQLに投影したい_additional_メソッド( 'Where'、' OrderBy'など)を投げかけていますか? –

+0

私の投稿の編集を参照して、ユーザのサブクエリを実行します。各ユーザを繰り返し処理するので、DBで行う必要があります。パフォーマンスのために、私は 'c# 'でこれを行うことはできません。 – tenbits

関連する問題