linq-to-entities - "Unable to create a constant value of type ' 使用 FirstOrDefault 时用户 '"?

标签 linq-to-entities entity-framework-5

我有一个名为 UpdateOrders 的方法,它应该允许用户只编辑他们拥有的订单,除非用户是管理员,在这种情况下他们可以编辑所有订单。

我在 .NET 4.5 中使用 Entity Framework 5。

以下代码在行 var target = _context.Orders.FirstOrDefault... 处产生错误“无法创建‘User’类型的常量值”。

public Order UpdateOrders(Order order)
{
    var userName = HttpContext.Current.User.Identity.Name.ToLower();

    var target = _context.Orders.FirstOrDefault(o => o.OrderID == order.OrderID
        && (o.User.UserName.ToLower() == userName || _context.Users.FirstOrDefault(u => u.UserName == userName).Role.RoleName == "Administrator"));

    if (target != null)
    {
        _context.Entry(target).CurrentValues.SetValues(order);
        _context.SaveChanges();
    }

    return target;
}

我可以将 _context.Users.FirstOrDefault(u => u.UserName == userName).Role.RoleName 删除到一个单独的行,将 RoleName 设置为一个变量并将该变量放在代码并运行良好,但这意味着对数据库又一次访问。

如果我使用 _context.Orders.Where 不会产生错误并在单个查询中执行所有逻辑。所以这段代码实际上工作得很好:

public void TestMethod(Order order)
{
    var userName = HttpContext.Current.User.Identity.Name.ToLower();

    var target = _context.Orders.Where(o => o.OrderID == order.OrderID
        && (o.User.UserName.ToLower() == userName || _context.Users.FirstOrDefault(u => u.UserName == userName).Role.RoleName == "Administrator")).ToList();

    return;
}

为什么 FirstOrDefault 有问题而 Where 没有?有没有办法获得我想要的结果并将所有逻辑保留在单个查询中?

谢谢!

最佳答案

我认为您实际上已经在 Entity Framework 中发现了一个错误。我很羡慕!

(编辑:看起来这个错误已在 EF 6.0.0-rc1 中修复)

这就是我认为这是一个错误的原因:翻译表达式谓词的机制肯定对 FirstOrDefault 行为不当,而对于 Where 则不会发生。我可以使用这些人为的查询针对类似模式重现您的问题:

// works
var target = _context.Orders.Where (o => _context.Users.FirstOrDefault() != null).FirstOrDefault ();

// Exception: Unable to create a constant value...
target = _context.Orders.FirstOrDefault (o => _context.Users.FirstOrDefault() != null);

它不仅抛出异常,而且观察 LINQPad 中的 SQL 你可以看到在它崩溃之前发出了一个有效的 SELECT * FROM Users 查询,就好像它试图处理_context.Users.FirstOrDefault() 调用作为 IEnumerable 版本而不是 IQueryable 版本。在内部 FirstOrDefault 调用中使用未映射的方法作为谓词会导致相同的“常量值”异常,而不是预期的“LINQ to Entities 无法识别该方法”异常:

// throws "Unable to create a constant value..." exception!
target = _context.Orders.FirstOrDefault (o => _context.Users.FirstOrDefault(u => MyBogusMethod(u)) != null);

就好像 FirstOrDefault 的翻译器正在积极地尝试通过执行将 User 类型的表达式简化为 User 类型的实例表达式不知何故,但在缩减完成之前(在评估内部 FirstOrDefault 之前)它意识到不会允许生成的类型并爆炸。很奇怪。

我曾短暂地试图找出源代码中出了什么问题,但这超出了我的范围。 我建议向 EF 人员提交错误:http://entityframework.codeplex.com/workitem/list/basic

与此同时,只要您将谓词放在 Where 子句而不是 FirstOrDefault 中,它似乎就可以正常工作,即使您标记了 FirstOrDefault 到查询的末尾:

var target = _context.Orders.Where(o => 
              o.OrderID == order.OrderID
              && (o.User.UserName.ToLower() == userName 
                  || _context.Users.FirstOrDefault(u => 
                       u.UserName == userName).Role.RoleName == "Administrator")
              )
              .FirstOrDefault();

(您还可以考虑存储用户在 User 上具有“管理员”角色的事实,以避免需要像这样的复杂查询。)

关于linq-to-entities - "Unable to create a constant value of type ' 使用 FirstOrDefault 时用户 '"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18623498/

相关文章:

c# - 修改edmx模板文件

c# - 为什么 LINQ-to-Entities 将此查询放在子选择中?

.net - Entity Framework 函数导入参数类型在从数据库更新模型时重置

c# - EF 5 代码迁移错误 : "There is already an object named _____ in the database"

c# - Entity Framework (.NET 完整框架)订购包括

.net - Linq SqlMethods.Like 失败

c# - Linq:方法无法转换为存储表达式

entity-framework - 如何编写需要子查询的 Linq 查询?

ef-code-first - 非静态方法需要一个目标。 Entity Framework 5 代码优先

c# - 获取具有条件的实体对象及其子实体(使用 Dynamic Linq)