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;
};
}
}