- 增加 UnitOfWorkManager 工作单元管理器,实现多种传播事务;#289

This commit is contained in:
28810 2020-04-24 01:47:48 +08:00
parent 2e62db563d
commit 5e15749aa8
15 changed files with 621 additions and 405 deletions

View File

@ -0,0 +1,70 @@
using FreeSql;
using FreeSql.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace aspnetcore_transaction.Controllers
{
[ApiController]
[Route("")]
public class HomeController : ControllerBase
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
[HttpGet]
//[Transactional]
virtual public object Get([FromServices] BaseRepository<Song> repoSong, [FromServices] BaseRepository<Detail> repoDetail, [FromServices] SongRepository repoSong2,
[FromServices] SongService serviceSong)
{
serviceSong.Test();
return "111";
}
}
public class SongService
{
BaseRepository<Song> _repoSong;
BaseRepository<Detail> _repoDetail;
SongRepository _repoSong2;
public SongService(BaseRepository<Song> repoSong, BaseRepository<Detail> repoDetail, SongRepository repoSong2)
{
_repoSong = repoSong;
_repoDetail = repoDetail;
_repoSong2 = repoSong2;
}
[Transactional]
public virtual void Test()
{
_repoSong.Insert(new Song());
_repoDetail.Insert(new Detail());
_repoSong2.Insert(new Song());
}
}
public class SongRepository : DefaultRepository<Song, int>
{
public SongRepository(UnitOfWorkManager uowm) : base(uowm?.Orm, uowm) { }
}
public class Song
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Title { get; set; }
}
public class Detail
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public int SongId { get; set; }
public string Title { get; set; }
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace aspnetcore_transaction
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseServiceProviderFactory(new FreeSql.DynamicProxyServiceProviderFactory());
}
}

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:35350/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"dbcontext_01": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:35351/"
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using aspnetcore_transaction.Controllers;
using FreeSql;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace aspnetcore_transaction
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
Fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\test_trans.db")
.UseAutoSyncStructure(true)
.UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText))
.UseNoneCommandParameter(true)
.Build();
}
public IConfiguration Configuration { get; }
public static IFreeSql Fsql { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IFreeSql>(Fsql);
services.AddScoped<UnitOfWorkManager>();
services.AddFreeRepository(null, typeof(Startup).Assembly);
services.AddScoped<SongService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.OutputEncoding = Encoding.GetEncoding("GB2312");
Console.InputEncoding = Encoding.GetEncoding("GB2312");
app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseEndpoints(a => a.MapControllers());
}
}
}

View File

