“无法创建类型为'User'的常量值"使用FirstOrDefault时? [英] "Unable to create a constant value of type 'User'" when using FirstOrDefault?

查看:118
本文介绍了“无法创建类型为'User'的常量值"使用FirstOrDefault时?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a method called UpdateOrders that is supposed to allow users to only edit orders that they own, unless the user is an administrator, in which case they can edit all orders.

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

I'm using Entity Framework 5 in .NET 4.5.

下面的代码在行var target = _context.Orders.FirstOrDefault...处产生错误无法创建类型为'用户'的常量".

The following code produces the error "Unable to create a constant value of type 'User'" at the line 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设置为变量,并将该变量放入代码中,并且可以正常运行,但这意味着对数据库的影响更大.

I can remove _context.Users.FirstOrDefault(u => u.UserName == userName).Role.RoleName to a separate line that sets the RoleName to a variable and put the variable in the code and it runs fine, but it means one more hit to the database.

如果我使用_context.Orders.Where不会产生错误,并在单个查询中执行所有逻辑.因此,这段代码实际上可以正常工作:

If I use _context.Orders.Where doesn't produce the error and performs all of the logic in a single query. So this code actually works just fine:

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没有的问题?有没有一种方法可以得到我想要的结果并将所有逻辑保存在单个查询中?

Why is FirstOrDefault having problems that Where does not? Is there a way to get my desired result and keep all of the logic in a single query?

谢谢!

推荐答案

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

I think you've actually found a bug in Entity Framework. I'm envious!

(看来此错误已在EF 6.0.0-rc1中修复)

这就是为什么我认为这是一个错误:翻译表达式谓词的机器肯定对于FirstOrDefault行为不佳,而Where则不会发生这种情况.我可以使用这些人为的查询针对类似的模式来重现您的问题:

Here's why I think it's a bug: the machinery which is translating the expression predicate is definitely misbehaving for FirstOrDefault in a way that does not occur with Where. I can repro your issue against a similar schema with these contrived queries:

// 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无法识别该方法"异常:

Not only does it throw an exception, but observing the SQL in LINQPad you can see that an effective SELECT * FROM Users query is being issued right before it blows up, as if it's trying to treat the _context.Users.FirstOrDefault() call as the IEnumerable version rather than the IQueryable version. Using an unmapped method as a predicate in the inner FirstOrDefault call results in the same "constant value" exception, rather than the "LINQ to Entities does not recognize the method" exception one would expect:

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

好像FirstOrDefault的转换器正在通过某种方式执行表达式,但在完成还原之前(内部FirstOrDefault进行了评估),它会意识到将不允许使用结果类型并且将其炸掉.很奇怪.

It's as if the translator for FirstOrDefault is aggressively trying to reduce an expression of type User down to an instance of type User by executing the expression somehow, but before the reduction is complete (before the inner FirstOrDefault is evaluated) it realizes that the resulting type is not going to be allowed and blows up. Very strange.

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

I briefly tried to figure out what's going wrong in the source, but it's way beyond me. I'd recommend filing a bug with the EF guys: http://entityframework.codeplex.com/workitem/list/basic

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

In the meantime, it seems to work correctly as long as you put the predicate in the Where clause rather than the FirstOrDefault, even if you tag FirstOrDefault onto the end of your query:

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上存储用户具有管理员"角色这一事实,以避免需要类似这样的复杂查询.)

(You might also consider storing the fact that the user has the "Administrator" role on the User, to avoid the need for complex queries like this.)

这篇关于“无法创建类型为'User'的常量值"使用FirstOrDefault时?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