1

我正在从数据库中获取一个键列表,我根据某些条件对其进行修改。该列表被序列化为对 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);
4

2 回答 2

3

Resharper 的分析是正确的。您可能会多次执行查询。问题出在您的第一行:

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

在 Entity Framework 中,很少有扩展会实现查询。其中之一是AsEnumerable()。你的行的右边部分有效地构造了一个IQueryable但左边的部分是一个IEnumerable。那时,将通过调用IQueryable隐式转换为 an ,您的查询将被具体化。IQueryableAsEnumerable

如果你想推迟查询的执行,左边应该是IQueryablevar为了简单起见):

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

于 2013-07-10T09:57:30.050 回答
0

我认为当您在对象集上进行选择时您的代码不正确,您会得到一个可查询的对象我认为您不能将可查询对象分配给 IEnumerable。

在您使用 IQueryable<> ToList() 方法之前,queryAble 实际上不会对数据库进行请求。

因此,您首先应该定义您的查询,然后使用 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 });

这是我见过很多的方法。希望这对你也有帮助。

于 2013-07-10T09:38:33.450 回答