Ami*_*aqi 166 linq-to-entities entity-framework entity-framework-4 entity-framework-5
我知道LINQ to Entities和LINQ to Objects的一些区别,第一个实现IQueryable
和第二个实现IEnumerable
,我的问题范围在EF 5中.
我的问题是这3种方法的技术差异是什么?我看到,在许多情况下,所有这些都有效.我也看到使用它们的组合.ToList().AsQueryable()
.
这些方法究竟意味着什么?
是否有任何性能问题或某些因素会导致使用另一个?
例如,为什么会使用?.ToList().AsQueryable()
而不是.AsQueryable()
?
Ger*_*old 329
关于这一点有很多话要说.让我专注于AsEnumerable
与AsQueryable
和提ToList()
沿途.
AsEnumerable
并分别AsQueryable
转换或转换为IEnumerable
或IQueryable
.我说演员或转换有一个原因:
当源对象已实现目标接口时,将返回源对象本身,但将其强制转换为目标接口.换句话说:类型没有改变,但编译时类型是.
当源对象未实现目标接口时,源对象将转换为实现目标接口的对象.因此,类型和编译时类型都会更改.
让我用一些例子来说明这一点.我有这个小方法来报告编译时类型和对象的实际类型(由Jon Skeet提供):
void ReportTypeProperties<T>(T obj)
{
Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}
Run Code Online (Sandbox Code Playgroud)
让我们尝试一个任意的linq-to-sql Table<T>
,它实现IQueryable
:
ReportTypeProperties(context.Observations);
ReportTypeProperties(context.Observations.AsEnumerable());
ReportTypeProperties(context.Observations.AsQueryable());
Run Code Online (Sandbox Code Playgroud)
结果:
Compile-time type: Table`1
Actual type: Table`1
Compile-time type: IEnumerable`1
Actual type: Table`1
Compile-time type: IQueryable`1
Actual type: Table`1
Run Code Online (Sandbox Code Playgroud)
您会看到始终返回表类本身,但其表示会更改.
现在是一个实现的对象IEnumerable
,而不是IQueryable
:
var ints = new[] { 1, 2 };
ReportTypeProperties(ints);
ReportTypeProperties(ints.AsEnumerable());
ReportTypeProperties(ints.AsQueryable());
Run Code Online (Sandbox Code Playgroud)
结果:
Compile-time type: Int32[]
Actual type: Int32[]
Compile-time type: IEnumerable`1
Actual type: Int32[]
Compile-time type: IQueryable`1
Actual type: EnumerableQuery`1
Run Code Online (Sandbox Code Playgroud)
它就是.AsQueryable()
已将数组转换为一个EnumerableQuery
",表示IEnumerable<T>
集合作为IQueryable<T>
数据源".(MSDN).
AsEnumerable
经常用于从任何IQueryable
实现切换到LINQ到对象(L2O),主要是因为前者不支持L2O所具有的功能.有关更多详细信息,请参阅AsEnumerable()对LINQ实体的影响是什么?.
例如,在实体框架查询中,我们只能使用有限数量的方法.因此,例如,如果我们需要在查询中使用我们自己的方法之一,我们通常会编写类似的东西
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => MySuperSmartMethod(x))
Run Code Online (Sandbox Code Playgroud)
ToList
- 将IEnumerable<T>
a 转换为a List<T>
- 通常也用于此目的.使用AsEnumerable
vs. 的优点ToList
是AsEnumerable
不执行查询.AsEnumerable
保留延迟执行并且不构建通常无用的中间列表.
另一方面,当需要强制执行LINQ查询时,ToList
可以采用这种方式.
AsQueryable
可用于在LINQ语句中创建可枚举集合接受表达式.有关更多详细信息,请参阅此处:我真的需要在集合上使用AsQueryable()吗?.
AsEnumerable
像药物一样工作.这是一个快速解决方案,但需要付出代价并且无法解决潜在的问题.
在许多Stack Overflow答案中,我看到人们申请AsEnumerable
修复LINQ表达式中不支持的方法的任何问题.但价格并不总是很清楚.例如,如果你这样做:
context.MyLongWideTable // A table with many records and columns
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate })
Run Code Online (Sandbox Code Playgroud)
...一切都被巧妙地翻译成了一个过滤(Where
)和项目(Select
)的SQL语句.也就是说,SQL结果集的长度和宽度都会减少.
现在假设用户只想查看日期部分CreateDate
.在实体框架中,您将很快发现......
.Select(x => new { x.Name, x.CreateDate.Date })
Run Code Online (Sandbox Code Playgroud)
...不受支持(在撰写本文时).啊,幸运的是有AsEnumerable
修复:
context.MyLongWideTable.AsEnumerable()
.Where(x => x.Type == "type")
.Select(x => new { x.Name, x.CreateDate.Date })
Run Code Online (Sandbox Code Playgroud)
当然,它可能会运行.但它将整个表拉入内存然后应用过滤器和投影.嗯,大多数人都很聪明,可以做第Where
一个:
context.MyLongWideTable
.Where(x => x.Type == "type").AsEnumerable()
.Select(x => new { x.Name, x.CreateDate.Date })
Run Code Online (Sandbox Code Playgroud)
但仍然首先获取所有列,并在内存中完成投影.
真正的解决方法是:
context.MyLongWideTable
.Where(x => x.Type == "type")
.Select(x => new { x.Name, DbFunctions.TruncateTime(x.CreateDate) })
Run Code Online (Sandbox Code Playgroud)
(但这需要更多的知识......)
现在一个重要的警告.当你这样做
context.Observations.AsEnumerable()
.AsQueryable()
Run Code Online (Sandbox Code Playgroud)
你最终得到的源对象表示为IQueryable
.(因为这两种方法只能转换并且不转换).
但是,当你这样做
context.Observations.AsEnumerable().Select(x => x)
.AsQueryable()
Run Code Online (Sandbox Code Playgroud)
结果会是什么?
的Select
产生WhereSelectEnumerableIterator
.这是一个实现的内部.Net类IEnumerable
,而不是IQueryable
.因此,转换到另一种类型已经发生,随后AsQueryable
再也无法返回原始来源.
这个含义是,使用AsQueryable
是没有办法神奇地注入查询提供其具体特点为枚举.假设你这样做
var query = context.Observations.Select(o => o.Id)
.AsEnumerable().Select(x => x.ToString())
.AsQueryable()
.Where(...)
Run Code Online (Sandbox Code Playgroud)
where条件永远不会转换为SQL.AsEnumerable()
其次是LINQ语句,最终切断了与实体框架查询提供程序的连接.
我故意展示这个例子,因为我在这里看到了一些问题,例如人们试图Include
通过调用将"注入" 功能集成到集合中AsQueryable
.它编译并运行,但它什么都不做,因为底层对象不再具有Include
实现.
到目前为止,这只是关于Queryable.AsQueryable
和Enumerable.AsEnumerable
扩展方法.但是当然任何人都可以使用相同的名称(和函数)编写实例方法或扩展方法.
实际上,特定AsEnumerable
扩展方法的一个常见示例是DataTableExtensions.AsEnumerable
.DataTable
不执行IQueryable
或IEnumerable
,因此正扩展方法不适用.
Xin*_*Xin 40
ToList()
AsEnumerable()
Func<TSource, bool>
AsQueryable已()
Expression<Func<TSource, bool>>
AsQueryable()
通常比AsEnumerable()
最初生成T-SQL 要快得多,其中包括Linq中的所有where条件.ash*_*ina 12
ToList()将成为内存中的所有内容,然后您将继续处理它.所以,ToList().where(应用一些过滤器)在本地执行.AsQueryable()将远程执行所有操作,即将其上的过滤器发送到数据库以进行应用.在您执行它之前,Queryable不会执行任何操作.然而,ToList立即执行.
另外,看看这个答案为什么使用AsQueryable()而不是List()?.
编辑:此外,在你的情况下,一旦你做ToList()然后每个后续操作是本地的,包括AsQueryable().一旦开始本地执行,就无法切换到远程.希望这会让它更清晰一些.
归档时间: |
|
查看次数: |
62691 次 |
最近记录: |