@ -0,0 +1,51 @@
using FreeSql;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
namespace FreeSql
{
/// <summary>
/// 使用事务执行,请查看 Program.cs 代码开启动态代理
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : DynamicProxyAttribute, IActionFilter
{
public Propagation Propagation { get; set; } = Propagation.Requierd;
public IsolationLevel? IsolationLevel { get; set; }
[DynamicProxyFromServices]
UnitOfWorkManager _uowManager;
IUnitOfWork _uow;
public override Task Before(DynamicProxyBeforeArguments args) => OnBefore(_uowManager);
public override Task After(DynamicProxyAfterArguments args) => OnAfter(args.Exception);
//这里是为了 controller
public void OnActionExecuting(ActionExecutingContext context) => OnBefore(context.HttpContext.RequestServices.GetService(typeof(UnitOfWorkManager)) as UnitOfWorkManager);
public void OnActionExecuted(ActionExecutedContext context) => OnAfter(context.Exception);
Task OnBefore(UnitOfWorkManager uowm)
{
_uow = uowm.Begin(this.Propagation, this.IsolationLevel);
return Task.FromResult(false);
}
Task OnAfter(Exception ex)
{
try
{
if (ex == null) _uow.Commit();
else _uow.Rollback();
}
finally
{
_uow.Dispose();
}
return Task.FromResult(false);
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FreeSql.DynamicProxy" Version="1.2.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
</ItemGroup>
</Project>

View File

@ -35,21 +35,21 @@ namespace FreeSql
public ISelect<T1> Select<T1>() where T1 : class public ISelect<T1> Select<T1>() where T1 : class
{ {
_resolveDbContext()?.FlushCommand(); _resolveDbContext?.Invoke()?.FlushCommand();
return _originalFsql.Select<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction(false)); return _originalFsql.Select<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction(false));
} }
public ISelect<T1> Select<T1>(object dywhere) where T1 : class => Select<T1>().WhereDynamic(dywhere); public ISelect<T1> Select<T1>(object dywhere) where T1 : class => Select<T1>().WhereDynamic(dywhere);
public IDelete<T1> Delete<T1>() where T1 : class public IDelete<T1> Delete<T1>() where T1 : class
{ {
_resolveDbContext()?.FlushCommand(); _resolveDbContext?.Invoke()?.FlushCommand();
return _originalFsql.Delete<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction()); return _originalFsql.Delete<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction());
} }
public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => Delete<T1>().WhereDynamic(dywhere); public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => Delete<T1>().WhereDynamic(dywhere);
public IUpdate<T1> Update<T1>() where T1 : class public IUpdate<T1> Update<T1>() where T1 : class
{ {
var db = _resolveDbContext(); var db = _resolveDbContext?.Invoke();
db?.FlushCommand(); db?.FlushCommand();
var update = _originalFsql.Update<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction()); var update = _originalFsql.Update<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction());
if (db?.Options.NoneParameter != null) update.NoneParameter(db.Options.NoneParameter.Value); if (db?.Options.NoneParameter != null) update.NoneParameter(db.Options.NoneParameter.Value);
@ -59,7 +59,7 @@ namespace FreeSql
public IInsert<T1> Insert<T1>() where T1 : class public IInsert<T1> Insert<T1>() where T1 : class
{ {
var db = _resolveDbContext(); var db = _resolveDbContext?.Invoke();
db?.FlushCommand(); db?.FlushCommand();
var insert = _originalFsql.Insert<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction()); var insert = _originalFsql.Insert<T1>().WithTransaction(_resolveUnitOfWork()?.GetOrBeginTransaction());
if (db?.Options.NoneParameter != null) insert.NoneParameter(db.Options.NoneParameter.Value); if (db?.Options.NoneParameter != null) insert.NoneParameter(db.Options.NoneParameter.Value);

View File

@ -6,7 +6,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>YeXiangQin</Authors> <Authors>YeXiangQin</Authors>
<Description>FreeSql is the ORM in .NetCore, .NetFramework, And Xamarin. It supports Mysql, Postgresql, SqlServer, Oracle, Sqlite, Odbc, 达梦, And Access</Description> <Description>FreeSql is the ORM in .NetCore, .NetFramework, And Xamarin. It supports Mysql, Postgresql, SqlServer, Oracle, Sqlite, Odbc, 达梦, And Access</Description>
<PackageProjectUrl>https://github.com/2881099/FreeSql.DbContext</PackageProjectUrl> <PackageProjectUrl>https://github.com/2881099/FreeSql/wiki/DbContext</PackageProjectUrl>
<PackageTags>FreeSql ORM DbContext</PackageTags> <PackageTags>FreeSql ORM DbContext</PackageTags>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -227,41 +227,6 @@
<param name="asTable">分表规则,参数:旧表名;返回:新表名 https://github.com/2881099/FreeSql/wiki/Repository</param> <param name="asTable">分表规则,参数:旧表名;返回:新表名 https://github.com/2881099/FreeSql/wiki/Repository</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="T:FreeSql.RepositoryUnitOfWorkManager">
<summary>
仓储的工作单元管理器
</summary>
</member>
<member name="F:FreeSql.RepositoryUnitOfWorkManager.Propagation.Requierd">
<summary>
如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。
</summary>
</member>
<member name="F:FreeSql.RepositoryUnitOfWorkManager.Propagation.Supports">
<summary>
支持当前事务,如果没有当前事务,就以非事务方法执行。
</summary>
</member>
<member name="F:FreeSql.RepositoryUnitOfWorkManager.Propagation.Mandatory">
<summary>
使用当前事务,如果没有当前事务,就抛出异常。
</summary>
</member>
<member name="F:FreeSql.RepositoryUnitOfWorkManager.Propagation.NotSupported">
<summary>
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
</summary>
</member>
<member name="F:FreeSql.RepositoryUnitOfWorkManager.Propagation.Never">
<summary>
以非事务方式执行操作,如果当前事务存在则抛出异常。
</summary>
</member>
<member name="F:FreeSql.RepositoryUnitOfWorkManager.Propagation.Nested">
<summary>
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,就新建一个事务。
</summary>
</member>
<member name="M:FreeSql.IDataFilter`1.Enable(System.String[])"> <member name="M:FreeSql.IDataFilter`1.Enable(System.String[])">
<summary> <summary>
开启过滤器,若使用 using 则使用完后,恢复为原有状态 开启过滤器,若使用 using 则使用完后,恢复为原有状态
@ -381,6 +346,65 @@
例如20191121_214504_1 例如20191121_214504_1
</summary> </summary>
</member> </member>
<member name="T:FreeSql.UnitOfWorkManager">
<summary>
工作单元管理器
</summary>
</member>
<member name="P:FreeSql.UnitOfWorkManager.Current">
<summary>
当前的工作单元
</summary>
</member>
<member name="M:FreeSql.UnitOfWorkManager.Binding(FreeSql.IBaseRepository)">
<summary>
将仓储的事务交给我管理
</summary>
<param name="repository"></param>
</member>
<member name="M:FreeSql.UnitOfWorkManager.Begin(FreeSql.Propagation,System.Nullable{System.Data.IsolationLevel})">
<summary>
创建工作单元
</summary>
<param name="propagation">事务传播方式</param>
<param name="isolationLevel">事务隔离级别</param>
<returns></returns>
</member>
<member name="T:FreeSql.Propagation">
<summary>
事务传播方式
</summary>
</member>
<member name="F:FreeSql.Propagation.Requierd">
<summary>
如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。
</summary>
</member>
<member name="F:FreeSql.Propagation.Supports">
<summary>
支持当前事务,如果没有当前事务,就以非事务方法执行。
</summary>
</member>
<member name="F:FreeSql.Propagation.Mandatory">
<summary>
使用当前事务,如果没有当前事务,就抛出异常。
</summary>
</member>
<member name="F:FreeSql.Propagation.NotSupported">
<summary>
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
</summary>
</member>
<member name="F:FreeSql.Propagation.Never">
<summary>
以非事务方式执行操作,如果当前事务存在则抛出异常。
</summary>
</member>
<member name="F:FreeSql.Propagation.Nested">
<summary>
以嵌套事务方式执行。
</summary>
</member>
<member name="M:FreeSqlDbContextExtensions.Entity``1(FreeSql.ICodeFirst,System.Action{FreeSql.Extensions.EfCoreFluentApi.EfCoreTableFluent{``0}})"> <member name="M:FreeSqlDbContextExtensions.Entity``1(FreeSql.ICodeFirst,System.Action{FreeSql.Extensions.EfCoreFluentApi.EfCoreTableFluent{``0}})">
<summary> <summary>
EFCore 95% 相似的 FluentApi 扩展方法 EFCore 95% 相似的 FluentApi 扩展方法

View File

@ -1,201 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
namespace FreeSql
{
/// <summary>
/// 仓储的工作单元管理器
/// </summary>
public class RepositoryUnitOfWorkManager : IDisposable
{
IFreeSql _fsql;
List<IRepositoryUnitOfWork> _uows = new List<IRepositoryUnitOfWork>();
bool _isNotSupported = false;
public RepositoryUnitOfWorkManager(IFreeSql fsql)
{
_fsql = fsql ?? throw new ArgumentNullException($"{nameof(RepositoryUnitOfWorkManager)} 构造参数 {nameof(fsql)} 不能为 null");
}
~RepositoryUnitOfWorkManager() => this.Dispose();
int _disposeCounter;
public void Dispose()
{
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
try
{
Exception exception = null;
for (var a = _uows.Count - 1; a >= 0; a--)
{
try
{
if (exception == null) _uows[a].Commit();
else _uows[a].Rollback();
}
catch (Exception ex)
{
if (exception == null) exception = ex;
}
}
if (exception != null) throw exception;
}
finally
{
_uows.Clear();
GC.SuppressFinalize(this);
}
}
public enum Propagation
{
/// <summary>
/// 如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。
/// </summary>
Requierd,
/// <summary>
/// 支持当前事务,如果没有当前事务,就以非事务方法执行。
/// </summary>
Supports,
/// <summary>
/// 使用当前事务,如果没有当前事务,就抛出异常。
/// </summary>
Mandatory,
/// <summary>
/// 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
/// </summary>
NotSupported,
/// <summary>
/// 以非事务方式执行操作,如果当前事务存在则抛出异常。
/// </summary>
Never,
/// <summary>
/// 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,就新建一个事务。
/// </summary>
Nested
}
public IRepositoryUnitOfWork Begin(Propagation propagation, IsolationLevel? isolationLevel = null)
{
if (propagation == Propagation.Requierd)
{
if (_isNotSupported == false)
{
for (var a = _uows.Count - 1; a >= 0; a--)
if (_uows[a].GetOrBeginTransaction(false) != null)
return new UnitOfWorkProxy(_uows[a]);
}
var uow = new RepositoryUnitOfWork(_fsql);
if (isolationLevel != null) uow.IsolationLevel = isolationLevel.Value;
try { uow.GetOrBeginTransaction(); }
catch { uow.Dispose(); throw; }
_uows.Add(uow);
return uow;
}
if (propagation == Propagation.Supports)
{
if (_isNotSupported == false)
{
for (var a = _uows.Count - 1; a >= 0; a--)
if (_uows[a].GetOrBeginTransaction(false) != null)
return new UnitOfWorkProxy(_uows[a]);
}
return new UnitOfWorkNothing(_fsql);
}
if (propagation == Propagation.Mandatory)
{
if (_isNotSupported == false)
{
for (var a = _uows.Count - 1; a >= 0; a--)
if (_uows[a].GetOrBeginTransaction(false) != null)
return new UnitOfWorkProxy(_uows[a]);
throw new Exception("Propagation_Mandatory: 使用当前事务,如果没有当前事务,就抛出异常");
}
throw new Exception("Propagation_Mandatory: 使用当前事务如果没有当前事务就抛出异常NotSupported 事务挂起中)");
}
if (propagation == Propagation.NotSupported)
{
if (_isNotSupported == false)
{
_isNotSupported = true;
return new UnitOfWorkNothing(_fsql) { OnDispose = () => _isNotSupported = false };
}
return new UnitOfWorkNothing(_fsql);
}
if (propagation == Propagation.Never)
{
if (_isNotSupported == false)
{
for (var a = _uows.Count - 1; a >= 0; a--)
if (_uows[a].GetOrBeginTransaction(false) != null)
throw new Exception("Propagation_Never: 以非事务方式执行操作,如果当前事务存在则抛出异常");
}
return new UnitOfWorkNothing(_fsql);
}
if (propagation == Propagation.Nested)
{
var uow = new RepositoryUnitOfWork(_fsql);
if (isolationLevel != null) uow.IsolationLevel = isolationLevel.Value;
try { uow.GetOrBeginTransaction(); }
catch { uow.Dispose(); throw; }
_uows.Add(uow);
return uow;
}
throw new NotImplementedException();
}
class UnitOfWorkProxy : IRepositoryUnitOfWork
{
IRepositoryUnitOfWork _baseUow;
public UnitOfWorkProxy(IRepositoryUnitOfWork baseUow) => _baseUow = baseUow;
public IsolationLevel? IsolationLevel { get => _baseUow.IsolationLevel; set { } }
public DbContext.EntityChangeReport EntityChangeReport => _baseUow.EntityChangeReport;
public bool Enable => _baseUow.Enable;
public void Close() => _baseUow.Close();
public void Open() => _baseUow.Open();
public DbTransaction GetOrBeginTransaction(bool isCreate = true) => _baseUow.GetOrBeginTransaction(isCreate);
public void Commit() => this.Dispose();
public void Rollback() => _baseUow.Rollback();
public void Dispose() { }
public IBaseRepository<TEntity, TKey> GetRepository<TEntity, TKey>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class => _baseUow.GetRepository<TEntity, TKey>(filter);
public IBaseRepository<TEntity> GetRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class => _baseUow.GetRepository<TEntity>(filter);
public IBaseRepository<TEntity, Guid> GetGuidRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null, Func<string, string> asTable = null) where TEntity : class => _baseUow.GetGuidRepository<TEntity>(filter);
}
class UnitOfWorkNothing : IRepositoryUnitOfWork
{
internal IFreeSql _fsql;
internal Action OnDispose;
public UnitOfWorkNothing(IFreeSql fsql) => _fsql = fsql;
public IsolationLevel? IsolationLevel { get; set; }
public DbContext.EntityChangeReport EntityChangeReport { get; } = new DbContext.EntityChangeReport();
public bool Enable { get; }
public void Close() { }
public void Open() { }
public DbTransaction GetOrBeginTransaction(bool isCreate = true) => null;
public void Commit()
{
if (EntityChangeReport != null && EntityChangeReport.OnChange != null && EntityChangeReport.Report.Any() == true)
EntityChangeReport.OnChange.Invoke(EntityChangeReport.Report);
this.Dispose();
}
public void Rollback() => this.Dispose();
public void Dispose() {
EntityChangeReport?.Report.Clear();
OnDispose?.Invoke();
}
public IBaseRepository<TEntity, TKey> GetRepository<TEntity, TKey>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class => new DefaultRepository<TEntity, TKey>(_fsql, filter);
public IBaseRepository<TEntity> GetRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class => new DefaultRepository<TEntity, int>(_fsql, filter);
public IBaseRepository<TEntity, Guid> GetGuidRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null, Func<string, string> asTable = null) where TEntity : class => new GuidRepository<TEntity>(_fsql, filter, asTable);
}
}
}

