修改并行上传
This commit is contained in:
1 parent
9cf69dee5d
commit
eb107db8c5
5 files changed
+194
-76
No files matched your search
@@ -24,41 +24,57 @@ namespace CloudBuilder.Core.DatabaseAccessor
|
||||
[检查异常]
|
||||
//Controller对象不抛出异常,返回的是异常对象ValidatedException、ServiceErrorException(方便前端信息统一处理)
|
||||
[提交或回滚事务]
|
||||
|
||||
|
||||
注意(并发问题修复):
|
||||
1) ActionFilter 的实例是「每个 Action 一份、被所有并发请求共用」的,不是每请求一个。
|
||||
原来把 TransactionScope 存在实例字段 _transactionScope 上,两个请求重叠时:
|
||||
A 请求刚写下自己的 scope,B 请求立刻把它覆盖成自己的 scope(在另一个线程上创建),
|
||||
A 请求结束时 Dispose 的其实是 B 的 scope,于是抛
|
||||
"A TransactionScope must be disposed on the same thread that it was created."
|
||||
而且还会把别人的事务提前提交/回滚。现在改为按请求存放在 HttpContext.Items 里。
|
||||
2) 创建时必须带 TransactionScopeAsyncFlowOption.Enabled,否则事务与创建线程强绑定,
|
||||
Action 里 await 之后续体落到别的线程池线程,Dispose 同样会抛上面那条异常。
|
||||
提交/回滚的判断逻辑(ValidatedException / ServiceErrorException → 回滚)保持原样未变。
|
||||
*/
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class UnitOfWorkAttribute : ActionFilterAttribute
|
||||
{
|
||||
private TransactionScope _transactionScope;
|
||||
/// <summary>
|
||||
/// 事务对象在 HttpContext.Items 里的键。
|
||||
/// 用 HttpContext.Items 而不是实例字段:过滤器实例被所有并发请求共用,
|
||||
/// 实例字段会让并发请求互相覆盖(详见类注释)。
|
||||
/// </summary>
|
||||
private const string ScopeKey = "__UnitOfWork_TransactionScope__";
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
_transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew,
|
||||
// 同一请求内重复进入(嵌套)时复用外层事务,不重复开启
|
||||
if (context.HttpContext.Items.ContainsKey(ScopeKey))
|
||||
{
|
||||
base.OnActionExecuting(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// TransactionScopeAsyncFlowOption.Enabled:允许事务随 async/线程流转,
|
||||
// 否则 Dispose 可能发生在另一个线程上而抛异常
|
||||
var scope = new TransactionScope(TransactionScopeOption.RequiresNew,
|
||||
new TransactionOptions
|
||||
{
|
||||
IsolationLevel = IsolationLevel.ReadCommitted,
|
||||
Timeout = TransactionManager.MaximumTimeout
|
||||
});
|
||||
},
|
||||
TransactionScopeAsyncFlowOption.Enabled);
|
||||
|
||||
context.HttpContext.Items[ScopeKey] = scope;
|
||||
|
||||
base.OnActionExecuting(context);
|
||||
}
|
||||
|
||||
//public override void OnActionExecuting(ActionExecutingContext context)
|
||||
//{
|
||||
// // 正确:单机数据库事务(不是分布式)
|
||||
// _transactionScope = new TransactionScope(
|
||||
// TransactionScopeOption.Required,
|
||||
// new TransactionOptions
|
||||
// {
|
||||
// IsolationLevel = IsolationLevel.ReadCommitted
|
||||
// },
|
||||
// TransactionScopeAsyncFlowOption.Enabled); // 必须加这个
|
||||
|
||||
// base.OnActionExecuting(context);
|
||||
//}
|
||||
|
||||
public override void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
if (_transactionScope == null)
|
||||
// 取不到说明本请求没有开启(或已处理),直接放行
|
||||
if (context.HttpContext.Items[ScopeKey] is not TransactionScope transactionScope)
|
||||
{
|
||||
base.OnActionExecuted(context);
|
||||
return;
|
||||
@@ -89,13 +105,14 @@ namespace CloudBuilder.Core.DatabaseAccessor
|
||||
if (!needRollback)
|
||||
{
|
||||
//必须要有 SaveChanges() 调用,否则数据只会停留在内存中,不会真正保存到数据库。
|
||||
_transactionScope.Complete();
|
||||
transactionScope.Complete();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 不 Complete() 就是自动回滚
|
||||
_transactionScope.Dispose();
|
||||
transactionScope.Dispose();
|
||||
context.HttpContext.Items.Remove(ScopeKey);
|
||||
}
|
||||
|
||||
base.OnActionExecuted(context);
|
||||
|
||||
Reference in new issue
Block a user