diff --git a/sample/WebApi.Test.Unit/Program.cs b/sample/WebApi.Test.Unit/Program.cs index 800a2407..89a30ab8 100644 --- a/sample/WebApi.Test.Unit/Program.cs +++ b/sample/WebApi.Test.Unit/Program.cs @@ -1,4 +1,3 @@ -using EasilyNET.Core.Misc; using Serilog; using Serilog.Events; using Serilog.Sinks.OpenTelemetry; @@ -10,7 +9,6 @@ #endif Console.Title = $"❤️ {Constant.InstanceName}"; -AssemblyHelper.AddExcludeLibs("Npgsql."); var builder = WebApplication.CreateBuilder(args); // 配置Kestrel支持HTTP1,2,3 diff --git a/src/EasilyNET.Core/Misc/AssemblyHelper.cs b/src/EasilyNET.Core/Misc/AssemblyHelper.cs index 31ca8790..2c0c058d 100644 --- a/src/EasilyNET.Core/Misc/AssemblyHelper.cs +++ b/src/EasilyNET.Core/Misc/AssemblyHelper.cs @@ -1,3 +1,6 @@ +using System.Collections.Concurrent; +using System.Collections.Frozen; +using System.Diagnostics; using System.Reflection; using System.Runtime.Loader; using Microsoft.Extensions.DependencyModel; @@ -10,52 +13,43 @@ namespace EasilyNET.Core.Misc; /// /// 程序集帮助类 /// -// ReSharper disable once UnusedType.Global public static class AssemblyHelper { - private static readonly HashSet Filters = ["dotnet-", "Microsoft.", "mscorlib", "netstandard", "System", "Windows", "PresentationUI"]; - - /// - /// 需要排除的项目 - /// - private static readonly HashSet FilterLibs = []; + private static readonly ConcurrentDictionary AssemblyCache = []; /// /// 构造函数 /// static AssemblyHelper() { - AllAssemblies = DependencyContext.Default?.GetDefaultAssemblyNames().Where(c => c.Name is not null && !Filters.Any(c.Name.StartsWith) && !FilterLibs.Any(c.Name.StartsWith)).Select(Assembly.Load); - AllTypes = AllAssemblies.AsNotNull().SelectMany(c => c.GetTypes()); + AllAssemblies = LoadAssemblies(); + AllTypes = AllAssemblies.SelectMany(c => c.GetTypes()); } /// /// 获取所有扫描到符合条件的程序集 /// - public static IEnumerable? AllAssemblies { get; } + public static IEnumerable AllAssemblies { get; } /// /// 获取所有扫描到符合条件的程序集中的类型 /// - public static IEnumerable? AllTypes { get; } - - /// - /// 添加排除项目,该排除项目可能会影响AutoDependenceInjection自动注入,请使用的时候自行测试. - /// - /// - public static void AddExcludeLibs(params string[] names) => FilterLibs.AddRangeIfNotContains(names); + public static IEnumerable AllTypes { get; } /// /// 根据程序集名字得到程序集 /// /// /// - public static IEnumerable GetAssembliesByName(params string[] assemblyNames) => assemblyNames.Select(o => AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(AppContext.BaseDirectory, $"{o}.dll"))); + public static IEnumerable GetAssembliesByName(params string[] assemblyNames) + { + return assemblyNames.Select(name => AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(AppContext.BaseDirectory, $"{name}.dll"))); + } /// /// 查找指定条件的类型 /// - public static IEnumerable FindTypes(Func predicate) => AllTypes.AsNotNull().Where(predicate); + public static IEnumerable FindTypes(Func predicate) => AllTypes.Where(predicate); /// /// 查找所有指定特性标记的类型 @@ -69,10 +63,72 @@ static AssemblyHelper() /// /// /// - public static IEnumerable FindTypesByAttribute(Type type) => AllTypes.AsNotNull().Where(a => a.IsDefined(type, true)).Distinct(); + public static IEnumerable FindTypesByAttribute(Type type) => AllTypes.Where(a => a.IsDefined(type, true)).Distinct(); /// /// 查找指定条件的类型 /// - public static IEnumerable FindAllItems(Func predicate) => AllAssemblies.AsNotNull().Where(predicate); + public static IEnumerable FindAllItems(Func predicate) => AllAssemblies.Where(predicate); + + private static IEnumerable LoadAssemblies() + { + var assemblyNames = DependencyContext.Default?.GetDefaultAssemblyNames().Where(c => c.Name is not null).ToFrozenSet(); + if (assemblyNames is null) yield break; + var batchSize = (int)Math.Ceiling((double)assemblyNames.Count / Environment.ProcessorCount); // 计算每组的大小 + var loadedAssemblies = new ConcurrentBag(); + if (assemblyNames.Count >= Environment.ProcessorCount) + { + var batches = assemblyNames.Select((name, index) => new { name, index }) + .GroupBy(x => x.index / batchSize) + .Select(g => g.Select(x => x.name)); + Parallel.ForEach(batches, batch => + { + foreach (var assemblyName in batch) + { + LoadAssembly(assemblyName, loadedAssemblies); + } + }); + } + else + { + foreach (var assemblyName in assemblyNames) + { + LoadAssembly(assemblyName, loadedAssemblies); + } + } + foreach (var assembly in loadedAssemblies) + { + yield return assembly; + } + } + + private static void LoadAssembly(AssemblyName assemblyName, ConcurrentBag loadedAssemblies) + { + if (AssemblyCache.TryGetValue(assemblyName.FullName, out var cachedAssembly)) + { + if (cachedAssembly is not null) + { + loadedAssemblies.Add(cachedAssembly); + } + return; + } + try + { + var assembly = Assembly.Load(assemblyName); + AssemblyCache[assemblyName.FullName] = assembly; + // 预检查程序集中的类型,确保可以加载 + _ = assembly.GetTypes(); + loadedAssemblies.Add(assembly); + } + catch (ReflectionTypeLoadException) + { + AssemblyCache[assemblyName.FullName] = null; + Debug.WriteLine($"无法加载程序集中的某些类型: {assemblyName.Name}, 将跳过."); + } + catch (Exception ex) + { + AssemblyCache[assemblyName.FullName] = null; + Debug.WriteLine($"加载程序集失败: {assemblyName.Name}, 错误: {ex.Message}"); + } + } } \ No newline at end of file