View File

@ -7,11 +7,19 @@ namespace FreeSql
{ {
public DefaultRepository(IFreeSql fsql) : base(fsql, null, null) { } public DefaultRepository(IFreeSql fsql) : base(fsql, null, null) { }
public DefaultRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter) : base(fsql, filter, null) { } public DefaultRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter) : base(fsql, filter, null) { }
public DefaultRepository(IFreeSql fsql, UnitOfWorkManager uowManger) : base(uowManger?.Orm ?? fsql, null, null)
{
uowManger?.Binding(this);
}
} }
public class GuidRepository<TEntity> : BaseRepository<TEntity, Guid> where TEntity : class public class GuidRepository<TEntity> : BaseRepository<TEntity, Guid> where TEntity : class
{ {
public GuidRepository(IFreeSql fsql) : this(fsql, null, null) { } public GuidRepository(IFreeSql fsql) : this(fsql, null, null) { }
public GuidRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter, Func<string, string> asTable) : base(fsql, filter, asTable) { } public GuidRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter, Func<string, string> asTable) : base(fsql, filter, asTable) { }
public GuidRepository(IFreeSql fsql, UnitOfWorkManager uowManger) : base(uowManger?.Orm ?? fsql, null, null)
{
uowManger?.Binding(this);
}
} }
} }

