在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表达式中执行此操作的建议。
谢谢
答案 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。这应该可以解决问题的原因。但正如所说,看起来整个过程都可以改进。