0

MVC 3 プロジェクトで Linq to Entity (Entity Framework) を使用します。

私のモデル:

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

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

表 - PropertyItems
PropertyItemID (PK)

テーブル - MemberContactPreference (ユーザーが選択した PropertyItems を含む - 多対多)
UserID(FK)
PropertyItemID(FK)

テーブル ClientProperties (クライアントに属する PropertyItems を含む - 多対多)
ClientID (FK)
PropertyItemID (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 を使用せずにラムダ式でこれを行うための提案。

ありがとう

4

2 に答える 2

1

forループでiqueryableを生成することは危険なことのように思われ、モンスターのSQL結合が一度に実行される可能性があります。

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

// 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();
于 2012-05-12T09:03:26.790 に答える
1

Hugo がクエリを改善する方法を提案してくれたことを信じています (+1)。しかし、それはあなたの問題の原因、つまり変更されたクロージャの落とし穴をまだ説明していません。

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

次のようにループを変更します。

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

クエリで property1 を使用します。これで問題の原因が解決するはずです。しかし、前述のように、プロセス全体を改善できるようです。

于 2012-05-12T13:32:43.820 に答える