View File

@ -0,0 +1,274 @@

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Threading;
namespace FreeSql
{
/// <summary>
/// 工作单元管理器
/// </summary>
public class UnitOfWorkManager : IDisposable
{
DbContextScopedFreeSql _ormScoped;
public IFreeSql Orm => _ormScoped;
List<UowInfo> _rawUows = new List<UowInfo>();
List<UowInfo> _allUows = new List<UowInfo>();
List<RepoInfo> _repos = new List<RepoInfo>();
public UnitOfWorkManager(IFreeSql fsql)
{
if (fsql == null) throw new ArgumentNullException($"{nameof(UnitOfWorkManager)} 构造参数 {nameof(fsql)} 不能为 null");
_ormScoped = DbContextScopedFreeSql.Create(fsql, null, () => this.Current);
}
#region Dispose
~UnitOfWorkManager() => this.Dispose();
int _disposeCounter;
public void Dispose()
{
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
try
{
Exception exception = null;
for (var a = _rawUows.Count - 1; a >= 0; a--)
{
try
{
if (exception == null) _rawUows[a].Uow.Commit();
else _rawUows[a].Uow.Rollback();
}
catch (Exception ex)
{
if (exception == null) exception = ex;
}
}
if (exception != null) throw exception;
}
finally
{
_rawUows.Clear();
_allUows.Clear();
_repos.Clear();
GC.SuppressFinalize(this);
}
}
#endregion
/// <summary>
/// 当前的工作单元
/// </summary>
public IUnitOfWork Current => _allUows.LastOrDefault()?.Uow;
/// <summary>
/// 将仓储的事务交给我管理
/// </summary>
/// <param name="repository"></param>
public void Binding(IBaseRepository repository)
{
var repoInfo = new RepoInfo(repository);
repository.UnitOfWork = Current;
_repos.Add(repoInfo);
}
void SetAllRepositoryUow()
{
foreach (var repo in _repos)
repo.Repository.UnitOfWork = Current ?? repo.OrginalUow;
}
/// <summary>
/// 创建工作单元
/// </summary>
/// <param name="propagation">事务传播方式</param>
/// <param name="isolationLevel">事务隔离级别</param>
/// <returns></returns>
public IUnitOfWork Begin(Propagation propagation = Propagation.Requierd, IsolationLevel? isolationLevel = null)
{
switch (propagation)
{
case Propagation.Requierd: return FindedUowCreateVirtual() ?? CreateUow(isolationLevel);
case Propagation.Supports: return FindedUowCreateVirtual() ?? CreateUowNothing(_allUows.LastOrDefault()?.IsNotSupported ?? false);
case Propagation.Mandatory: return FindedUowCreateVirtual() ?? throw new Exception("Propagation_Mandatory: 使用当前事务,如果没有当前事务,就抛出异常");
case Propagation.NotSupported: return CreateUowNothing(true);
case Propagation.Never:
var isNotSupported = _allUows.LastOrDefault()?.IsNotSupported ?? false;
if (isNotSupported == false)
{
for (var a = _rawUows.Count - 1; a >= 0; a--)
if (_rawUows[a].Uow.GetOrBeginTransaction(false) != null)
throw new Exception("Propagation_Never: 以非事务方式执行操作,如果当前事务存在则抛出异常");
}
return CreateUowNothing(isNotSupported);
case Propagation.Nested: return CreateUow(isolationLevel);
default: throw new NotImplementedException();
}
}
IUnitOfWork FindedUowCreateVirtual()
{
var isNotSupported = _allUows.LastOrDefault()?.IsNotSupported ?? false;
if (isNotSupported == false)
{
for (var a = _rawUows.Count - 1; a >= 0; a--)
if (_rawUows[a].Uow.GetOrBeginTransaction(false) != null)
{
var uow = new UnitOfWorkVirtual(_rawUows[a].Uow);
var uowInfo = new UowInfo(uow, UowInfo.UowType.Virtual, isNotSupported);
uow.OnDispose = () => _allUows.Remove(uowInfo);
_allUows.Add(uowInfo);
SetAllRepositoryUow();
return uow;
}
}
return null;
}
IUnitOfWork CreateUowNothing(bool isNotSupported)
{
var uow = new UnitOfWorkNothing(Orm);
var uowInfo = new UowInfo(uow, UowInfo.UowType.Nothing, isNotSupported);
uow.OnDispose = () => _allUows.Remove(uowInfo);
_allUows.Add(uowInfo);
SetAllRepositoryUow();
return uow;
}
IUnitOfWork CreateUow(IsolationLevel? isolationLevel)
{
var uow = new UnitOfWorkOrginal(new UnitOfWork(Orm));
var uowInfo = new UowInfo(uow, UowInfo.UowType.Orginal, false);
if (isolationLevel != null) uow.IsolationLevel = isolationLevel.Value;
try { uow.GetOrBeginTransaction(); }
catch { uow.Dispose(); throw; }
uow.OnDispose = () =>
{
_rawUows.Remove(uowInfo);
_allUows.Remove(uowInfo);
SetAllRepositoryUow();
};
_rawUows.Add(uowInfo);
_allUows.Add(uowInfo);
SetAllRepositoryUow();
return uow;
}
class RepoInfo
{
public IBaseRepository Repository;
public IUnitOfWork OrginalUow;
public RepoInfo(IBaseRepository repository)
{
this.Repository = repository;
this.OrginalUow = repository.UnitOfWork;
}
}
class UowInfo
{
public IUnitOfWork Uow;
public UowType Type;
public bool IsNotSupported;
public enum UowType { Orginal, Virtual, Nothing }
public UowInfo(IUnitOfWork uow, UowType type, bool isNotSupported)
{
this.Uow = uow;
this.Type = type;
this.IsNotSupported = isNotSupported;
}
}
class UnitOfWorkOrginal : IUnitOfWork
{
IUnitOfWork _baseUow;
internal Action OnDispose;
public UnitOfWorkOrginal(IUnitOfWork baseUow) => _baseUow = baseUow;
public IsolationLevel? IsolationLevel { get => _baseUow.IsolationLevel; set => _baseUow.IsolationLevel = value; }
public DbContext.EntityChangeReport EntityChangeReport => _baseUow.EntityChangeReport;
public bool Enable => _baseUow.Enable;
public void Close() => _baseUow.Close();
public void Open() => _baseUow.Open();
public DbTransaction GetOrBeginTransaction(bool isCreate = true) => _baseUow.GetOrBeginTransaction(isCreate);
public void Commit() => _baseUow.Commit();
public void Rollback() => _baseUow.Rollback();
public void Dispose()
{
_baseUow.Dispose();
OnDispose?.Invoke();
}
}
class UnitOfWorkVirtual : IUnitOfWork
{
IUnitOfWork _baseUow;
internal Action OnDispose;
public UnitOfWorkVirtual(IUnitOfWork baseUow) => _baseUow = baseUow;
public IsolationLevel? IsolationLevel { get => _baseUow.IsolationLevel; set { } }
public DbContext.EntityChangeReport EntityChangeReport => _baseUow.EntityChangeReport;
public bool Enable => _baseUow.Enable;
public void Close() => _baseUow.Close();
public void Open() => _baseUow.Open();
public DbTransaction GetOrBeginTransaction(bool isCreate = true) => _baseUow.GetOrBeginTransaction(isCreate);
public void Commit() { }
public void Rollback() => _baseUow.Rollback();
public void Dispose() => OnDispose?.Invoke();
}
class UnitOfWorkNothing : IUnitOfWork
{
internal IFreeSql _fsql;
internal Action OnDispose;
public UnitOfWorkNothing(IFreeSql fsql) => _fsql = fsql;
public IsolationLevel? IsolationLevel { get; set; }
public DbContext.EntityChangeReport EntityChangeReport { get; } = new DbContext.EntityChangeReport();
public bool Enable { get; }
public void Close() { }
public void Open() { }
public DbTransaction GetOrBeginTransaction(bool isCreate = true) => null;
public void Commit()
{
if (EntityChangeReport != null && EntityChangeReport.OnChange != null && EntityChangeReport.Report.Any() == true)
EntityChangeReport.OnChange.Invoke(EntityChangeReport.Report);
EntityChangeReport?.Report.Clear();
}
public void Rollback() => EntityChangeReport?.Report.Clear();
public void Dispose() => OnDispose?.Invoke();
}
}
/// <summary>
/// 事务传播方式
/// </summary>
public enum Propagation
{
/// <summary>
/// 如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。
/// </summary>
Requierd,
/// <summary>
/// 支持当前事务,如果没有当前事务,就以非事务方法执行。
/// </summary>
Supports,
/// <summary>
/// 使用当前事务,如果没有当前事务,就抛出异常。
/// </summary>
Mandatory,
/// <summary>
/// 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
/// </summary>
NotSupported,
/// <summary>
/// 以非事务方式执行操作,如果当前事务存在则抛出异常。
/// </summary>
Never,
/// <summary>
/// 以嵌套事务方式执行。
/// </summary>
Nested
}
}

