4

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

+-------+   +-----------+   +-------+
| Users |   | UserRoles |   | Roles |
+-------+   +-----------+   +-------+
| ID (P)| < | UserID (P)|   | ID (P)|
| ...   |   | RoleID (P)| > | ...   |
+-------+   +-----------+   +-------+

我正在使用 Entitiy 框架作为我的 ORM,并且我正在尝试在单个 Linq 查询中构建我的 ViewModel,定义如下:

public class RoleDetail {
  public class RoleUser {
    public int ID { get; set; }
    public string Username { get; set; }
  }
  public int ID { get; set; }
  public string Rolename { get; set; }
  public bool Active { get; set; }
  public IEnumerable<RoleUser> Users { get; set; }
}

我认为以下查询应该有效:

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();

但是,这会引发以下错误:

LINQ to Entities 无法识别方法 'System.Collections.Generic.List [RolesRepository+RoleDetail+RoleUser] ToList[RoleUser] (System.Collections.Generic.IEnumerable[RolesRepository+RoleDetail+RoleUser])' 方法,并且此方法不能被翻译成商店表达式。

我没有经常使用 Linq,所以我很确定我的查询在某种程度上是错误的。有人可以告诉我我的查询哪里出错了,是否有更好的方法来完成我想要实现的目标?

先感谢您!

4

3 回答 3

3

对 linq-to-entities 的经验很少,所以这是在黑暗中拍摄的:

为什么需要 .ToList()?

.Select() 已经返回一个 IEnumerable,所以它应该可以在没有的情况下工作。

ToList() 返回一个 List,不是 IList 或类似接口,而是一个具体的 c# 类。所以我想 Linq to Entities 无法将其转换为自己的幕后语法

这行得通吗?

       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();
于 2012-04-28T16:15:48.117 回答
2

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

执行发生在必须实际枚举查询的位置,在您的情况下就是FirstOrDefault调用。您可以通过调用强制查询提前执行IQueryable.AsEnumerable();之后的任何内容都由 LINQ 2 Objects 处理,并且几乎可以做任何你想做的事情。

在您的情况下,您的 lambda 表达式唯一不可翻译的方面是对 的调用ToList,它没有等效的数据库,因此您不能简单地调用它;这样做的缺点是您的Users属性将被设置一些任意的内部类,这恰好是IQueryable.SelectEF 实现的结果。这对您来说可能是也可能不是问题,这取决于您定义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();
于 2012-04-28T16:34:38.253 回答
1

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

于 2012-04-28T16:18:41.533 回答