LFlow/LFlow.Base/Program.cs

331 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
///
/// </summary>
public static class Program
{
/// <summary>
/// 入口
/// </summary>
/// <param name="args"></param>
///
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<IMinioClient>
(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<IHostApplicationLifetime>().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();
}
/// <summary>
/// 配置SqlSugar
/// </summary>
/// <param name="services"></param>
public static void ConfigureSqlSugar(this IServiceCollection services)
{
services.AddSingleton<ISqlSugarClient>(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<IHttpContextAccessor>();
var log = s.GetService<Serilog.ILogger>();
// var obj = appServive?.HttpContext?.RequestServices.GetService<Log>();
// Console.WriteLine("AOP" + obj.GetHashCode());
log?.Debug($"{appServive?.HttpContext?.Request.Path}:{sql}\r\n{pars}");
};
});
sqlSugar.DbMaintenance.CreateDatabase();
return sqlSugar;
});
}
/// <summary>
/// 从子文件夹中加载DLL
/// </summary>
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());
}
}
/// <summary>
/// 配置DLL加载器
/// 接管DLL加载过程处理依赖问题从当前路径和Service文件夹中加载DLL
/// </summary>
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;
};
}
}