修改EF结果并序列化到客户端

时间:2013-07-10 09:05:35

标签: c# asp.net entity-framework

我从数据库中获取密钥列表,我根据特定条件修改了这些密钥。该列表被序列化为对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);

2 个答案:

答案 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);

此外,我猜这是对您的疏忽,但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将停止投诉。< / 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 });

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