用Entity Framework開發項目過程中,發現linq查詢返回的結果,與SQL查詢出的結果不一致。
問題表現為:SQL返回了我們期望的結果,但是LINQ查詢出來的結果確是一些重復記錄。
這種問題一般是對數據庫視圖進行查詢時發生,問題產生的原因及現象如下:
1)使用.net 的linq to sql,或linq to entities 生成的實體對象,出於為提高效率等原因,會對每個對象自動判斷出一些列做為實體對象的Key(EntityKey),
這個Key就是相當於我們表中的主鍵,查詢結果時,會根據這個Key列的值進行判斷,假設數據庫中有視圖View1有3條記錄如下:
====View1=====
列1(key列) 列2 列3
1 a1 a1
1 a2 a2
3 a3 a3
2)假設此View1的實體對象中,列1判斷為Key列(標識列),如果當前我們有一個如下的查詢,SQL語句如下:
SELECT 列1,列2,列3 FROM [View1]
顯然,查詢出來正確的結果為:
====SQL返回正確的結果=====
列1(key列) 列2 列3
1 a1 a1
1 a2 a2
3 a3 a3
3)但是如果你用LINQ語句去查:var objs = db.entity.select(v=>v);//entity為實體對象
得出的結果為:
====LINQ返回錯誤的結果=====
列1(key列) 列2 列3
1 a1 a1
1 a1 a1 (這行是錯誤的)
3 a3 a3
問題分析:
我們會發現,第2條記錄與第一條記錄是一樣的,原因就在於實體對象中的Key列即列1的值相同(在我們的例子中都為1),
那么LINQ返回第一條數據時,沒有問題,正常返回;
返回第二條時,判斷Key列的值,發現都為1,那么就認為第二條數和第一條數據是一樣的,所以直接返回第一行記錄的值,做為第二條記錄。
第三條記錄,key列不同,取回正確數據。
如果LINQ的查詢還附加排序也會影響LINQ查詢的結果,總之會返回不同的錯誤結果
解決方法:
1.找出視圖中可以用來唯一標識該行記錄的那些列,在edmx中將它們設為實體鍵(Key列),在我的項目中還給這個字段加了唯一鍵約束,防止LINQ查詢結果出錯。
通過上面分析,我們就可以明白為什么出現這種問題都是在使用視圖的時候,因為如果是表的話,那么基本上主鍵會被判斷為實體對象的Key,顯示不會出現數據重復問題。
但是視圖就不一樣,特別是一些GroupBy語句創建的視圖,.net底層自動判定的EntityKey,就會有問題,一般都是隨便找一個或者幾個列,GroupBy 語句的視圖中,
基本上沒有哪一個或者哪幾個字段能做為記錄唯一標識,那么我們只能給記錄生成標識列來解決。
比如有一個視圖View2的創建語句如下:
select col1,col2,col3,count(col4) as col4 from [table] group by col1,col2,col3
此時為了避免錯誤的結果,有些朋友,就把視圖中col1,col2,col3的列都設為實體鍵,這樣的確可以避免一些問題,有時候列太多了,全設為實體鍵,不是一個好辦法。
但是,我們可以給記錄自動生成列,就是使用 row_number() 方法,比如可以使用如下語句創建視圖:
select [index]=row_number() over (order by ID), col1,col2,col3,count(col4) as col4 from [table] group by col1,col2,col3
然后將index列設為實體鍵就OK了。
2.使用EF執行SQL語句的方法 DbContext.Database.SqlQuery<T>(視圖名),也可以解決問題,效率就不用我說,通過SQL Server Profiler跟蹤測試一下就知道了。
本文參考了TobeorNot的經驗加補充,如有更好的方法,請指教。