View File

@ -78,6 +78,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.Linq", "
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Dameng", "Providers\FreeSql.Provider.Dameng\FreeSql.Provider.Dameng.csproj", "{E74D90E8-1CBC-4677-817B-1CA05AB97937}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Dameng", "Providers\FreeSql.Provider.Dameng\FreeSql.Provider.Dameng.csproj", "{E74D90E8-1CBC-4677-817B-1CA05AB97937}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aspnetcore_transaction", "Examples\aspnetcore_transaction\aspnetcore_transaction.csproj", "{07AB0B37-A8B1-4FB1-9259-7B804E369E36}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -472,6 +474,18 @@ Global
{E74D90E8-1CBC-4677-817B-1CA05AB97937}.Release|x64.Build.0 = Release|Any CPU {E74D90E8-1CBC-4677-817B-1CA05AB97937}.Release|x64.Build.0 = Release|Any CPU
{E74D90E8-1CBC-4677-817B-1CA05AB97937}.Release|x86.ActiveCfg = Release|Any CPU {E74D90E8-1CBC-4677-817B-1CA05AB97937}.Release|x86.ActiveCfg = Release|Any CPU
{E74D90E8-1CBC-4677-817B-1CA05AB97937}.Release|x86.Build.0 = Release|Any CPU {E74D90E8-1CBC-4677-817B-1CA05AB97937}.Release|x86.Build.0 = Release|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Debug|x64.ActiveCfg = Debug|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Debug|x64.Build.0 = Debug|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Debug|x86.ActiveCfg = Debug|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Debug|x86.Build.0 = Debug|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|Any CPU.Build.0 = Release|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|x64.ActiveCfg = Release|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|x64.Build.0 = Release|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|x86.ActiveCfg = Release|Any CPU
{07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -499,6 +513,7 @@ Global
{B397A761-F646-41CF-A160-AB6C05DAF2FB} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {B397A761-F646-41CF-A160-AB6C05DAF2FB} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
{57B3F5B0-D46A-4442-8EC6-9A9A784404B7} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} {57B3F5B0-D46A-4442-8EC6-9A9A784404B7} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA}
{E74D90E8-1CBC-4677-817B-1CA05AB97937} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {E74D90E8-1CBC-4677-817B-1CA05AB97937} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
{07AB0B37-A8B1-4FB1-9259-7B804E369E36} = {94C8A78D-AA15-47B2-A348-530CD86BFC1B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98} SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98}

