EF Count()> 0但是First()抛出异常

时间:2016-11-11 10:22:29

标签: c# entity-framework

我遇到了一个奇怪的问题。当用户访问我的网络应用程序的任何页面时 我会检查用户是否有权访问它,并在第一次出现时提供试用期。

这是我的代码:

List<string> temp_workers_id = new List<string>();
...
if (temp_workers_id.Count > 6)
                {
                    System.Data.SqlTypes.SqlDateTime sqlDate = new System.Data.SqlTypes.SqlDateTime(DateTime.Now.Date);    
var rusers = dbctx.tblMappings.Where(tm => temp_workers_id.Any(c => c == tm.ModelID));
    var permissions = dbctx.UserPermissions
       .Where(p => rusers
          .Any(ap => ap.UserID == p.UserID)
             && p.DateStart != null 
             && p.DateEnd != null 
             && p.DateStart <= sqlDate.Value 
             && p.DateEnd >= sqlDate.Value);

    if (permissions.Count() < 1)
    {
       permissions = dbctx.UserPermissions
          .Where(p => rusers
             .Any(ap => ap.UserID == p.UserID) 
                && p.DateStart == null 
                && p.DateEnd == null);

       var used = dbctx.UserPermissions
          .Where(p => rusers
             .Any(ap => ap.UserID == p.UserID) 
                && p.DateStart != null 
                && p.DateEnd != null);

    if (permissions.Count() > 0 && used.Count() < 1)
    {
       var p = permissions.First();
       using (Models.TTTDbContext tdbctx = new Models.TTTDbContext())
       {
              var tp = tdbctx.UserPermissions.SingleOrDefault(tup => tup.UserID == p.UserID);
              tp.DateStart = DateTime.Now.Date;
              tp.DateEnd = DateTime.Now.Date.AddDays(60);
              tdbctx.SaveChanges();
       }

此处First()方法抛出异常:

  

序列不包含元素

那怎么可能?

编辑: 我不认为用户打开两个浏览器并同时在这里导航,但可能是并发问题?

1 个答案:

答案 0 :(得分:1)

您声称只在服务器日志中找到了这个,并且在调试期间没有遇到它。这意味着在这些行之间:

if (permissions.Count() > 0)
{
    var p = permissions.First();

其他一些进程或线程更改了您的数据库,因此查询不再匹配任何文档。

这是由permissions持有一个延迟评估的资源引起的,这意味着查询只在您迭代它时执行(Count()First())。

所以在Count()中,执行查询:

SELECT COUNT(*) ... WHERE ...

在那一刻返回一行。然后在外部修改数据,从而导致下一个查询(First()):

SELECT n1, n2, ... WHERE ...

返回零行,导致First()抛出。

现在,如何解决这个问题,取决于您,并完全取决于您希望如何为此方案建模。这意味着第二个查询实际上是正确的:在那个时刻,没有更多行符合查询条件。您可以实现一次查询:

permissions = query.Where(...).ToList()

但这意味着你的逻辑操作陈旧的数据。如果您使用FirstOrDefault()

,也会发生同样的情况
var permissionToApply = permissions.FirstOrDefault();
if (permissionToApply != null)
{
    // rest of your logic
}

所以这基本上是一个双输的场景。你总是有可能在过时的数据上运行,这意味着下一个代码:

 tdbctx.UserPermissions.SingleOrDefault(tup => tup.UserID == p.UserID);

也会抛出。因此,每次查询数据库时,都必须编写代码,使其能够处理不再存在的记录。

相关问题