using LFlow.Base.Interfaces; using LFlow.Base.Utils; using LFlow.Cache; using LFlow.InternalEventBus; using Minio; using LFlow.Middleware; using LFlow.Middleware.Register; using Serilog; using Serilog.Events; using SqlSugar; using System.Reflection; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using System.Runtime; using Microsoft.Extensions.Configuration; namespace LFlow.Base; /// /// /// public static class Program { /// /// 入口 /// /// /// public static void Main(string[] args) { // 先用Serilog的BootstrapLogger Log.Logger = new LoggerConfiguration() .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.Console() .CreateBootstrapLogger(); ConfigDllLoader(); var builder = WebApplication.CreateBuilder(args); // 添加到Service中 builder.Services.AddSerilog((services, lc) => lc .ReadFrom.Configuration(builder.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() // .WriteTo.Console() ); builder.Services.AddLogging(loggingBuilder => { loggingBuilder.AddSerilog(Log.Logger, dispose: true); }); builder.Services.AddResponseCompression(); builder.Services.AddResponseCaching(); Log.Logger.Information("LoadSubService"); builder.Services.LoadSubService(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddHttpContextAccessor(); builder.Services.AddResponseCompression(); Log.Logger.Information("ConfigureSqlSugar"); builder.Services.ConfigureSqlSugar(); builder.Services.AddControllers(c => { c.Conventions.Add(new ApiExplorerGroupPerVersionConvention()); }); //注入IMinIo builder.Services.AddSingleton (s => { var config = builder.Configuration.GetSection("MinIoConfig"); IMinioClient client = new MinioClient() .WithEndpoint(config.GetSection("endPoint").Value) .WithCredentials(config.GetSection("AccessKey").Value, config.GetSection("SecretKey").Value) .WithSSL(false) .Build(); return client; } ); builder.Services.AddSwaggerGen(u => { u.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Version = "Ver.1", Title = "LFlow", Description = "LFlow api test and document", Contact = new Microsoft.OpenApi.Models.OpenApiContact { Name = "ling", Email = "noemail@ling.chat" } }); u.IncludeXmlComments("Base.xml", true); // 获取自定义的xml目录 var xmlPath = Path.Combine(System.AppContext.BaseDirectory, "Services"); DirectoryInfo dir = new(xmlPath); // 获取目录下的所有xml文件,并设置为swagger文档包含文件 dir.GetFiles("*.xml").ToList().ForEach(f => { u.IncludeXmlComments(f.FullName, true); u.SwaggerDoc(Path.GetFileNameWithoutExtension(f.Name), new Microsoft.OpenApi.Models.OpenApiInfo { Version = "Ver.1", Title = Path.GetFileNameWithoutExtension(f.Name), Description = "LFlow api test and document", }); }); }); // 配置跨域策略 builder.Services.AddCors(options => { options.AddPolicy("AllowAny", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); builder.Host.UseSerilog(Log.Logger); // 注册中间件 builder.Services.RegisterMiddlewares(App.SubServiceAssembly); // 注册内部事件总线 builder.Services.AddInternalEventBus(App.SubServiceAssembly); // 注册自身缓存器 builder.Services.AddSelfCache(); // init App.service App.services = builder.Services; var app = builder.Build(); app.UseSerilogRequestLogging(options => { // 配置日志级别 options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Information; }); app.MapControllers(); // 添加根目录映射 app.MapGet("/", async context => { context.Response.ContentType = "text/html"; await context.Response.SendFileAsync(Path.Combine(app.Environment.WebRootPath, "index.html")); }); // if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(u => { u.DocumentTitle = "LFlow"; var xmlPath = Path.Combine(System.AppContext.BaseDirectory, "Services"); DirectoryInfo dir = new(xmlPath); // 获取目录下的所有xml文件,并设置为swagger文档包含文件 dir.GetFiles("*.xml").ToList().ForEach(f => { var progName = Path.GetFileNameWithoutExtension(f.Name); u.SwaggerEndpoint($"/swagger/{progName}/swagger.json", progName); }); }); } // 在启动后调用Sqlsugar进行CodeFirst // 挂载启动后事件 app.Services.GetRequiredService().ApplicationStarted.Register(() => { Log.Logger.Information("ApplicationStarted"); CodeFirst.InitTable(); InternalEventBus.InternalEventBus.Publish("Init", "ApplicationStarted"); }); // 启用自定义中间件 app.UseLFlowMiddleware(); // 启用内部事件总线 app.UseInternalEventBus(); // 启用缓存 app.UseResponseCaching(); // 启用压缩 app.UseResponseCompression(); // 启用静态文件支持 app.UseStaticFiles(); app.UseCors("AllowAny"); app.Run(); } /// /// 配置SqlSugar /// /// public static void ConfigureSqlSugar(this IServiceCollection services) { services.AddSingleton(s => { SqlSugarScope sqlSugar = new(new ConnectionConfig() { DbType = SqlSugar.DbType.Sqlite, ConnectionString = "DataSource=LFlow-dev.db", IsAutoCloseConnection = true, InitKeyType = InitKeyType.Attribute //从实体特性中读取主键自增列信息 }, db => { //单例参数配置,所有上下文生效 db.Aop.OnLogExecuting = (sql, pars) => { //获取作IOC作用域对象 var appServive = s.GetService(); var log = s.GetService(); // var obj = appServive?.HttpContext?.RequestServices.GetService(); // Console.WriteLine("AOP" + obj.GetHashCode()); log?.Debug($"{appServive?.HttpContext?.Request.Path}:{sql}\r\n{pars}"); }; }); sqlSugar.DbMaintenance.CreateDatabase(); return sqlSugar; }); } /// /// 从子文件夹中加载DLL /// public static void LoadSubService(this IServiceCollection services) { //TODO: 从配置文件中读取加载路径 var path = Path.Combine(AppContext.BaseDirectory, "Services"); // Console.WriteLine(path); var files = Directory.GetFiles(path, "*.dll"); try { foreach (var file in files) { try { bool isModule = false; Log.Logger.Information($"Load file -> {file}..."); var assembly = Assembly.LoadFile(file); var types = assembly.GetTypes(); // bool isUseController = false; foreach (var type in types) { // Console.WriteLine(type); if (type.IsClass && !type.IsAbstract) { var interfaces = type.GetInterfaces(); if (interfaces.Contains(typeof(IModule))) { Log.Logger.Information($"\tFound IModule -> {type.FullName}"); var module = Activator.CreateInstance(type) as IModule; module?.ConfigureModule(services); isModule = true; } } } // if (isUseController) // { // Log.Logger.Information($"\tAdd Controllers for {assembly.FullName}"); // // 添加Controller // services.AddControllers().AddApplicationPart(assembly); // } if (isModule) { App.SubServiceAssembly.Add(assembly); } Log.Logger.Information("done.\r\n"); } catch (Exception ex) { Log.Logger.Error(ex, $"Load file -> {file} error."); } } } catch (System.Exception ex) { Console.WriteLine(ex.ToString()); } } /// /// 配置DLL加载器 /// 接管DLL加载过程,处理依赖问题,从当前路径和Service文件夹中加载DLL /// public static void ConfigDllLoader() { AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var assemblyName = new AssemblyName(args.Name); // 判断是否已经被加载 避免重复加载 var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.FullName == assemblyName.FullName); if (loadedAssembly != null) { Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded."); return loadedAssembly; } // 从当前路径下加载 var path = Path.Combine(AppContext.BaseDirectory, $"{assemblyName.Name}.dll"); if (File.Exists(path)) { Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded for {path}."); return Assembly.LoadFile(path); } // 从Service文件夹中加载 path = Path.Combine(AppContext.BaseDirectory, "Service", $"{assemblyName.Name}.dll"); if (File.Exists(path)) { Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded for {path}."); return Assembly.LoadFile(path); } Log.Logger?.Error($"Assembly {assemblyName.Name} not found."); return null; }; } }