Select子句包含非EF方法调用

c# entity-framework linq linq-to-entities postgresql

我在构建实体框架LINQ查询时遇到问题,该查询的select子句包含对非EF对象的方法调用。

下面的代码是用于将数据从一个DBMS转换为另一个DBMS上的不同模式的应用程序的一部分。在下面的代码中,Role是我与DBMS无关的自定义类,其他类都是由我的数据库模式由Entity Framework生成的:

// set up ObjectContext's for Old and new DB schemas
var New = new NewModel.NewEntities();
var Old = new OldModel.OldEntities();

// cache all Role names and IDs in the new-schema roles table into a dictionary
var newRoles = New.roles.ToDictionary(row => row.rolename, row => row.roleid);

// create a list or Role objects where Name is name in the old DB, while
// ID is the ID corresponding to that name in the new DB
var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles.ToList();

但是调用ToList会给我这个NotSupportedException:

LINQ to Entities无法识别方法'Int32 get_Item(System.String)'方法,并且此方法无法转换为存储表达式

听起来像LINQ-to-Entities正在调用我的调用,将名称作为键输出字典。我当然不太了解EF,知道为什么这是一个问题。

我正在使用devart的dotConnect for PostgreSQL实体框架提供程序,虽然我在这一点上假设这不是DBMS特定的问题。

我知道我可以通过将查询分成两个查询来实现它,如下所示:

var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select r;
var roles2 = from r in roles.AsEnumerable()
            select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles2.ToList();

但我想知道是否有更优雅和/或更有效的方法来解决这个问题,理想情况下不会在两个查询中将其拆分。

无论如何,我的问题是两部分:

首先,我可以将此LINQ查询转换为Entity Framework将接受的内容,理想情况下不会分成两部分吗?

其次,我也很想了解一点EF,所以我可以理解为什么EF无法在数据库访问之上对我的自定义.NET代码进行分层。我的DBMS不知道如何在Dictionary类上调用一个方法,但是为什么EF在它已经从数据库中提取数据之后不能简单地进行那些Dictionary方法调用?当然,如果我想将多个EF查询组合在一起并将自定义.NET代码放在中间,我希望它会失败,但在这种情况下.NET代码只是在最后,所以为什么这是一个问题呢? EF?我假设答案是“这个功能没有进入EF 1.0”,但我正在寻找更多的解释为什么这很难证明它离开EF 1.0。

一般承认的答案

问题在于,在使用Linq的延迟执行时,您必须确定要处理的位置以及要将管道传输到客户端应用程序的数据。在第一个实例中,Linq解析表达式并将所有角色数据作为前导

New.roles.ToDictionary(row => row.rolename, row => row.roleid);

此时,数据从数据库移动到客户端并转换为您的字典。到现在为止还挺好。

问题是你的第二个Linq表达式要求Linq 使用 DB上的字典第二个DB 进行转换。换句话说,它试图找出一种方法将整个字典结构传递给DB,以便它可以选择正确的ID值作为查询延迟执行的一部分。我怀疑如果你把下半场改成了,它会解决得很好

var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select r.RoleName;
var list = roles.ToDictionary(roleName => roleName, newRoles[roleName]);

这样,它就可以解析您在DB上的选择(仅选择角色名称)作为处理ToDictionary调用的前提(它应该在客户端上执行,如您所期望的那样)。这基本上就是你在第二个例子中所做的,因为AsEnumerable在ToList调用中使用它之前将数据提取到客户端。您可以轻松地将其更改为类似的内容

var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select r;
var list = roles.AsEnumerable().Select(r => new Role { Name = r.RoleName, ID = newRoles[r.RoleName] });

它的效果也一样。对AsEnumerable()的调用解析查询,将数据拉到客户端以便在其后面的Select中使用。

请注意,我还没有对此进行过测试,但就我所理解的实体框架而言,这是我对最新动态的最佳解释。


热门答案

雅各布完全正确。如果不将它拆分为两部分,则无法转换所需的查询,因为实体框架无法将get_Item调用转换为SQL查询。
唯一的方法是编写LINQ to Entities查询,然后将LINQ to Objects查询写入其结果,就像Jacob建议的那样。
问题是特定于实体框架的问题,它不是来自我们对实体框架支持的实现。



许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因