diff --git a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Controllers/TestController.cs b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Controllers/TestController.cs index c577da40..7cdf2fbb 100644 --- a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Controllers/TestController.cs +++ b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Controllers/TestController.cs @@ -16,6 +16,7 @@ using Yi.Framework.Job; using Yi.Framework.Language; using Yi.Framework.Model.Models; using Yi.Framework.Repository; +using Yi.Framework.Uow.Interceptors; using Yi.Framework.WebCore; using Yi.Framework.WebCore.AttributeExtend; using Yi.Framework.WebCore.AuthorizationPolicy; @@ -62,10 +63,10 @@ namespace Yi.Framework.ApiMicroservice.Controllers ThumbnailSharpInvoer thumbnailSharpInvoer, CacheInvoker cacheInvoker) => - (_logger,_iUserService, _iRoleService, _quartzInvoker, _hub, _local, _thumbnailSharpInvoer, _cacheDb) = + (_logger, _iUserService, _iRoleService, _quartzInvoker, _hub, _local, _thumbnailSharpInvoer, _cacheDb) = (logger, iUserService, iRoleService, quartzInvoker, hub, local, thumbnailSharpInvoer, cacheInvoker); - + /// /// swagger跳转 @@ -109,6 +110,15 @@ namespace Yi.Framework.ApiMicroservice.Controllers return Result.Success().SetData(await _iUserService.DbTest()); } + [HttpGet] + public async Task TestUnitOfWork() + { + var userId = await _iUserService.AddInfo(new DTOModel.UserInfoDto { User = new UserEntity { Address = "", UserName = "lisi", Password = "123456" }.BuildPassword() }); + throw new ApplicationException("测试uow"); + await _iRoleService._repository.InsertReturnSnowflakeIdAsync(new RoleEntity { RoleName = "测试", RoleCode = "tt" }); + return Result.Success(); + } + /// /// 执行Sql返回 /// diff --git a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Program.cs b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Program.cs index 403da255..6245fd97 100644 --- a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Program.cs +++ b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Program.cs @@ -18,6 +18,9 @@ using Yi.Framework.WebCore.LogExtend; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.AspNetCore.Mvc.Controllers; using Yi.Framework.WebCore.AutoFacExtend; +using AspectCore.Extensions.DependencyInjection; +using AspectCore.Extensions.Hosting; +using Yi.Framework.Uow.Interceptors; var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddCommandLine(args); @@ -31,6 +34,9 @@ builder.Host.ConfigureAppConfiguration((hostBuilderContext, configurationBuilder #endregion configurationBuilder.AddApolloService("Yi"); }); + +builder.Services.AddUnitOfWork(); + builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); builder.Host.ConfigureContainer(containerBuilder => { @@ -47,6 +53,7 @@ builder.Host.ConfigureContainer(containerBuilder => #endregion containerBuilder.AddAutoIocService("Yi.Framework.Repository", "Yi.Framework.Service"); }); + ////ע룬mvcģתӸioc builder.Services.Replace(ServiceDescriptor.Transient()); @@ -154,6 +161,7 @@ builder.Services.AddHttpContextAccessor(); #endregion builder.Services.AddSingleton(); + #region //ȫóʼֵ #endregion @@ -237,6 +245,5 @@ app.UseEndpoints(endpoints => endpoints.MapHub("/api/hub/main"); endpoints.MapControllers(); }); - //׼Ӷ⻧ app.Run(); \ No newline at end of file diff --git a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Yi.Framework.ApiMicroservice.csproj b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Yi.Framework.ApiMicroservice.csproj index 922abba3..947ac7b8 100644 --- a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Yi.Framework.ApiMicroservice.csproj +++ b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/Yi.Framework.ApiMicroservice.csproj @@ -21,6 +21,11 @@ + + + + + diff --git a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/yi-sqlsugar-dev.db b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/yi-sqlsugar-dev.db index 92ccd29f..fbff548e 100644 Binary files a/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/yi-sqlsugar-dev.db and b/Yi.Framework.Net6/Yi.Framework.ApiMicroservice/yi-sqlsugar-dev.db differ diff --git a/Yi.Framework.Net6/Yi.Framework.Model/UserEntity.cs b/Yi.Framework.Net6/Yi.Framework.Model/UserEntity.cs index 07d56a5a..c66544ee 100644 --- a/Yi.Framework.Net6/Yi.Framework.Model/UserEntity.cs +++ b/Yi.Framework.Net6/Yi.Framework.Model/UserEntity.cs @@ -23,7 +23,7 @@ namespace Yi.Framework.Model.Models /// /// 构建密码,MD5盐值加密 /// - public void BuildPassword(string password = null) + public UserEntity BuildPassword(string password = null) { //如果不传值,那就把自己的password当作传进来的password if (password == null) @@ -32,6 +32,7 @@ namespace Yi.Framework.Model.Models } this.Salt = Common.Helper.MD5Helper.GenerateSalt(); this.Password = Common.Helper.MD5Helper.SHA2Encode(password, this.Salt); + return this; } /// diff --git a/Yi.Framework.Net6/Yi.Framework.Uow/IUnitOfWork.cs b/Yi.Framework.Net6/Yi.Framework.Uow/IUnitOfWork.cs new file mode 100644 index 00000000..b98951b2 --- /dev/null +++ b/Yi.Framework.Net6/Yi.Framework.Uow/IUnitOfWork.cs @@ -0,0 +1,13 @@ +using System.Data; + +namespace Yi.Framework.Uow +{ + public interface IUnitOfWork : IDisposable + { + public void Init(bool isTransactional, IsolationLevel? isolationLevel, int? timeout); + public void BeginTran(); + + public void CommitTran(); + public void RollbackTran(); + } +} \ No newline at end of file diff --git a/Yi.Framework.Net6/Yi.Framework.Uow/Interceptors/UnitOfWorkAttribute.cs b/Yi.Framework.Net6/Yi.Framework.Uow/Interceptors/UnitOfWorkAttribute.cs new file mode 100644 index 00000000..3d681c2d --- /dev/null +++ b/Yi.Framework.Net6/Yi.Framework.Uow/Interceptors/UnitOfWorkAttribute.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Uow.Interceptors +{ + public class UnitOfWorkAttribute : Attribute// : AbstractInterceptorAttribute + { + public UnitOfWorkAttribute(bool isTransactional = true) + { + IsTransactional = isTransactional; + } + public UnitOfWorkAttribute(IsolationLevel isolationLevel, bool isTransactional = true) : this(isTransactional) + { + IsolationLevel = isolationLevel; + } + public UnitOfWorkAttribute(IsolationLevel isolationLevel, int timeout, bool isTransactional = true) : this(isolationLevel, isTransactional) + { + Timeout = timeout; + } + + public bool IsTransactional { get; } + + public IsolationLevel? IsolationLevel { get; } + + /// + /// Milliseconds + /// + public int? Timeout { get; } + public bool IsDisabled { get; } + + + //public override Task Invoke(AspectContext context, AspectDelegate next) + //{ + // if (IsTransactional) + // { + // ServiceLocator.in.getservice() + // } + //} + } +} diff --git a/Yi.Framework.Net6/Yi.Framework.Uow/Interceptors/UnitOfWorkInterceptor.cs b/Yi.Framework.Net6/Yi.Framework.Uow/Interceptors/UnitOfWorkInterceptor.cs new file mode 100644 index 00000000..912d2d10 --- /dev/null +++ b/Yi.Framework.Net6/Yi.Framework.Uow/Interceptors/UnitOfWorkInterceptor.cs @@ -0,0 +1,78 @@ +using Castle.DynamicProxy; +using Nest; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Uow.Interceptors +{ + public class UnitOfWorkInterceptor : IInterceptor + { + private readonly IUnitOfWork _unitOfWork; + public UnitOfWorkInterceptor(IUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public void Intercept(IInvocation invocation) + { + if (!IsUnitOfWorkMethod(invocation.Method, out var uowAttr)) + { + invocation.Proceed(); + return; + } + + try + { + _unitOfWork.BeginTran(); + //执行被拦截的方法 + invocation.Proceed(); + _unitOfWork.CommitTran(); + } + catch (Exception ex) + { + _unitOfWork.RollbackTran(); + throw ex; + } + } + + + public static bool IsUnitOfWorkMethod( MethodInfo methodInfo,out UnitOfWorkAttribute unitOfWorkAttribute) + { + + //Method declaration + var attrs = methodInfo.GetCustomAttributes(true).OfType().ToArray(); + if (attrs.Any()) + { + unitOfWorkAttribute = attrs.First(); + return !unitOfWorkAttribute.IsDisabled; + } + + if (methodInfo.DeclaringType != null) + { + //Class declaration + attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType().ToArray(); + if (attrs.Any()) + { + unitOfWorkAttribute = attrs.First(); + return !unitOfWorkAttribute.IsDisabled; + } + + ////Conventional classes + //if (typeof(IUnitOfWorkEnabled).GetTypeInfo().IsAssignableFrom(methodInfo.DeclaringType)) + //{ + // unitOfWorkAttribute = null; + // return true; + //} + } + + unitOfWorkAttribute = null; + return false; + } + + } +} diff --git a/Yi.Framework.Net6/Yi.Framework.Uow/Microsoft/ApsNetCore/Extensions/UowIServiceCollectionExtensions.cs b/Yi.Framework.Net6/Yi.Framework.Uow/Microsoft/ApsNetCore/Extensions/UowIServiceCollectionExtensions.cs new file mode 100644 index 00000000..bd424fdc --- /dev/null +++ b/Yi.Framework.Net6/Yi.Framework.Uow/Microsoft/ApsNetCore/Extensions/UowIServiceCollectionExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Uow; +using Yi.Framework.Uow.Interceptors; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class UowIServiceCollectionExtensions + { + public static void AddUnitOfWork(this IServiceCollection services) + { + services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork)); + + services.AddSingleton(); + } + } +} diff --git a/Yi.Framework.Net6/Yi.Framework.Uow/UnitOfWork.cs b/Yi.Framework.Net6/Yi.Framework.Uow/UnitOfWork.cs new file mode 100644 index 00000000..84c74df1 --- /dev/null +++ b/Yi.Framework.Net6/Yi.Framework.Uow/UnitOfWork.cs @@ -0,0 +1,75 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Uow +{ + public class UnitOfWork : IUnitOfWork + { + + public bool IsTransactional { get; protected set; } + + public IsolationLevel? IsolationLevel { get; protected set; } + + /// + /// Milliseconds + /// + public int? Timeout { get; protected set; } + + public void Init(bool isTransactional, IsolationLevel? isolationLevel, int? timeout) + { + IsTransactional = isTransactional; + IsolationLevel = isolationLevel; + Timeout = timeout; + } + + public ISqlSugarClient SugarClient { get; set; } + /// + /// 因为sqlsugarclient的生命周期是作用域的,也就是说一个请求线程内是共用一个client,暂时先直接注入 + /// + /// + public UnitOfWork(ISqlSugarClient sqlSugarClient) + { + this.SugarClient = sqlSugarClient; + } + + + public void Dispose() + { + SugarClient?.Dispose(); + SugarClient?.Close(); + } + + + + public void BeginTran() + { + if (IsTransactional) + { + if (IsolationLevel.HasValue) + { + SugarClient.Ado.BeginTran(IsolationLevel.Value); + } + else + { + SugarClient.Ado.BeginTran(); + } + } + } + + public void CommitTran() + { + if (IsTransactional) + SugarClient.Ado.CommitTran(); + } + public void RollbackTran() + { + if (IsTransactional) + SugarClient.Ado.RollbackTran(); + } + } +} diff --git a/Yi.Framework.Net6/Yi.Framework.Uow/Yi.Framework.Uow.csproj b/Yi.Framework.Net6/Yi.Framework.Uow/Yi.Framework.Uow.csproj new file mode 100644 index 00000000..8ac127cb --- /dev/null +++ b/Yi.Framework.Net6/Yi.Framework.Uow/Yi.Framework.Uow.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/Yi.Framework.Net6/Yi.Framework.WebCore/AutoFacExtend/CustomAutofacModule.cs b/Yi.Framework.Net6/Yi.Framework.WebCore/AutoFacExtend/CustomAutofacModule.cs index 4c6c0a85..66f6c59b 100644 --- a/Yi.Framework.Net6/Yi.Framework.WebCore/AutoFacExtend/CustomAutofacModule.cs +++ b/Yi.Framework.Net6/Yi.Framework.WebCore/AutoFacExtend/CustomAutofacModule.cs @@ -15,6 +15,7 @@ using Yi.Framework.Interface; using Yi.Framework.Job; using Yi.Framework.Repository; using Yi.Framework.Service; +using Yi.Framework.Uow.Interceptors; using Yi.Framework.WebCore.AutoFacExtend; using Module = Autofac.Module; @@ -48,7 +49,8 @@ namespace Yi.Framework.WebCore.AutoFacExtend containerBuilder.RegisterAssemblyTypes(assemblysServices) .AsImplementedInterfaces() .InstancePerLifetimeScope() - .EnableInterfaceInterceptors(); + .EnableInterfaceInterceptors() + .InterceptedBy(typeof(UnitOfWorkInterceptor)); ///反射注册任务调度层 var assemblysJob = GetDll("Yi.Framework.Job.dll"); diff --git a/Yi.Framework.Net6/Yi.Framework.WebCore/MiddlewareExtend/AutoIocExtend.cs b/Yi.Framework.Net6/Yi.Framework.WebCore/MiddlewareExtend/AutoIocExtend.cs index 2396c09d..aefe4f96 100644 --- a/Yi.Framework.Net6/Yi.Framework.WebCore/MiddlewareExtend/AutoIocExtend.cs +++ b/Yi.Framework.Net6/Yi.Framework.WebCore/MiddlewareExtend/AutoIocExtend.cs @@ -1,5 +1,6 @@  using Autofac; +using Autofac.Extras.DynamicProxy; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using System; @@ -9,6 +10,7 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; using Yi.Framework.Common.Attribute; +using Yi.Framework.Uow.Interceptors; namespace Yi.Framework.WebCore.MiddlewareExtend { @@ -44,41 +46,41 @@ namespace Yi.Framework.WebCore.MiddlewareExtend case LifeTime.Singleton: if (type.IsGenericType) { - build.RegisterGeneric(type).As(serviceType).SingleInstance(); + build.RegisterGeneric(type).As(serviceType).SingleInstance().EnableInterfaceInterceptors(); } else { - build.RegisterType(type).As(serviceType).SingleInstance(); + build.RegisterType(type).As(serviceType).SingleInstance().EnableInterfaceInterceptors(); } break; case LifeTime.Scoped: if (type.IsGenericType) { - build.RegisterGeneric(type).As(serviceType).InstancePerLifetimeScope(); + build.RegisterGeneric(type).As(serviceType).InstancePerLifetimeScope().EnableInterfaceInterceptors(); } else { - build.RegisterType(type).As(serviceType).InstancePerLifetimeScope(); + build.RegisterType(type).As(serviceType).InstancePerLifetimeScope().EnableInterfaceInterceptors(); } break; case LifeTime.Transient: if (type.IsGenericType) { - build.RegisterGeneric(type).As(serviceType).InstancePerDependency(); + build.RegisterGeneric(type).As(serviceType).InstancePerDependency().EnableInterfaceInterceptors(); } else { - build.RegisterType(type).As(serviceType).InstancePerDependency(); + build.RegisterType(type).As(serviceType).InstancePerDependency().EnableInterfaceInterceptors(); } break; default: if (type.IsGenericType) { - build.RegisterGeneric(type).As(serviceType).InstancePerDependency(); + build.RegisterGeneric(type).As(serviceType).InstancePerDependency().EnableInterfaceInterceptors(); } else { - build.RegisterType(type).As(serviceType).InstancePerDependency(); + build.RegisterType(type).As(serviceType).InstancePerDependency().EnableInterfaceInterceptors(); } break; } diff --git a/Yi.Framework.Net6/Yi.Framework.WebCore/Yi.Framework.WebCore.csproj b/Yi.Framework.Net6/Yi.Framework.WebCore/Yi.Framework.WebCore.csproj index a24eeae2..79f8dded 100644 --- a/Yi.Framework.Net6/Yi.Framework.WebCore/Yi.Framework.WebCore.csproj +++ b/Yi.Framework.Net6/Yi.Framework.WebCore/Yi.Framework.WebCore.csproj @@ -36,6 +36,7 @@ + diff --git a/Yi.Framework.Net6/Yi.Framework.sln b/Yi.Framework.Net6/Yi.Framework.sln index 6ae99eb4..7d1d0180 100644 --- a/Yi.Framework.Net6/Yi.Framework.sln +++ b/Yi.Framework.Net6/Yi.Framework.sln @@ -47,7 +47,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.WeChatPay", "Y EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Template", "Yi.Framework.Template\Yi.Framework.Template.csproj", "{A51E9091-3745-461A-A3CB-32598BF0DC77}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.XUnitTest", "Yi.Framework.XUnitTest\Yi.Framework.XUnitTest.csproj", "{88E3298A-135D-4D9C-B98D-41A2C4268385}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.XUnitTest", "Yi.Framework.XUnitTest\Yi.Framework.XUnitTest.csproj", "{88E3298A-135D-4D9C-B98D-41A2C4268385}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.Uow", "Yi.Framework.Uow\Yi.Framework.Uow.csproj", "{657E4EA0-5A34-4D09-A39C-419C31E740FE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -127,6 +129,10 @@ Global {88E3298A-135D-4D9C-B98D-41A2C4268385}.Debug|Any CPU.Build.0 = Debug|Any CPU {88E3298A-135D-4D9C-B98D-41A2C4268385}.Release|Any CPU.ActiveCfg = Release|Any CPU {88E3298A-135D-4D9C-B98D-41A2C4268385}.Release|Any CPU.Build.0 = Release|Any CPU + {657E4EA0-5A34-4D09-A39C-419C31E740FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {657E4EA0-5A34-4D09-A39C-419C31E740FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {657E4EA0-5A34-4D09-A39C-419C31E740FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {657E4EA0-5A34-4D09-A39C-419C31E740FE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -150,6 +156,7 @@ Global {C307189D-C42C-4C09-BB65-5A386C9F182B} = {9ABAF6B1-6C02-498A-90A2-ABC1140CF89A} {A51E9091-3745-461A-A3CB-32598BF0DC77} = {9ABAF6B1-6C02-498A-90A2-ABC1140CF89A} {88E3298A-135D-4D9C-B98D-41A2C4268385} = {C90E38FB-69EA-4997-8B3A-2C71EFA65B2B} + {657E4EA0-5A34-4D09-A39C-419C31E740FE} = {DB2506F5-05FD-4E76-940E-41D7AA148550} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1ED77A6E-377F-4EEF-A3D0-D65C94657DAF}