Select子句包含非EF方法调用

我在构建entity frameworkLINQ查询时遇到问题,该查询的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 PostgreSQLentity framework提供程序,虽然我在这一点上假设这不是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在从DB中提取数据之后不能简单地进行那些Dictionary方法调用? 当然,如果我想将多个EF查询组合在一起并将自定义.NET代码放在中间,我希望它会失败,但在这种情况下,.NET代码只在最后,所以为什么这是一个问题呢? EF? 我假设答案是“这个function没有进入EF 1.0”,但我正在寻找更多的解释为什么这很难certificate它离开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中使用。

请注意,我还没有对此进行过测试,但就我所理解的entity framework而言,这是我对最新情况的最佳解释。

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