199 lines
8.1 KiB
C#
199 lines
8.1 KiB
C#
using Masuit.Tools;
|
||
using Microsoft.Extensions.DependencyModel;
|
||
using Serilog;
|
||
using System.Reflection;
|
||
using System.Runtime.Loader;
|
||
using YuanXuan.IM.Common.Attributes;
|
||
|
||
namespace YuanXuan.IM.Api.CollectionExtensions
|
||
{
|
||
/// <summary>
|
||
/// 批量注册服务
|
||
/// </summary>
|
||
public static class BatchRegisterServiceCollectionExtension
|
||
{
|
||
/// <summary>
|
||
/// 批量注册带属性的服务和后台服务
|
||
/// </summary>
|
||
/// <param name="services"></param>
|
||
public static void BatchRegisterServices(this IServiceCollection services)
|
||
{
|
||
var allAssembly = GetAllAssembly();
|
||
|
||
services.RegisterServiceByAttribute(ServiceLifetime.Singleton, allAssembly);
|
||
services.RegisterServiceByAttribute(ServiceLifetime.Scoped, allAssembly);
|
||
services.RegisterServiceByAttribute(ServiceLifetime.Transient, allAssembly);
|
||
|
||
services.RegisterBackgroundService(allAssembly);
|
||
}
|
||
/// <summary>
|
||
/// 通过 InjectAttribute 批量注册服务
|
||
/// </summary>
|
||
/// <param name="services"></param>
|
||
/// <param name="serviceLifetime"></param>
|
||
private static void RegisterServiceByAttribute(this IServiceCollection services, ServiceLifetime serviceLifetime, List<Assembly> allAssembly)
|
||
{
|
||
var types = allAssembly.SelectMany(t => t.GetTypes())
|
||
.Where(t => t.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0 &&
|
||
t.GetCustomAttribute<InjectAttribute>()?.Lifetime == serviceLifetime &&
|
||
t.IsClass && !t.IsAbstract).ToList();
|
||
|
||
foreach (var type in types)
|
||
{
|
||
var baseInterfaces = type.BaseType?.GetInterfaces();
|
||
Type? typeInterface = null;
|
||
|
||
// 获取所有接口,然后过滤掉系统接口和常见的不适合DI的接口
|
||
var allInterfaces = type.GetInterfaces();
|
||
var filteredInterfaces = allInterfaces.Where(IsCustomInterface).ToList();
|
||
|
||
// 如果没有自定义接口,则使用直接注入
|
||
if (filteredInterfaces.Count == 0)
|
||
{
|
||
//服务非继承自接口的直接注入
|
||
switch (serviceLifetime)
|
||
{
|
||
case ServiceLifetime.Singleton: services.AddSingleton(type); break;
|
||
case ServiceLifetime.Scoped: services.AddScoped(type); break;
|
||
case ServiceLifetime.Transient: services.AddTransient(type); break;
|
||
}
|
||
Log.Information("直接注入服务: {TypeName} (生命周期: {Lifetime})", type.Name, serviceLifetime);
|
||
}
|
||
else
|
||
{
|
||
// 如果有多个自定义接口,选择最合适的接口
|
||
typeInterface = ChooseBestInterface(filteredInterfaces, type);
|
||
|
||
//服务继承自接口的和接口一起注入
|
||
switch (serviceLifetime)
|
||
{
|
||
case ServiceLifetime.Singleton: services.AddSingleton(typeInterface, type); break;
|
||
case ServiceLifetime.Scoped: services.AddScoped(typeInterface, type); break;
|
||
case ServiceLifetime.Transient: services.AddTransient(typeInterface, type); break;
|
||
}
|
||
Log.Information("接口注入服务: {TypeName} -> {InterfaceName} (生命周期: {Lifetime})",
|
||
type.Name, typeInterface.Name, serviceLifetime);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 注册后台服务
|
||
/// </summary>
|
||
/// <param name="services"></param>
|
||
/// <param name="serviceLifetime"></param>
|
||
private static void RegisterBackgroundService(this IServiceCollection services, List<Assembly> allAssembly)
|
||
{
|
||
List<Type> types = allAssembly.SelectMany(t => t.GetTypes()).Where(t => typeof(BackgroundService).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract).ToList();
|
||
|
||
foreach (var type in types)
|
||
{
|
||
services.AddSingleton(typeof(IHostedService), type);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 判断是否为自定义接口(过滤掉系统接口和常见的不适合DI的接口)
|
||
/// </summary>
|
||
/// <param name="interfaceType">接口类型</param>
|
||
/// <returns></returns>
|
||
private static bool IsCustomInterface(Type interfaceType)
|
||
{
|
||
// 系统命名空间的接口(不适合DI)
|
||
var systemNamespaces = new[]
|
||
{
|
||
"System",
|
||
"Microsoft",
|
||
"Windows",
|
||
"IDisposable",
|
||
"IComparable",
|
||
"IEquatable",
|
||
"IEnumerable",
|
||
"ICollection",
|
||
"IList",
|
||
"IAsyncEnumerable",
|
||
"IAsyncDisposable"
|
||
};
|
||
|
||
// 如果接口名称包含系统命名空间或常见系统接口名称,则过滤掉
|
||
if (systemNamespaces.Any(ns => interfaceType.Namespace?.StartsWith(ns) == true ||
|
||
interfaceType.Name.StartsWith(ns)))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 检查接口是否有自定义项目的命名空间
|
||
var customNamespaces = new[]
|
||
{
|
||
"LearningOfficer.OA",
|
||
"LearningOfficer"
|
||
};
|
||
|
||
// 如果接口有自定义项目的命名空间,则认为是自定义接口
|
||
return customNamespaces.Any(ns => interfaceType.Namespace?.StartsWith(ns) == true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从多个自定义接口中选择最合适的接口
|
||
/// </summary>
|
||
/// <param name="interfaces">接口列表</param>
|
||
/// <param name="implementationType">实现类型</param>
|
||
/// <returns></returns>
|
||
private static Type ChooseBestInterface(List<Type> interfaces, Type implementationType)
|
||
{
|
||
// 如果只有一个接口,直接返回
|
||
if (interfaces.Count == 1)
|
||
return interfaces[0];
|
||
|
||
// 优先选择名称匹配的接口(例如:MyService -> IMyService)
|
||
var serviceName = implementationType.Name;
|
||
var preferredInterface = interfaces.FirstOrDefault(i =>
|
||
i.Name == "I" + serviceName ||
|
||
i.Name == serviceName.Replace("Service", "").Replace("Impl", "") + "Service");
|
||
|
||
if (preferredInterface != null)
|
||
return preferredInterface;
|
||
|
||
// 按接口名称排序,选择最合适的
|
||
return interfaces
|
||
.OrderBy(i => i.Name.StartsWith("I") ? 0 : 1) // 优先选择 I 开头的接口
|
||
.ThenBy(i => i.Name.Length) // 选择名称较短的接口
|
||
.First();
|
||
}
|
||
/// <summary>
|
||
/// 获取全部 Assembly
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private static List<Assembly> GetAllAssembly()
|
||
{
|
||
var allAssemblies = new List<Assembly>();
|
||
var loadedAssemblies = new HashSet<string>();
|
||
|
||
var dependencyContext = DependencyContext.Default;
|
||
var libraries = dependencyContext.RuntimeLibraries
|
||
.Where(lib => !lib.Serviceable && lib.Type != "package")
|
||
.ToList();
|
||
|
||
foreach (var library in libraries)
|
||
{
|
||
try
|
||
{
|
||
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(library.Name));
|
||
if (loadedAssemblies.Add(assembly.FullName))
|
||
{
|
||
allAssemblies.Add(assembly);
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
Log.Fatal("加载程序集失败:{0}", library.Name);
|
||
// Log or handle the exception if necessary
|
||
}
|
||
}
|
||
|
||
return allAssemblies;
|
||
}
|
||
|
||
}
|
||
}
|