View File

@ -2310,137 +2310,6 @@
<param name="parms"></param> <param name="parms"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:FreeSql.IAdo.ExecuteReaderAsync(System.Func{System.Data.Common.DbDataReader,System.Threading.Tasks.Task},System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
查询若使用读写分离查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
</summary>
<param name="readerHander"></param>
<param name="cmdType"></param>
<param name="cmdText"></param>
<param name="cmdParms"></param>
</member>
<member name="M:FreeSql.IAdo.ExecuteReaderAsync(System.Func{System.Data.Common.DbDataReader,System.Threading.Tasks.Task},System.String,System.Object)">
<summary>
查询ExecuteReaderAsync(dr => {}, "select * from user where age > ?age", new { age = 25 })
</summary>
<param name="cmdText"></param>
<param name="parms"></param>
</member>
<member name="M:FreeSql.IAdo.ExecuteArrayAsync(System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
查询
</summary>
<param name="cmdText"></param>
<param name="cmdParms"></param>
</member>
<member name="M:FreeSql.IAdo.ExecuteArrayAsync(System.String,System.Object)">
<summary>
查询ExecuteArrayAsync("select * from user where age > ?age", new { age = 25 })
</summary>
<param name="cmdText"></param>
<param name="parms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.ExecuteDataSetAsync(System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
查询
</summary>
<param name="cmdText"></param>
<param name="cmdParms"></param>
</member>
<member name="M:FreeSql.IAdo.ExecuteDataSetAsync(System.String,System.Object)">
<summary>
查询ExecuteDataSetAsync("select * from user where age > ?age; select 2", new { age = 25 })
</summary>
<param name="cmdText"></param>
<param name="parms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.ExecuteDataTableAsync(System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
查询
</summary>
<param name="cmdText"></param>
<param name="cmdParms"></param>
</member>
<member name="M:FreeSql.IAdo.ExecuteDataTableAsync(System.String,System.Object)">
<summary>
查询ExecuteDataTableAsync("select * from user where age > ?age", new { age = 25 })
</summary>
<param name="cmdText"></param>
<param name="parms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.ExecuteNonQueryAsync(System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
在【主库】执行
</summary>
<param name="cmdType"></param>
<param name="cmdText"></param>
<param name="cmdParms"></param>
</member>
<member name="M:FreeSql.IAdo.ExecuteNonQueryAsync(System.String,System.Object)">
<summary>
在【主库】执行ExecuteNonQueryAsync("delete from user where age > ?age", new { age = 25 })
</summary>
<param name="cmdText"></param>
<param name="parms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.ExecuteScalarAsync(System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
在【主库】执行
</summary>
<param name="cmdType"></param>
<param name="cmdText"></param>
<param name="cmdParms"></param>
</member>
<member name="M:FreeSql.IAdo.ExecuteScalarAsync(System.String,System.Object)">
<summary>
在【主库】执行ExecuteScalarAsync("select 1 from user where age > ?age", new { age = 25 })
</summary>
<param name="cmdText"></param>
<param name="parms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.QueryAsync``1(System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
执行SQL返回对象集合QueryAsync&lt;User&gt;("select * from user where age > ?age", new SqlParameter { ParameterName = "age", Value = 25 })
</summary>
<typeparam name="T"></typeparam>
<param name="cmdType"></param>
<param name="cmdText"></param>
<param name="cmdParms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.QueryAsync``1(System.String,System.Object)">
<summary>
执行SQL返回对象集合QueryAsync&lt;User&gt;("select * from user where age > ?age", new { age = 25 })
</summary>
<typeparam name="T"></typeparam>
<param name="cmdText"></param>
<param name="parms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.QueryAsync``2(System.Data.CommandType,System.String,System.Data.Common.DbParameter[])">
<summary>
执行SQL返回对象集合Query&lt;User&gt;("select * from user where age > ?age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 })
</summary>
<typeparam name="T1"></typeparam>
<param name="cmdType"></param>
<param name="cmdText"></param>
<param name="cmdParms"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IAdo.QueryAsync``2(System.String,System.Object)">
<summary>
执行SQL返回对象集合Query&lt;User&gt;("select * from user where age > ?age; select * from address", new { age = 25 })
</summary>
<typeparam name="T1"></typeparam>
<param name="cmdText"></param>
<param name="parms"></param>
<returns></returns>
</member>
<member name="E:FreeSql.IAop.ParseExpression"> <member name="E:FreeSql.IAop.ParseExpression">
<summary> <summary>
可自定义解析表达式 可自定义解析表达式
@ -2961,12 +2830,6 @@
<param name="timeout">超时</param> <param name="timeout">超时</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:FreeSql.Internal.ObjectPool.IObjectPool`1.GetAsync">
<summary>
获取资源
</summary>
<returns></returns>
</member>
<member name="M:FreeSql.Internal.ObjectPool.IObjectPool`1.Return(FreeSql.Internal.ObjectPool.Object{`0},System.Boolean)"> <member name="M:FreeSql.Internal.ObjectPool.IObjectPool`1.Return(FreeSql.Internal.ObjectPool.Object{`0},System.Boolean)">
<summary> <summary>
使用完毕后,归还资源 使用完毕后,归还资源
@ -3037,12 +2900,6 @@
</summary> </summary>
<param name="obj">资源对象</param> <param name="obj">资源对象</param>
</member> </member>
<member name="M:FreeSql.Internal.ObjectPool.IPolicy`1.OnGetAsync(FreeSql.Internal.ObjectPool.Object{`0})">
<summary>
从对象池获取对象成功的时候触发,通过该方法统计或初始化对象
</summary>
<param name="obj">资源对象</param>
</member>
<member name="M:FreeSql.Internal.ObjectPool.IPolicy`1.OnReturn(FreeSql.Internal.ObjectPool.Object{`0})"> <member name="M:FreeSql.Internal.ObjectPool.IPolicy`1.OnReturn(FreeSql.Internal.ObjectPool.Object{`0})">
<summary> <summary>
归还对象给对象池的时候触发 归还对象给对象池的时候触发

View File

@ -16,14 +16,14 @@ namespace FreeSql.Internal.CommonProvider
class Transaction2 class Transaction2
{ {
internal Aop.TraceBeforeEventArgs AopBefore; internal Aop.TraceBeforeEventArgs AopBefore;
internal Object<DbConnection> Conn; internal Object<DbConnection> Connection;
internal DbTransaction Transaction; internal DbTransaction Transaction;
internal DateTime RunTime; internal DateTime RunTime;
internal TimeSpan Timeout; internal TimeSpan Timeout;
public Transaction2(Object<DbConnection> conn, DbTransaction tran, TimeSpan timeout) public Transaction2(Object<DbConnection> conn, DbTransaction tran, TimeSpan timeout)
{ {
Conn = conn; Connection = conn;
Transaction = tran; Transaction = tran;
RunTime = DateTime.Now; RunTime = DateTime.Now;
Timeout = timeout; Timeout = timeout;
@ -31,7 +31,6 @@ namespace FreeSql.Internal.CommonProvider
} }
private ConcurrentDictionary<int, Transaction2> _trans = new ConcurrentDictionary<int, Transaction2>(); private ConcurrentDictionary<int, Transaction2> _trans = new ConcurrentDictionary<int, Transaction2>();
private object _trans_lock = new object();
public DbTransaction TransactionCurrentThread => _trans.TryGetValue(Thread.CurrentThread.ManagedThreadId, out var conn) && conn.Transaction?.Connection != null ? conn.Transaction : null; public DbTransaction TransactionCurrentThread => _trans.TryGetValue(Thread.CurrentThread.ManagedThreadId, out var conn) && conn.Transaction?.Connection != null ? conn.Transaction : null;
public Aop.TraceBeforeEventArgs TransactionCurrentThreadAopBefore => _trans.TryGetValue(Thread.CurrentThread.ManagedThreadId, out var conn) && conn.Transaction?.Connection != null ? conn.AopBefore : null; public Aop.TraceBeforeEventArgs TransactionCurrentThreadAopBefore => _trans.TryGetValue(Thread.CurrentThread.ManagedThreadId, out var conn) && conn.Transaction?.Connection != null ? conn.AopBefore : null;
@ -61,35 +60,27 @@ namespace FreeSql.Internal.CommonProvider
throw ex; throw ex;
} }
if (_trans.ContainsKey(tid)) CommitTransaction(); if (_trans.ContainsKey(tid)) CommitTransaction();
_trans.TryAdd(tid, tran);
lock (_trans_lock)
_trans.TryAdd(tid, tran);
} }
private void CommitTimeoutTransaction() private void CommitTimeoutTransaction()
{ {
if (_trans.Count > 0) if (_trans.Count > 0)
{ {
Transaction2[] trans = null; var trans = _trans.Values.Where(st2 => DateTime.Now.Subtract(st2.RunTime) > st2.Timeout).ToArray();
lock (_trans_lock) foreach (var tran in trans) CommitTransaction(true, tran, null, "Timeout自动提交");
trans = _trans.Values.Where(st2 => DateTime.Now.Subtract(st2.RunTime) > st2.Timeout).ToArray();
foreach (Transaction2 tran in trans) CommitTransaction(true, tran, null, "Timeout自动提交");
} }
} }
private void CommitTransaction(bool isCommit, Transaction2 tran, Exception rollbackException, string remark = null) private void CommitTransaction(bool isCommit, Transaction2 tran, Exception rollbackException, string remark = null)
{ {
if (tran == null || tran.Transaction == null || tran.Transaction.Connection == null) return; if (tran == null || tran.Transaction == null || tran.Transaction.Connection == null) return;
_trans.TryRemove(tran.Connection.LastGetThreadId, out var oldtran);
if (_trans.ContainsKey(tran.Conn.LastGetThreadId))
lock (_trans_lock)
if (_trans.ContainsKey(tran.Conn.LastGetThreadId))
_trans.TryRemove(tran.Conn.LastGetThreadId, out var oldtran);
Exception ex = null; Exception ex = null;
if (string.IsNullOrEmpty(remark)) remark = isCommit ? "提交" : "回滚"; if (string.IsNullOrEmpty(remark)) remark = isCommit ? "提交" : "回滚";
try try
{ {
Trace.WriteLine($"线程{tran.Conn.LastGetThreadId}事务{remark}"); Trace.WriteLine($"线程{tran.Connection.LastGetThreadId}事务{remark}");
if (isCommit) tran.Transaction.Commit(); if (isCommit) tran.Transaction.Commit();
else tran.Transaction.Rollback(); else tran.Transaction.Rollback();
} }
@ -100,7 +91,7 @@ namespace FreeSql.Internal.CommonProvider
} }
finally finally
{ {
ReturnConnection(MasterPool, tran.Conn, ex); //MasterPool.Return(tran.Conn, ex); ReturnConnection(MasterPool, tran.Connection, ex); //MasterPool.Return(tran.Conn, ex);
var after = new Aop.TraceAfterEventArgs(tran.AopBefore, remark, ex ?? rollbackException); var after = new Aop.TraceAfterEventArgs(tran.AopBefore, remark, ex ?? rollbackException);
_util?._orm?.Aop.TraceAfterHandler?.Invoke(this, after); _util?._orm?.Aop.TraceAfterHandler?.Invoke(this, after);
@ -141,10 +132,8 @@ namespace FreeSql.Internal.CommonProvider
if (Interlocked.Increment(ref _disposeCounter) != 1) return; if (Interlocked.Increment(ref _disposeCounter) != 1) return;
try try
{ {
Transaction2[] trans = null; var trans = _trans.Values.ToArray();
lock (_trans_lock) foreach (var tran in trans) CommitTransaction(false, tran, null, "Dispose自动提交");
trans = _trans.Values.ToArray();
foreach (Transaction2 tran in trans) CommitTransaction(false, tran, null, "Dispose自动提交");
} }
catch { } catch { }