![](/img/trans.png)
[英]EF Core 2 throws exception on SaveChanges, after first update for related entities
[英]EF Count() > 0 but First() throws exception
我遇到了一个奇怪的问题。 当用户访问我的Web应用程序的任何页面时,我都会检查用户是否有权访问它,并提供首次试用期。
这是我的代码:
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()
方法抛出异常:
序列不包含任何元素
那怎么可能呢?
编辑:我不认为用户打开两个浏览器并同时在此处导航,但是可能是并发问题吗?
您声称您仅在服务器日志中找到了它,而在调试过程中没有遇到它。 这意味着在这些行之间:
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);
也会扔。 因此,每次查询数据库时,都必须以一种可以处理不再存在的记录的方式编写代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.