Linq和Lambda表达式涉及涉及连接的复杂sql查询

时间:2012-05-12 00:27:38

标签: linq entity-framework lambda

在MVC 3项目中使用Linq to Entity(Entity Framework)。

我的模特:

表 - 用户
用户ID(PK)
...

表 - 客户

ClientID(PK)

表 - PropertyItems
PropertyItemID(PK)

表 - MemberContactPreference(包含由用户选择的PropertyItems - 多对多)
用户名(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的情况下在Lambda表达式中执行此操作的建议。

谢谢

2 个答案:

答案 0 :(得分: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();

答案 1 :(得分:1)

我相信Hugo做得很好,建议改进查询的方法(+1)。但这还没有解释你的问题的原因,这是修改后的封闭陷阱。

我认为在你的循环之后有一些代码实际上在UsersMatchingClientProperties中执行查询。此时,查询将使用循环变量property 的最后一个值执行! (循环变量是在迭代中创建的每个查询委托中的闭包,并且每次迭代都会修改它。)

像这样改变循环:

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

并在查询中使用property1。这应该可以解决问题的原因。但正如所说,看起来整个过程都可以改进。