3

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

我在 .NET 4.5 中使用实体框架 5。

以下代码在第 1 行产生错误“无法创建类型为 'User' 的常量值” var target = _context.Orders.FirstOrDefault...

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不存在的问题?有没有办法获得我想要的结果并将所有逻辑保留在一个查询中?

谢谢!

4

1 回答 1

4

我认为您实际上在实体框架中发现了一个错误。我好羡慕!

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

这就是我认为这是一个错误的原因:翻译表达式谓词的机器肯定是行为不端,FirstOrDefaultWhere. 我可以使用这些人为的查询针对类似的架构重现您的问题:

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

就好像 for 的翻译器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,以避免需要像这样的复杂查询。)

于 2013-09-04T23:22:28.603 回答