2012-05-12 21 views
0

MVC 3プロジェクトのLinq to Entity(Entity Framework)の使用。結合を含む複雑なSQLクエリのLinqとLambda式

マイモデル:

表 - ユーザー
ユーザーID(PK)
...

表 - クライアント
クライアントID(PK)

表 - PropertyItems
PropertyItemID(PK )

テーブル - MemberContactPreference(C多くの多くの)
クライアントID(FK)
PropertyItemID( - ontains PropertyItemsはUsers-多くの多くの)
ユーザーID(FK)
PropertyItemID(FK)

表のClientProperties(クライアントに属しPropertyItemsが含まれていますが選択しましたFK)

クライアントが選択したすべてのプロパティを選択したすべての個別ユーザーをリストしたいとします。

私のアプローチ:

私はそれが正しい結果にのみ最初の時間を与える

Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID) 

Iqueryable<User> UsersMatchingClientProperties = GetAllUsers(); 



foreach (ClientProperty property in clientproperties) 
{ 

UsersMatchingClientProperties = (from uem in UsersMatchingClientProperties 
            join ucp in GetAllMemberContactPreferences on 
            ucp.UserID == uem.UserID 
            where uem.MemberContactPreferences.SelectMany(  
            mcp => mcp.PropertyItemID == property.PropertyItemID) 
            select uem).Distinct; 
} 

に特定のクライアントのすべてのプロパティのリストを得ました。これは、各反復でUsersMatchingClientPropertiesの項目数を減らさないためです。実際には、コレクションを新しい結果セットに置き換えます。私は各繰り返しでこのコレクションをフィルタリングしたいと思います。

また、Linqを使用せずにラムダ式でこれを行うための提案。 forループ内のIQueryableの生成がモンスターSQLで終わる可能性がある、危険なことのように思える

おかげ

+0

私はあなたが何をしようとしているのか理解していますが、私が気づいていることは、あなたが戻ってくるときにその列挙を列挙していないということです。つまり、GetAllUsers().ToArray()およびGetClientProperties(ClientID).ToArray()を使用して、これらのセットが次のクエリの前にメモリに格納されていることを確認します。私はあなたが意図しているとは思わない –

答えて

1

は一度に実行されて参加します。

とにかく、私はあなたがそれを必要とは思わない。このようなものはいかがですか?ここで

// for a given client, find all users 
// that selected ALL properties this client also selected 

Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID) 

Iqueryable<User> allUsers= GetAllUsers(); 

Iqueryable<MemberContactPreference> allMemberContactProperties = GetAllMemberContactPreferences(); 


Iqueryable<User> UsersMatchingClientProperties = allUsers 
.Where(user => allMemberContactProperties 
       .Where(membP => membP.UserID==user.UserID) 
       .All(membP => clientProperties 
          .Select(clientP => clientP.PropertyID) 
          .Contains(membP.PropertyID) 
       ) 
); 

は、あなたが与えられたクライアント

// for a given client, find all users 
// that selected ANY properties this client also selected 

Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID) 

Iqueryable<User> allUsers= GetAllUsers(); 

Iqueryable<MemberContactPreference> allMemberContactProperties = GetAllMemberContactPreferences(); 


Iqueryable<User> UsersMatchingClientProperties = clientproperties 
.Join(allMembersContactProperties, // join clientproperties with memberproperties 
     clientP => clientP.PropertyItemID, 
     membP => membP.PropertyItemID, 
     (clientP, membP) => membP)) // after the join, ignore the clientproperties, keeping only memberproperties 
.Distinct()      // distinct is optional here. but perhaps faster with it? 
.Join(allUsers,     //join memberproperties with users 
     membP => membP.UserID, 
     user => user.UserID, 
     (membP, user) => user))  // after the join, ignore the member properties, keeping only users 
.Distinct(); 
1

のための任意のプロパティを選択したユーザーが、私はヒューゴは、(1)のクエリを改善する方法を示唆して良い仕事をした信頼したい場合は、代替クエリです。しかし、それはあなたの問題の原因をまだ説明していません。これは修正された閉鎖の落とし穴です。

私はあなたのループの後に実際にUsersMatchingClientPropertiesでクエリを実行するいくつかのコードがあると思います。その時点で、クエリはループ変数の最後の値を持つを実行しますproperty! (ループ変数は、反復で作成される各クエリーデリゲートのクロージャであり、反復ごとに変更されます)。

変更するには、このようなループ:

foreach (ClientProperty property in clientproperties) 
{ 
    var property1 = property; 
    ... 

とクエリでプロパティ1を使用します。それは問題の原因を解決するはずです。しかし、言ったように、プロセス全体を改善できるように見えます。

+0

右、[外部変数トラップ](http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-考慮 - harmful.aspx)、良いコール!うまくいけばそれはネット5.0で固定されます – HugoRune

+0

それはそうです:http://blogs.msdn.com/b/ericlippert/archive/2009/11/16/closing-over-the-loop-variable-part-two。 aspx –