2012-05-12 68 views
0

在MVC 3項目中使用Linq to Entity(Entity Framework)。涉及連接的複雜sql查詢的Linq和Lambda表達式

我的模型:

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

表 - 客戶
客戶端ID(PK)

表 - PropertyItems
PropertyItemID(PK )

Table - MemberContactPreference(C ontains選擇通過用戶 - 多對多)
用戶ID(FK)
PropertyItemID(FK)

表ClientProperties(包含屬於客戶PropertyItems PropertyItems - 多對多)
客戶端ID(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

林不太確定我明白你要做什麼,但我注意到的一件事是,當你找回它時,你還沒有枚舉集合。這意味着它保持爲可查詢狀態,使用'GetAllUsers(),ToArray()'和'GetClientProperties(ClientID).ToArray()'來確保這些集合在下一個查詢之前在內存中,因爲它會在db上執行我不認爲你的意圖是 –

回答

1

在那一代將IQueryable的for循環似乎是一件危險的事情,這可能在怪物SQL JOIN被立即執行結束。

無論如何,我不認爲你需要那樣做。這樣的事情呢?

// 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; 
    ... 

和使用property1查詢。這應該可以解決問題的原因。但如前所述,看起來整個過程可以改進。

+0

right,[外部變量陷阱](http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-被認爲有害.aspx),好電話!希望這將被固定在網絡5.0 – HugoRune

+0

看來他們這樣做:http://blogs.msdn.com/b/ericlippert/archive/2009/11/16/closing-over-the-loop-variable-part-two。 ASPX –