为什么EF 4.1不支持复杂查询以及linq-to-sql?

Kal*_*exx 9 entity-framework linq-to-sql entity-framework-4.1

我正在将内部Web应用程序从Linq-To-Sql转换为现有数据库中的EF CodeFirst.我最近越来越多地对Linq-To-Sql的局限性感到恼火,并且在更新一个交织在一起的数据库表之后不得不更新edmx,这最终让我感到很沮丧,无法切换到EF.

但是,我遇到了几种情况,使用linq和Linq-To-Sql比最新的Entity Framework更强大,我想知道是否有人知道它的推理?其中大部分似乎都涉及转型.例如,以下查询适用于L2S但不适用于EF:

        var client = (from c in _context.Clients
                      where c.id == id
                      select ClientViewModel.ConvertFromEntity(c)).First();
Run Code Online (Sandbox Code Playgroud)

在L2S中,这正确地从数据库中检索客户端并将其转换为ClientViewModel类型,但在EF中,这种异常表示Linq to Entities无法识别该方法(这在我编写它时是有意义的.

为了让这个在EF工作,我必须selectFirst()通话后移动到.

另一个例子是我的查询来检索客户端列表.在我的查询中,我将其转换为匿名结构,以转换为JSON:

        var clients = (from c in _context.Clients
                       orderby c.name ascending
                       select new
                       {
                           id = c.id,
                           name = c.name,
                           versionString = Utils.GetVersionString(c.ProdVersion),
                           versionName = c.ProdVersion.name,
                           date = c.prod_deploy_date.ToString()
                       })
                       .ToList();
Run Code Online (Sandbox Code Playgroud)

我的Utils.GetVersionString()方法不仅导致EF中不受支持的方法异常,c.prod_deploy_date.ToString()原因也是如此,这很简单DateTime.像以前一样,为了解决它,我必须在之后进行选择转换ToList().


编辑:我刚遇到的另一个案例是,EF无法处理比较L2S对其没有任何问题的实体的子句.例如查询

context.TfsWorkItemTags.Where(x => x.TfsWorkItem == TfsWorkItemEntity).ToList()
Run Code Online (Sandbox Code Playgroud)

抛出一个异常而不是我必须这样做

context.TfsWorkItemTags.Where(x => x.TfsWorkItem.id == tfsWorkItemEntity.id).ToList() 
Run Code Online (Sandbox Code Playgroud)


编辑2:我想添加我发现的另一个问题.显然你不能在EF Linq查询中使用数组,这可能比我更烦恼.因此,例如,现在我将表示版本的实体转换为a int[4]并尝试对其进行查询.在Linq-to-Sql中,我使用了以下查询:

return context.ReleaseVersions.Where(x => x.major_version == ver[0] && x.minor_version == ver[1]
                                          && x.build_version == ver[2] && x.revision_version == ver[3])
                              .Count() > 0;
Run Code Online (Sandbox Code Playgroud)

这失败,出现以下异常:

The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities.
Run Code Online (Sandbox Code Playgroud)

编辑3:我发现了EF的不良Linq实现的另一个实例.以下是在L2S中有效但在EF 4.1中不起作用的查询:

        DateTime curDate = DateTime.Now.Date;
        var reqs = _context.TestRequests.Where(x => DateTime.Now > (curDate + x.scheduled_time.Value)).ToList();
Run Code Online (Sandbox Code Playgroud)

这会引发ArgumentException消息DbArithmeticExpression arguments must have a numeric common type.


为什么看起来他们降低了EF中Linq查询的能力而不是L2S?

Lad*_*nka 15

编辑(2012年9月2日):更新以反映.NET 4.5并添加了一些缺少的功能

这不是答案 - 它不可能是因为唯一能够回答您问题的合格人员可能是ADO.NET团队的产品经理.

如果您检查旧数据集的功能集,然后检查linq-to-sql和EF,您会发现在较新的API中删除了关键功能,因为较新的API是在更短的时间内开发的,并且需要付出巨大努力才能提供新的功能.

只列出DataSet中可用但在以后的API中不可用的一些关键功能:

  • 批量处理
  • 独特的钥匙

Linq-to-Sql中提供但EF不支持的功能(可能列表不完全正确,我很长时间没有使用过L2S):

  • 记录数据库活动
  • 延迟加载的属性
  • DefaultIfEmpty自第一个版本以来的左外连接()EF(从EFv4开始,它有它)
  • 全球渴望加载定义
  • AssociateWith - 例如急切加载数据的条件
  • 代码自第一个版本以来
  • IMultipleResults 支持返回多个结果集的存储过程(EF在.NET 4.5中有它,但没有设计器支持此功能)
  • 支持表值函数(EF在.NET 4.5中有此功能)
  • 还有一些

现在我们可以列出EF ObjectContext API(EFv4)中可用的功能以及DbContext API(EFv4.1)中缺少的功能:

  • 映射存储过程
  • 条件映射
  • 映射数据库功能
  • 定义查询,QueryViews,模型定义的函数
  • 除非您转换DbContext回,否则ESQL不可用ObjectContext
  • 除非你转换DbContext回来,否则不可能操纵独立关系的状态ObjectContext
  • 除非你转换回来,否则不可能使用MergeOption.OverwriteChangesMergeOption.PreserveChangesDbContextObjectContext
  • 还有一些

我个人对此的感觉只是一种悲伤.缺少核心功能,以及以前的API中存在的功能被删除,因为ADO.NET团队显然没有足够的资源来重新实现它们 - 这使得迁移路径在许多情况下几乎不可能.整个情况更糟,因为缺少功能或迁移障碍没有直接列出(我担心即使有人报告,ADO.NET团队也不知道它们).

因此我认为DbContext API的整体想法是管理失败.目前,ADO.NET团队必须维护两个API - DbContext取代并不成熟,ObjectContext实际上它不能,因为它只是一个包装器,因此ObjectContext不会死.可用于EF开发的资源很可能减半.

还有更多相关的问题.一旦我们离开ADO.NET团队并从MS产品套件的角度看问题,我们就会看到很多差异,我有时甚至不知道是否有任何全球战略.

简单地说,EF的提供者以不同的方式工作,而在Linq-to-sql中工作的查询不必与EF一起工作.


Dev*_*ime 7

游戏有点晚了,但是我在搜索其他内容时发现了这篇文章,并认为我会在原帖中找到基本问题的答案,其中大部分归结为"LINQ to SQL允许表达式[x] ,但EF没有".

答案是查询提供程序(将LINQ表达式树转换为实际执行并返回可枚举的东西的代码)在L2S和EF之间根本不同.要理解原因,您必须意识到L2S和EF之间的另一个根本区别在于L2S是基于表的,而EF是基于实体模型的.换句话说,EF与概念实体模型一起工作,并且知道底层物理模型(DB表)不一定反映概念实体.这是因为表被规范化/非规范化,并且具有处理实体类型泛化(继承)的奇怪方式.因此,在EF中,您可以获得概念模型的图片(这是您在VB/C#中编写的对象,等等)以及到构成概念实体的物理基础表的映射.L2S不会这样做.L2S中的每个"模型实体"都是严格意义上的单个表,正好是表字段.

到目前为止,这本身并没有真正解释原帖中的问题,但希望你可以从根本上开始意识到,EF不是L2S +或L2S v4.0.它是一种非常不同的产品(一种真正的ORM),尽管两者都使用LINQ来获取数据库数据,这是一些巧合的重叠.

另一个有趣的区别是,EF从地上爬起来,是DB无关,而L2S只能对MS SQL服务器(虽然谁是围绕L2S代码不够深嗅人会看到,有一些基础,让不同的数据块,但最终,它只与MS SQL Server绑定).这种差异对于为什么某些表达式在L2S LINQ中起作用而在EF LINQ中起作用也起着重要作用.EF的查询提供程序处理规范的DB表达式,在简单的英语中表示在几乎所有关系数据库中都具有SQL查询等价物的LINQ表达式.底层EF引擎(查询提供程序)将LINQ表达式转换为这些规范的DB表达式,然后将规范表达式树交给特定的数据库提供程序(比如Oracle或MySQL的EF提供程序),并将其转换为特定于产品的SQL.您可以在这里看到这些规范表达式应该由各个产品特定的提供者翻译:http://msdn.microsoft.com/en-us/library/ee789836.aspx

此外,EF允许一些特定于产品的DB函数(存储函数)作为表达式通过扩展.基础产品特定提供商负责提供和翻译这些提供商.

在这种情况下,EF只允许表达式是DB规范表达式或特定于存储的函数,因为树中的所有表达式都将转换为SQL以便对DB执行.

与L2S的不同之处在于,L2S将其可以从其有限的SQL生成器传递给DB的任何表达式,然后执行在返回的具体化对象集上无法转换为SQL的任何表达式.虽然这使得使用L2S看起来更简单,但你看不到的是,你的表达式的一半实际上并没有将它作为SQL传递到数据库,这会导致一些非常低效的查询带回更大的数据集然后被迭代再次在CLR内存中使用常规对象LINQ对其他表达式进行操作,而L2S无法转换为SQL.

通过使用EF将实体化数据返回到内存中的对象集,然后在内存中使用该集合上的其他LINQ语句,就可以获得与EF完全相同的效果 - 就像L2S一样,但在这种情况下,您只需要执行此操作显式,就像你说在使用非DB规范表达式之前必须调用.First().同样,在使用无法转换为SQL的其他表达式之前,可以调用.ToArray()或.ToList().

另一个很大的区别是,在EF中,实体必须全部使用.真实模型实体表示整体交易的概念对象.例如,你永远不会有半个用户.用户是一个状态取决于所有字段的对象.如果要返回部分实体或多个实体的展平连接,则必须定义投影(EF称为复杂类型),或者您可以使用某些新的4.1/4.2/4.3 POCO功能.