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);
而且,我猜這是你的疏忽,但foo
是decimal
的枚舉類型,但您可以過濾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的將停止抱怨。
感謝您的意見。從一開始就把它作爲一個通用列表並且在每個if case中使用Where()。Tolist()'是不是一個好主意? – Johan
是的。 ToList()將執行查詢。第一個將從數據庫執行,其餘的將全部創建中間列表。你應該考慮儘可能地堅持你的IQueryable,並且在你對數據庫的查詢很好時緩慢執行。 –
所以你建議我在頂部使用'IQueriable'而不是'IEnumerable ' - 修改它,就像我今天一樣 - >在末尾運行'ToList()'?這不會引起同樣的問題,因爲resharper現在正在警告我嗎? –
Johan