在事务范围内删除用户时超时


  • Background

我们正在尝试存档旧用户数据,以保持最常见的表更小。

  • 问题

    用于删除记录的普通 EF 代码适用于我们的自定义表。 AspNetUsers 表则另辟蹊径。 似乎这样做的方法是使用_userManager.Delete_userManager.DeleteAsync。 这些无需尝试在一个事务中执行多个数据库调用即可工作。 当我将其包装在事务范围中时,它会超时。 下面是一个示例:

    public bool DeleteByMultipleIds(List<string> idsToRemove)
    {
    try
    {
    using (var scope = new TransactionScope())
    {
    foreach (var id in idsToRemove)
    {
    var user = _userManager.FindById(id);
    //copy user data to archive table
    _userManager.Delete(user);//causes timeout
    }
    scope.Complete();
    }
    return true;
    }
    catch (TransactionAbortedException e)
    {
    Logger.Publish(e);
    return false;
    }
    catch (Exception e)
    {
    Logger.Publish(e);
    return false;
    }
    }
    

请注意,当代码运行时,我直接调用数据库,如下所示:

DELETE
FROM ASPNETUSERS
WHERE Id = 'X'

它也会超时。 此 SQL 在执行 C# 代码之前工作。 因此,似乎超过 1 db 的命中似乎锁定了表。 如何在一个事务中找到用户(db 命中 #1)并删除用户(db 命中 #2)?

对我来说,问题涉及在同一事务中使用多个单独的DbContextBeginTransaction()方法行不通。

在内部,UserManager.Delete()RunSync()包装器中调用async方法。因此,对我的TransactionScope使用TransactionScopeAsyncFlowOption.Enabled参数确实有效:

using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
_myContext1.Delete(organisation);
_myContext2.Delete(orders);
_userManager.Delete(user);
scope.Complete();
}

Microsoft 的建议是在使用 EF 执行事务时使用不同的 API。 这是由于 EF 和 TransactionScope 类之间的交互。 隐式事务范围强制事物达到可序列化状态,这会导致死锁。

EF 内部 API 的良好描述如下:MSDN 链接

作为参考,您可能需要查看用户管理器是否公开了数据上下文并将事务范围替换为using(var dbContextTransaction = context.Database.BeginTransaction()) { //code }

或者,查看您的方案,您实际上可以非常安全地找到用户 ID,然后尝试删除它,然后在找到它和删除它之间的几分之一秒内删除用户时捕获错误。

最新更新