224 lines
7.4 KiB
C#
224 lines
7.4 KiB
C#
using MAX.Core.DependencyInjection;
|
||
using MAX.Core.Interfaces;
|
||
using MAX.Framework.Commons;
|
||
using MAX.Framework.Dtos;
|
||
using MAX.Framework.Interfaces;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.Hosting;
|
||
using System.Diagnostics;
|
||
using System.Reflection;
|
||
|
||
namespace MAX.Framework.DependencyInjection
|
||
{
|
||
/// <summary>
|
||
/// IOC 注册入口(由宿主 Program.cs 调用)
|
||
/// </summary>
|
||
public static class ServiceCollectionExtensions
|
||
{
|
||
private static readonly Type[] DependencyMarkers =
|
||
[
|
||
typeof(ISingletonDependency),
|
||
typeof(IScopedDependency),
|
||
typeof(ITransientDependency),
|
||
];
|
||
|
||
/// <summary>
|
||
/// 兼容入口:只有服务集合,没有配置与环境(模块拿到的 <see cref="ModuleContext.Configuration"/> 是空配置)。
|
||
/// 新代码请用 <c>builder.AddStoreHub()</c>。
|
||
/// </summary>
|
||
public static IServiceCollection AddStoreHub(this IServiceCollection services)
|
||
{
|
||
return services.AddStoreHub(null, null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 带配置的注册入口:<c>builder.Services.AddStoreHub(builder.Configuration)</c>
|
||
/// </summary>
|
||
public static IServiceCollection AddStoreHub(this IServiceCollection services, IConfiguration? configuration)
|
||
{
|
||
return services.AddStoreHub(configuration, null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 注册应用服务:
|
||
/// <list type="number">
|
||
/// <item>自动注册所有实现 <see cref="ISingletonDependency"/> / <see cref="IScopedDependency"/> / <see cref="ITransientDependency"/> 的类型;</item>
|
||
/// <item>发现所有 <see cref="IStartup"/>,按 <see cref="IStartup.Priority"/> 降序执行 Initialize + Register;</item>
|
||
/// <item>发现所有 <see cref="IModule"/>,按 <see cref="IModule.Priority"/> 降序执行 <see cref="IModule.AddModule"/>,
|
||
/// 并把模块实例登记进容器,留给 <c>app.UseStoreHubModules()</c> 执行管道阶段。</item>
|
||
/// </list>
|
||
/// </summary>
|
||
public static IServiceCollection AddStoreHub(this IServiceCollection services, IConfiguration? configuration, IHostEnvironment? environment)
|
||
{
|
||
IEnumerable<Assembly> assemblies = GetApplicationAssemblies();
|
||
|
||
// 同一个类可能同时实现 IStartup 与 IModule,这里缓存实例,保证两个阶段拿到的是同一个对象
|
||
Dictionary<Type, object> instances = [];
|
||
|
||
RegisterDependencyMarkers(services, assemblies);
|
||
RunStartups(services, assemblies, instances);
|
||
|
||
ModuleContext context = new(
|
||
services,
|
||
new ServiceRegistrarProxy(services),
|
||
configuration ?? new ConfigurationBuilder().Build(),
|
||
environment);
|
||
|
||
// 注册表必须在 Build() 之前进容器,UseStoreHubModules() 才拿得到
|
||
services.AddSingleton(RunModules(assemblies, instances, context));
|
||
|
||
|
||
services.AddControllers(options =>
|
||
{
|
||
options.Filters.Add<GlobalExceptionFilter>();
|
||
options.Filters.Add<UnifyResultFilter>();
|
||
}).ConfigureApiBehaviorOptions(options =>
|
||
{
|
||
options.InvalidModelStateResponseFactory = context =>
|
||
{
|
||
// 取出所有校验错误信息,拼成一个字符串(或者只取第一个)
|
||
var errors = context.ModelState
|
||
.Where(e => e.Value?.Errors.Count > 0)
|
||
.SelectMany(e => e.Value!.Errors.Select(err => err.ErrorMessage))
|
||
.ToList();
|
||
|
||
var msg = string.Join("; ", errors);
|
||
|
||
var result = ApiResult.Fail(msg, ResultCode.ValidationError);
|
||
|
||
return new ObjectResult(result)
|
||
{
|
||
StatusCode = 200 // 和其他接口保持一致,用Code表达失败,而不是HTTP 400
|
||
};
|
||
};
|
||
|
||
});
|
||
return services;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动注册标记接口(ISingletonDependency / IScopedDependency / ITransientDependency)的实现类型
|
||
/// </summary>
|
||
private static void RegisterDependencyMarkers(IServiceCollection services, IEnumerable<Assembly> assemblies)
|
||
{
|
||
foreach (Type type in GetConcreteTypes(assemblies))
|
||
{
|
||
Debug.WriteLine(type.Namespace);
|
||
Type? marker = DependencyMarkers.FirstOrDefault(m => m.IsAssignableFrom(type));
|
||
if (marker is null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
ServiceLifetime lifetime = marker == typeof(ISingletonDependency) ? ServiceLifetime.Singleton
|
||
: marker == typeof(IScopedDependency) ? ServiceLifetime.Scoped
|
||
: ServiceLifetime.Transient;
|
||
|
||
// 注册为自身
|
||
services.Add(new ServiceDescriptor(type, type, lifetime));
|
||
|
||
// 注册为实现的接口(排除标记接口与系统接口)
|
||
foreach (Type iface in type.GetInterfaces())
|
||
{
|
||
if (DependencyMarkers.Contains(iface) || IsSystemInterface(iface))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
services.Add(new ServiceDescriptor(iface, type, lifetime));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发现所有 <see cref="IStartup"/> 实现,按 <see cref="IStartup.Priority"/> 降序执行
|
||
/// </summary>
|
||
private static void RunStartups(IServiceCollection services, IEnumerable<Assembly> assemblies, IDictionary<Type, object> instances)
|
||
{
|
||
IServiceRegistrar registrar = new ServiceRegistrarProxy(services);
|
||
|
||
foreach (IStartup startup in CreateInstances<IStartup>(assemblies, instances).OrderByDescending(s => s.Priority))
|
||
{
|
||
startup.Initialize();
|
||
startup.Register(registrar);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发现所有 <see cref="IModule"/> 实现,按 <see cref="IModule.Priority"/> 降序执行服务注册阶段
|
||
/// </summary>
|
||
private static ModuleRegistry RunModules(IEnumerable<Assembly> assemblies, IDictionary<Type, object> instances, ModuleContext context)
|
||
{
|
||
List<IModule> modules = [.. CreateInstances<IModule>(assemblies, instances).OrderByDescending(m => m.Priority)];
|
||
|
||
foreach (IModule module in modules)
|
||
{
|
||
try
|
||
{
|
||
module.AddModule(context);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 带上模块名,避免只看到一个没有上下文的异常
|
||
throw new InvalidOperationException($"模块 {module.GetType().FullName} 的 AddModule 执行失败。", ex);
|
||
}
|
||
}
|
||
|
||
return new ModuleRegistry(modules);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 反射创建(或复用)所有实现 <typeparamref name="T"/> 的具体类型实例
|
||
/// </summary>
|
||
private static IEnumerable<T> CreateInstances<T>(IEnumerable<Assembly> assemblies, IDictionary<Type, object> instances) where T : class
|
||
{
|
||
foreach (Type type in GetConcreteTypes(assemblies).Where(t => typeof(T).IsAssignableFrom(t)))
|
||
{
|
||
if (!instances.TryGetValue(type, out object? instance))
|
||
{
|
||
instance = Activator.CreateInstance(type, true)
|
||
?? throw new InvalidOperationException($"无法创建 {type.FullName} 的实例(需要有参数量为 0 的构造函数)。");
|
||
instances[type] = instance;
|
||
}
|
||
|
||
yield return (T)instance;
|
||
}
|
||
}
|
||
|
||
private static IEnumerable<Type> GetConcreteTypes(IEnumerable<Assembly> assemblies)
|
||
{
|
||
return assemblies
|
||
.SelectMany(SafeGetTypes)
|
||
.Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericTypeDefinition);
|
||
}
|
||
|
||
private static IEnumerable<Type> SafeGetTypes(Assembly assembly)
|
||
{
|
||
try
|
||
{
|
||
return assembly.GetTypes();
|
||
}
|
||
catch (ReflectionTypeLoadException ex)
|
||
{
|
||
return ex.Types.OfType<Type>();
|
||
}
|
||
}
|
||
|
||
private static IEnumerable<Assembly> GetApplicationAssemblies()
|
||
{
|
||
// 使用 AppTypeFinder:扫描应用输出目录 + 已加载程序集,
|
||
return StoreHubAppTypeFinder.Current.GetAssemblies();
|
||
}
|
||
|
||
private static bool IsSystemInterface(Type type)
|
||
{
|
||
return type.Namespace is null
|
||
|| type.Namespace.StartsWith("System", StringComparison.Ordinal)
|
||
|| type.Namespace.StartsWith("Microsoft", StringComparison.Ordinal);
|
||
}
|
||
|
||
}
|
||
}
|