我有一个名为 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/