2013-07-10 23 views
1

我從數據庫中獲取密鑰列表,我根據某些條件修改密鑰。該列表被序列化爲對AJAX調用的響應。修改EF結果並序列化到客戶端

但是,當我一次修改列表幾次時,Resharper告訴我「possible multiple enumeration of ienumerable」。

我應該在所有涉及列表的行上使用ToList()嗎?

請推薦一個合適的方法:

IEnumerable<decimal> foo = databaseContext.Foo.Select(f=>f.Key); 

if(something) 
    foo = foo.Where(f=>f.Bar > 5); 

if(somethingElse) 
    foo = foo.Where(f=>f.Bar > 15); 

var json = new JavaScriptSerializer.Serialize(new { fooKeys = foo.ToList() }); 

HttpContext.Current.Response.Write(json); 

回答

3

Resharper的分析是正確的。您可能會多次執行查詢。問題出在你的第一行:

IEnumerable<decimal> foo = databaseContext.Foo.Select(f=>f.Key); 

在Entity Framework中,很少有擴展會實現查詢。其中之一是AsEnumerable()。行的右邊部分有效地構造了一個IQueryable,但左邊的部分是IEnumerable。此時,IQueryable將通過調用AsEnumerable隱式轉換爲IQueryable,您的查詢將實現。

如果你想推遲查詢的執行過程中,左側應該是IQueryable(爲了簡化使用var):

var foo = databaseContext.Foo.Select(f=>f.Key); 

而且,我猜這是你的疏忽,但foodecimal的枚舉類型,但您可以過濾Bar屬性。這甚至不會編譯。

編輯:我冒昧地修改原來的代碼(第一部分)向您展示一個細分:

// databaseContext.Foo is a (presumably) DbSet<Foo> that implements 
// IQueryable<Foo>. Because the variable foo is set to be an IEnumerable<Foo> 
// and that IQueryable<Foo> implements IEnumerable<Foo> by calling 
// as AsEnumerable(), any further manipulation of the IEnumerable<Foo> 
// will be with LINQ to Object and not Linq to SQL (with Entity Framework) 
IEnumerable<Foo> foo = databaseContext.Foo; 

// Because of the previous point, this will potentially execute the query 
if(something) 
    foo = foo.Where(f=>f.Bar > 5); 

// And this will as well 
if(somethingElse) 
    foo = foo.Where(f=>f.Bar > 15); 

// And ToList() will definitely execute it. 
var json = new JavaScriptSerializer.Serialize(new { fooKeys = foo.Select(f => f.Key).ToList() }); 

HttpContext.Current.Response.Write(json); 

現在,如果你這樣做,而不是:

// DbSet<Foo> will create an IQueryable<Foo>. An Entity Framework IQueryProvider 
// will compile this to an SQL when we want to materialize the query 
IQueryable<Foo> foo = databaseContext.Foo; 

// Now, if this is hit, it's fine because IQueryable.Where returns an IQueryable 
// of the same type. We still live in the 
if(something) 
    foo = foo.Where(f=>f.Bar > 5); 

// Same point as before. foo is still an IQueryable<Foo> and the materialization 
// is not provoked yet. 
if(somethingElse) 
    foo = foo.Where(f=>f.Bar > 15); 

// Here, foo.Select() will return an IQueryable<decimal> (or whatever the type 
// of the Foo.Key property is) and then ToList() will get the IEnumerable<decimal> 
// version. At that point, any further manipulation is done through Linq to Object 
// but the query won't be sent to the database until it is iterated (ie 
// the IEnumerable<decimal>.GetEnumerator() is called). The IEnumerable<decimal> 
// version of the will be passed to the List<T>(IEnumerable<T>) constructor 
// which will iterate through the Enumerable with the GetEnumerator method. 
var json = new JavaScriptSerializer.Serialize(new { fooKeys = foo.Select(f => f.Key).ToList() }); 

HttpContext.Current.Response.Write(json); 

正如你所看到的,在開始時使用IQueryable和直到ToList(),你還是會住在IQueryable延遲查詢執行的世界,ReSharper的將停止抱怨。

+0

感謝您的意見。從一開始就把它作爲一個通用列表並且在每個if case中使用Where()。Tolist()'是不是一個好主意? – Johan

+0

是的。 ToList()將執行查詢。第一個將從數據庫執行,其餘的將全部創建中間列表。你應該考慮儘可能地堅持你的IQueryable,並且在你對數據庫的查詢很好時緩慢執行。 –

+0

所以你建議我在頂部使用'IQueriable '而不是'IEnumerable ' - 修改它,就像我今天一樣 - >在末尾運行'ToList()'?這不會引起同樣的問題,因爲resharper現在正在警告我嗎? – Johan

0

我覺得當你做一個選擇你得到一個可查詢的,我認爲你不能分配給可查詢的IEnumerable的對象集你的代碼是不正確的。

一個queryAble實際上並沒有對db執行一個請求,直到你使用IQueryable的ToList()方法。

所以你首先應該定義你的查詢,然後用ToList()方法對數據庫執行它,它將返回你的類型列表。

你應該重新安排你的代碼到這樣的東西。

var resultSetInMemory = new List<decimal>(); 

if(somethink) { 
resultSetInMemory = databaseContext.Foo 
            .Where(f=>f.Bar > 5) 
            .Select(f.key) 
            .ToList(); 
} 

if(somethingElse) { 
resultSetInMemory = databaseContext.Foo 
            .Where(f=>f.Bar > 15) 
            .Select(f.key) 
            .ToList(); 
} 

var json = new JavaScriptSerializer.Serialize(new { fooKeys = resultSetInMemory }); 

這是我看到了很多的辦法。希望這也能幫助你。

+0

因此,你建議可能有兩個查詢執行對數據庫,而失去了第一個結果,甚至沒有得到相同的結果,他應該? –

+0

啊,不會那麼好。你是對的。更好的方法是在函數的結尾說ToList(),你會返回結果。 – straeger