C# 返回复杂类型的Linq查询

C# 返回复杂类型的Linq查询,c#,asp.net-mvc,linq,entity-framework,C#,Asp.net Mvc,Linq,Entity Framework,我有一个非常标准的用户和角色模型: +-------+ +-----------+ +-------+ | Users | | UserRoles | | Roles | +-------+ +-----------+ +-------+ | ID (P)| < | UserID (P)| | ID (P)| | ... | | RoleID (P)| > | ... | +-------+ +-----------+ +-------

我有一个非常标准的用户和角色模型:

+-------+   +-----------+   +-------+
| Users |   | UserRoles |   | Roles |
+-------+   +-----------+   +-------+
| ID (P)| < | UserID (P)|   | ID (P)|
| ...   |   | RoleID (P)| > | ...   |
+-------+   +-----------+   +-------+
但是,这会引发以下错误:

LINQ to实体无法识别该方法 'System.Collections.Generic.List[RolesRepository+roledeail+RoleUser] 托利斯特[罗莱瑟] System.Collections.Generic.IEnumerable[RolesRepository+roledeail+RoleUser]' 方法,而此方法无法转换为存储表达式

我没有和Linq一起工作过很多次,所以我很确定我的查询在某种程度上是错误的。有人能告诉我我的问题出在哪里,有没有更好的方法来满足我的目标


提前谢谢你

对linq to实体几乎没有经验,因此这是一个黑暗中的拍摄:

你为什么需要,托利斯特

.Select已返回IEnumerable,因此它应在不使用IEnumerable的情况下工作

ToList返回一个列表,不是IList或类似的接口,而是一个具体的c类。所以我认为LINQtoEntities无法将其转换为自己的幕后语法

这行吗

       var query = Context.Roles
               .Include("Users")
               .Where(r => r.ID == id)
               .Select(r => new RoleDetail() {
                  ID = r.ID,
                  Rolename = r.Rolename,
                  Active = r.Active,
                  Users = r.Users
                           .Select(u => new RoleDetail.RoleUser() {
                              ID = u.ID,
                              Username = u.Username
                            })
                            // .ToList()
                })
               .FirstOrDefault();

看起来它试图在数据库中执行ToList,但不知道如何执行。我会尝试将其更改为ToArray,或者失败,只是将其保留为IEnumerable。

发送到EF LINQ提供程序的LINQ查询中的所有内容都需要是L2E可以直接转换为SQL的表达式。由您手动将EF可以转换的部分与必须在LINQ 2对象中完成的部分分开

执行发生在必须实际枚举查询的点上,在您的情况下,这是第一个ORDEFAULT调用。您可以通过调用IQueryable.AsEnumerable强制提前执行查询;之后的任何操作都由LINQ2对象处理,并且几乎可以做任何您想做的事情

在您的例子中,lambda表达式唯一不可翻译的方面是对ToList的调用,它没有数据库等价物,因此您不能简单地调用它;这样做的缺点是,您的Users属性将被设置为某个任意的内部类,该类恰好是EF实现中IQueryable.Select的结果。这对您来说可能是问题,也可能不是问题,这取决于您对RoleDetail.Users的定义以及您对它所做的操作

当您必须处理EF不喜欢的表达式时,另一种更普遍适用的解决方案是尽早插入AsEnumerable调用:

var query = Context.Roles
           .Include("Users")
           .Where(r => r.ID == id)
           .AsEnumerable()
           .Select(r => new RoleDetail() {
              ID = r.ID,
              Rolename = r.Rolename,
              Active = r.Active,
              Users = r.Users
                       .Select(u => new RoleDetail.RoleUser() {
                          ID = u.ID,
                          Username = u.Username
                        })
                        // .ToList()
            })
           .FirstOrDefault();

如果删除.ToList会发生什么?这就是问题所在。谢谢大家留下答案!去掉它就成功了-谢谢!我一直在使用.ToList,因为在视图可以迭代集合之前,通过存储库构造函数/析构函数基类实例化/释放的Entities类会被释放。这可能是另一回事,但托利斯特似乎正常工作。我根据第一个答案接受了,尽管所有答案都很有帮助。非常好的解释。因此,一旦调用.AsEnumerable,查询将处理实际的实体对象,而不是尝试创建一个db友好的SQL查询?这对我来说是有道理的。EF LINQ序列操作符返回IQueryable对象;它们只包含一个尚未编译且永远不会编译的表达式,它将用于生成SQL查询。调用不返回IQueryable的方法后,EF必须解析表达式、执行SQL并生成对象/序列。一旦这样做,EF就不存在了。这可能解释了为什么我的控制器将IQueryable对象返回到我的视图时抛出了一个错误-db上下文超出了范围,实际的SQL还没有执行?是的,这也是为什么需要Include调用;只要上下文在范围内,它就会延迟加载您使用的任何导航属性,但一旦该上下文被释放,您尚未检索到的任何数据就不再可用。
       var query = Context.Roles
               .Include("Users")
               .Where(r => r.ID == id)
               .Select(r => new RoleDetail() {
                  ID = r.ID,
                  Rolename = r.Rolename,
                  Active = r.Active,
                  Users = r.Users
                           .Select(u => new RoleDetail.RoleUser() {
                              ID = u.ID,
                              Username = u.Username
                            })
                            // .ToList()
                })
               .FirstOrDefault();
var query = Context.Roles
           .Include("Users")
           .Where(r => r.ID == id)
           .AsEnumerable()
           .Select(r => new RoleDetail() {
              ID = r.ID,
              Rolename = r.Rolename,
              Active = r.Active,
              Users = r.Users
                       .Select(u => new RoleDetail.RoleUser() {
                          ID = u.ID,
                          Username = u.Username
                        })
                        // .ToList()
            })
           .FirstOrDefault();