- Background
- 背景
We are trying to archive old user data to keep our most common tables smaller.
我们正在尝试归档旧的用户数据,以使最常用的表更小。
-
Issue
问题
Normal EF code for removing records works for our custom tables. The AspNetUsers table is a different story. It appears that the way to do it is using _userManager.Delete or _userManager.DeleteAsync. These work without trying to do multiple db calls in one transaction. When I wrap this in a transactionScope, it times out. Here is an example:
删除记录的普通EF代码适用于我们的自定义表。AspNetUsers表是另一个故事。这样做的方法似乎是使用_userManager。删除或_userManager.DeleteAsync。这些操作无需尝试在一个事务中执行多个db调用。当我将它封装到事务范围中时,它就会超时。这是一个例子:
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; } }
Note that while the code is running and I call straight to the DB like:
注意,当代码运行时,我直接调用DB,比如:
DELETE
FROM ASPNETUSERS
WHERE Id = 'X'
It will also time out. This SQL works before the the C# code is executed. Therefore, it appears that more than 1 db hit seems to lock the table. How can I find the user(db hit #1) and delete the user (db hit #2) in one transaction?
它也会超时。此SQL在执行c#代码之前工作。因此,似乎超过1db的命中似乎锁定了表。如何在一个事务中找到用户(db hit #1)并删除用户(db hit #2) ?
2 个解决方案
#1
1
Advice from microsoft is to use a different API when doing transactions with EF. This is due to the interactions between EF and the TransactionScope class. Implicitly transaction scope is forcing things up to serializable, which causes a deadlock.
来自微软的建议是在与EF进行交易时使用不同的API。这是由于EF和TransactionScope类之间的交互。隐式事务作用域将事务强制到serializable,这会导致死锁。
Good description of an EF internal API is here: MSDN Link
对一个EF内部API的很好的描述是:MSDN链接。
For reference you may need to look into user manager if it exposes the datacontext and replace your Transaction scope with using(var dbContextTransaction = context.Database.BeginTransaction()) { //code }
如果用户管理器公开了datacontext并使用(var dbContextTransaction = context.Database.BeginTransaction())
Alternatively, looking at your scenario, you are actually quite safe in finding the user ID, then trying to delete it and then just catching an error if the user has been deleted in the fraction of a second between finding it and deleting it.
或者,看看您的场景,您实际上可以很安全地找到用户ID,然后尝试删除它,如果用户在找到它和删除它之间的几秒钟内被删除,则捕获错误。
#2
0
For me, the problem involved the use of multiple separate DbContext
s within the same transaction. The BeginTransaction()
approach did not work.
对我来说,问题涉及在同一个事务中使用多个独立的dbcontext。BeginTransaction()方法不起作用。
Internally, UserManager.Delete()
is calling an async
method in a RunSync()
wrapper. Therefore, using the TransactionScopeAsyncFlowOption.Enabled
parameter for my TransactionScope
did work:
在内部,UserManager.Delete()在RunSync()包装器中调用异步方法。因此,使用TransactionScopeAsyncFlowOption。我的事务范围的启用参数确实有效:
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
_myContext1.Delete(organisation);
_myContext2.Delete(orders);
_userManager.Delete(user);
scope.Complete();
}
#1
1
Advice from microsoft is to use a different API when doing transactions with EF. This is due to the interactions between EF and the TransactionScope class. Implicitly transaction scope is forcing things up to serializable, which causes a deadlock.
来自微软的建议是在与EF进行交易时使用不同的API。这是由于EF和TransactionScope类之间的交互。隐式事务作用域将事务强制到serializable,这会导致死锁。
Good description of an EF internal API is here: MSDN Link
对一个EF内部API的很好的描述是:MSDN链接。
For reference you may need to look into user manager if it exposes the datacontext and replace your Transaction scope with using(var dbContextTransaction = context.Database.BeginTransaction()) { //code }
如果用户管理器公开了datacontext并使用(var dbContextTransaction = context.Database.BeginTransaction())
Alternatively, looking at your scenario, you are actually quite safe in finding the user ID, then trying to delete it and then just catching an error if the user has been deleted in the fraction of a second between finding it and deleting it.
或者,看看您的场景,您实际上可以很安全地找到用户ID,然后尝试删除它,如果用户在找到它和删除它之间的几秒钟内被删除,则捕获错误。
#2
0
For me, the problem involved the use of multiple separate DbContext
s within the same transaction. The BeginTransaction()
approach did not work.
对我来说,问题涉及在同一个事务中使用多个独立的dbcontext。BeginTransaction()方法不起作用。
Internally, UserManager.Delete()
is calling an async
method in a RunSync()
wrapper. Therefore, using the TransactionScopeAsyncFlowOption.Enabled
parameter for my TransactionScope
did work:
在内部,UserManager.Delete()在RunSync()包装器中调用异步方法。因此,使用TransactionScopeAsyncFlowOption。我的事务范围的启用参数确实有效:
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
_myContext1.Delete(organisation);
_myContext2.Delete(orders);
_userManager.Delete(user);
scope.Complete();
}