Files
StoreWeb/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs
T
2026-09-18 19:17:11 +08:00

224 lines
7.4 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 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);
}
}
}