我从数据库中获取密钥列表,我根据特定条件修改了这些密钥。该列表被序列化为对AJAX调用的响应。
但是当我在整个过程中多次修改列表时,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);
答案 0 :(得分:3)
Resharper在分析中是正确的。您可能会多次执行查询。问题出在你的第一行:
IEnumerable<decimal> foo = databaseContext.Foo.Select(f=>f.Key);
在实体框架中,很少有扩展会实现查询。其中一个是AsEnumerable()
。你的行的右边部分有效地构造了IQueryable
,但左手部分是IEnumerable
。此时,IQueryable
将通过调用IQueryable
隐式投放到AsEnumerable
,您的查询将会实现。
如果您想推迟执行查询,则左侧应为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将停止投诉。< / p>
答案 1 :(得分:0)
我认为当您对一个对象集进行选择时,您的代码是不正确的,我认为您无法将可查询分配给IEnumerable。
在您使用IQueryable&lt;&gt;之前,queryAble实际上不会对数据库执行请求。 ToList()方法。
所以你首先应该定义你的查询,然后用ToList()方法对db执行它,它将返回你的Type的List。
您应该将代码重新安排为类似的内容。
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 });
这是我见过的很多方法。希望这对你也有帮助。