diff --git a/IRC.Core.SCP/HostConfig/EFSetup.cs b/IRC.Core.SCP/HostConfig/EFSetup.cs index 00ee49222..73b279ffc 100644 --- a/IRC.Core.SCP/HostConfig/EFSetup.cs +++ b/IRC.Core.SCP/HostConfig/EFSetup.cs @@ -4,6 +4,7 @@ using IRaCIS.Core.Infra.EFCore; using Medallion.Threading; using Medallion.Threading.SqlServer; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -15,11 +16,12 @@ namespace IRaCIS.Core.SCP { services.AddHttpContextAccessor(); services.AddScoped(); + services.AddScoped(); + - //这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext //Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点 - services.AddDbContextPool(options => + services.AddDbContextPool((sp, options) => { // 在控制台 //public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); @@ -35,6 +37,7 @@ namespace IRaCIS.Core.SCP options.EnableSensitiveDataLogging(); options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); + options.AddInterceptors(sp.GetServices()); options.UseProjectables(); diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs index 915ff6018..04d8ef595 100644 --- a/IRaCIS.Core.API/Progranm.cs +++ b/IRaCIS.Core.API/Progranm.cs @@ -29,6 +29,10 @@ using IRaCIS.Core.Application.Service.ImageAndDoc; using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; using IRaCIS.Core.Application.BusinessFilter; +using Microsoft.AspNetCore.Http; +using IRaCIS.Core.Infrastructure.Extention; +using Newtonsoft.Json; +using Microsoft.AspNetCore.Diagnostics; #region 获取环境变量 @@ -190,6 +194,52 @@ var env = app.Environment; // Configure the HTTP request pipeline. +#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码 +//app.UseStatusCodePagesWithReExecute("/Error/{0}"); + +app.UseStatusCodePages(async context => +{ + var code = context.HttpContext.Response.StatusCode; + context.HttpContext.Response.ContentType = "application/json"; + if (code < 500) + { + await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})"))); + } + else + { + //ResultFilter 里面的异常并不会到这里 + await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})")))); + } + +}); + + +app.UseExceptionHandler(configure => +{ + configure.Run(async context => + { + var exceptionHandlerPathFeature = context.Features.Get(); + var ex = exceptionHandlerPathFeature?.Error; + context.Response.ContentType = "application/json"; + + if (ex != null) + { + var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : ""); + + await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}"))); + + Log.Logger.Error(errorInfo); + + + } + + }); +}); + + + +#endregion + //本地化 app.UseLocalization(); @@ -211,22 +261,22 @@ app.UseLogDashboard("/LogDashboard"); //hangfire app.UseHangfireConfig(env); - +#region 暂时废弃 ////限流 中间件 //app.UseIpRateLimiting(); +//if (env.IsDevelopment()) +//{ +// app.UseDeveloperExceptionPage(); +//} +//else +//{ +// //app.UseHsts(); +//} +#endregion + -if (env.IsDevelopment()) -{ - app.UseDeveloperExceptionPage(); -} -else -{ - //app.UseHsts(); -} -// 特殊异常处理 比如 404 -app.UseStatusCodePagesWithReExecute("/Error/{0}"); SwaggerSetup.Configure(app, env); diff --git a/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs index 820a2a67e..ae82e3027 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs @@ -7,6 +7,7 @@ using IRaCIS.Core.Infra.EFCore; using Medallion.Threading; using Medallion.Threading.SqlServer; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -21,12 +22,14 @@ namespace IRaCIS.Core.API services.AddHttpContextAccessor(); services.AddScoped(); + services.AddScoped(); + // First, register a pooling context factory as a Singleton service, as usual: //这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext //Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点 - services.AddDbContext(options => + services.AddDbContext((sp, options) => { // 在控制台 @@ -43,6 +46,7 @@ namespace IRaCIS.Core.API options.EnableSensitiveDataLogging(); options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); + options.AddInterceptors(sp.GetServices()); options.UseProjectables(); diff --git a/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs index 823c168da..011e9ceae 100644 --- a/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs @@ -45,6 +45,7 @@ namespace IRaCIS.Core.Application.Filter context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info)); + //warning 级别记录 //_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}"); } @@ -60,12 +61,17 @@ namespace IRaCIS.Core.Application.Filter _logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace)); } + + context.ExceptionHandled = true;//标记当前异常已经被处理过了 + + + //throw new Exception("test-result-exceptioin"); + } else { //继续 } - context.ExceptionHandled = true;//标记当前异常已经被处理过了 } } } diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs index 9985806ed..15414fe37 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs @@ -511,7 +511,6 @@ namespace IRaCIS.Core.Infra.EFCore public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) { // 采用触发器的方式 设置 CreateUserId CreateTime UpdateTime UpdateUserId 稽查实体里面没有这四个字段的值 因为先后顺序的原因 - SetCommonEntityAuditInfo(); await AddAudit(); try @@ -595,85 +594,7 @@ namespace IRaCIS.Core.Infra.EFCore } - /// - /// 重写savechange方式 统一增加审计信息 CreateUserId CreateTime UpdateTime Update UserId - /// - private void SetCommonEntityAuditInfo() - { - ChangeTracker.DetectChanges(); // Important! - - // 获取所有更改,删除,新增的实体,但排除审计实体(避免死循环) - var entities = ChangeTracker.Entries() - .Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)).Where(x => !typeof(DataInspection).IsAssignableFrom(x.Entity.GetType())).ToList(); - - foreach (var t in entities) - { - switch (t.State) - { - - case EntityState.Deleted: - - break; - case EntityState.Modified: - - if (t.Entity is IAuditUpdate updateEntity1) - { - updateEntity1.UpdateTime = DateTime.Now; - updateEntity1.UpdateUserId = _userInfo.Id; - } - - if (t.Entity is ISoftDelete softDelete) - { - if (softDelete.IsDeleted) - { - softDelete.DeleteUserId = _userInfo.Id; - softDelete.DeletedTime = DateTime.Now; - } - else - { - softDelete.DeletedTime = null; - } - } - - break; - //添加的时候,更新审计字段也赋值 - case EntityState.Added: - - - if (t.Entity is IAuditAdd addEntity) - { - if (addEntity.CreateTime == default(DateTime)) - { - addEntity.CreateTime = DateTime.Now; - } - - addEntity.CreateUserId = _userInfo.Id; - } - - if (t.Entity is IAuditUpdate updateEntity) - { - updateEntity.UpdateTime = DateTime.Now; - updateEntity.UpdateUserId = _userInfo.Id; - } - - if (t.Entity is IAuditAddWithUserName addEntity3) - { - if (addEntity3.CreateTime == default(DateTime)) - { - addEntity3.CreateTime = DateTime.Now; - - } - - addEntity3.CreateUserId = _userInfo.Id; - addEntity3.CreateUser = _userInfo.RealName; - } - break; - } - } - - - } public virtual DbSet TaskAllocationRule { get; set; } diff --git a/IRaCIS.Core.Infra.EFCore/Interceptor/AuditEntityInterceptor.cs b/IRaCIS.Core.Infra.EFCore/Interceptor/AuditEntityInterceptor.cs new file mode 100644 index 000000000..b2f99decd --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Interceptor/AuditEntityInterceptor.cs @@ -0,0 +1,103 @@ +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + + +namespace IRaCIS.Core.Infra.EFCore; + +public class AuditEntityInterceptor(IUserInfo _userInfo) : SaveChangesInterceptor +{ + public override InterceptionResult SavingChanges(DbContextEventData eventData, InterceptionResult result) + { + AuditEntities(eventData.Context); + + return base.SavingChanges(eventData, result); + } + + public override ValueTask> SavingChangesAsync(DbContextEventData eventData, + InterceptionResult result, CancellationToken cancellationToken = default) + { + AuditEntities(eventData.Context); + + return base.SavingChangesAsync(eventData, result, cancellationToken); + } + + public void AuditEntities(DbContext? context) + { + if (context == null) return; + + // 获取所有更改,删除,新增的实体,但排除审计实体(避免死循环) + foreach (var entry in context.ChangeTracker.Entries().Where(u => (u.State == EntityState.Modified || u.State == EntityState.Added)) + .Where(x => !typeof(DataInspection).IsAssignableFrom(x.Entity.GetType())).ToList()) + { + switch (entry.State) + { + + case EntityState.Deleted: + + break; + case EntityState.Modified: + + if (entry.Entity is IAuditUpdate updateEntity1) + { + updateEntity1.UpdateTime = DateTime.Now; + updateEntity1.UpdateUserId = _userInfo.Id; + } + + if (entry.Entity is ISoftDelete softDelete) + { + if (softDelete.IsDeleted) + { + softDelete.DeleteUserId = _userInfo.Id; + softDelete.DeletedTime = DateTime.Now; + } + else + { + softDelete.DeletedTime = null; + } + } + + break; + //添加的时候,更新审计字段也赋值 + case EntityState.Added: + + + if (entry.Entity is IAuditAdd addEntity) + { + if (addEntity.CreateTime == default(DateTime)) + { + addEntity.CreateTime = DateTime.Now; + } + + addEntity.CreateUserId = _userInfo.Id; + } + + if (entry.Entity is IAuditUpdate updateEntity) + { + updateEntity.UpdateTime = DateTime.Now; + updateEntity.UpdateUserId = _userInfo.Id; + } + + if (entry.Entity is IAuditAddWithUserName addEntity3) + { + if (addEntity3.CreateTime == default(DateTime)) + { + addEntity3.CreateTime = DateTime.Now; + + } + + addEntity3.CreateUserId = _userInfo.Id; + addEntity3.CreateUser = _userInfo.RealName; + } + break; + } + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Interceptor/AuditInterceptor.cs b/IRaCIS.Core.Infra.EFCore/Interceptor/OldAuditInterceptor.cs similarity index 100% rename from IRaCIS.Core.Infra.EFCore/Interceptor/AuditInterceptor.cs rename to IRaCIS.Core.Infra.EFCore/Interceptor/OldAuditInterceptor.cs diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs index a0edd84d0..c9f51dece 100644 --- a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs @@ -19,6 +19,8 @@ public string LocalizedInfo { get; set; } + + } ///