如何在.NET 7中使用新的AssemblyLoadContext加载程序集?

问题描述 投票:0回答:1

我正在寻找社区指导来帮助我将下面提到的代码转换为新的 .NET 7 兼容版本。尤其是

LoadModuleCatalog
方法里面的点点滴滴。

根据我迄今为止的研究,旧的 AppDomain 不再适用于 .NET 7.0 版本,而是 Microsoft 引入了新的 AssemblyLoadContext 来加载程序集,但由于缺乏有关此主题的在线文档和资源,我我正在努力将此代码片段转换为 .NET 7 兼容版本。

public class DynamicDirectoryModuleCatalog : ModuleCatalog
{
    void LoadModuleCatalog(string path, bool isFile = false)
    {
        AppDomain? parentDomain = AppDomain.CurrentDomain;
        Evidence evidence = new Evidence(parentDomain.Evidence);
        AppDomainSetup setup = parentDomain.SetupInformation;
        AppDomain childDomain =  AppDomain.CreateDomain("DiscoveryRegion", evidence, setup);

        try
        {
            List<string> loadedAssemblies = new List<string>();

            var assemblies = (
                 from Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()
                 where !(assembly is System.Reflection.Emit.AssemblyBuilder)
                    && assembly.GetType().FullName != "System.Reflection.Emit.InternalAssemblyBuilder"
                    && !String.IsNullOrEmpty(assembly.Location)
                 select assembly.Location
                 );


            loadedAssemblies.AddRange(assemblies);

            Type loaderType = typeof(InnerModuleInfoLoader);
            if (loaderType.Assembly != null)
            {
                var loader = (InnerModuleInfoLoader)childDomain.CreateInstanceFrom(loaderType.Assembly.Location, loaderType.FullName).Unwrap();
                loader.LoadAssemblies(loadedAssemblies);

                ModuleInfo[] modules = loader.GetModuleInfos(path, isFile);

                this.Items.AddRange(modules);

                if (isFile) LoadModules(modules);
            }
        }
        finally
        {
            AppDomain.Unload(childDomain);
        }
    }

    private class InnerModuleInfoLoader : MarshalByRefObject
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        internal ModuleInfo[] GetModuleInfos(string path, bool isFile = false)
        {
            Assembly moduleReflectionOnlyAssembly =
                AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().First(
                    asm => asm.FullName == typeof(IModule).Assembly.FullName);

            Type IModuleType = moduleReflectionOnlyAssembly.GetType(typeof(IModule).FullName);

            FileSystemInfo info = null;
            if (isFile)
                info = new FileInfo(path);
            else
                info = new DirectoryInfo(path);

            ResolveEventHandler resolveEventHandler = delegate(object sender, ResolveEventArgs args) { return OnReflectionOnlyResolve(args, info); };
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;
            IEnumerable<ModuleInfo> modules = GetNotAllreadyLoadedModuleInfos(info, IModuleType);
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;

            return modules.ToArray();
        }

        private static IEnumerable<ModuleInfo> GetNotAllreadyLoadedModuleInfos(FileSystemInfo info, Type IModuleType)
        {
            List<FileInfo> validAssemblies = new List<FileInfo>();
            Assembly[] alreadyLoadedAssemblies = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();

            FileInfo fileInfo = info as FileInfo;
            if (fileInfo != null)
            {
                if (alreadyLoadedAssemblies.FirstOrDefault(assembly => String.Compare(Path.GetFileName(assembly.Location), fileInfo.Name, StringComparison.OrdinalIgnoreCase) == 0) == null)
                {
                    var moduleInfos = Assembly.ReflectionOnlyLoadFrom(fileInfo.FullName).GetExportedTypes()
                    .Where(IModuleType.IsAssignableFrom)
                    .Where(t => t != IModuleType)
                    .Where(t => !t.IsAbstract).Select(t => CreateModuleInfo(t));

                    return moduleInfos;
                }
            }

            DirectoryInfo directory = info as DirectoryInfo;

            var files = directory.GetFiles("*.dll").Where(file => alreadyLoadedAssemblies.
                FirstOrDefault(assembly => String.Compare(Path.GetFileName(assembly.Location), file.Name, StringComparison.OrdinalIgnoreCase) == 0) == null);

            foreach (FileInfo file in files)
            {
                try
                {
                    Assembly.ReflectionOnlyLoadFrom(file.FullName);
                    validAssemblies.Add(file);
                }
                catch (BadImageFormatException)
                {
                    // skip non-.NET Dlls
                }
            }

            return validAssemblies.SelectMany(file => Assembly.ReflectionOnlyLoadFrom(file.FullName)
                                        .GetExportedTypes()
                                        .Where(IModuleType.IsAssignableFrom)
                                        .Where(t => t != IModuleType)
                                        .Where(t => !t.IsAbstract)
                                        .Select(type => CreateModuleInfo(type)));
        }


        private static Assembly OnReflectionOnlyResolve(ResolveEventArgs args, FileSystemInfo info)
        {
            Assembly loadedAssembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(
                asm => string.Equals(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase));
            if (loadedAssembly != null)
            {
                return loadedAssembly;
            }

            DirectoryInfo directory = info as DirectoryInfo;
            if (directory != null)
            {
                AssemblyName assemblyName = new AssemblyName(args.Name);
                string dependentAssemblyFilename = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
                if (File.Exists(dependentAssemblyFilename))
                {
                    return Assembly.ReflectionOnlyLoadFrom(dependentAssemblyFilename);
                }
            }

            return Assembly.ReflectionOnlyLoad(args.Name);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        internal void LoadAssemblies(IEnumerable<string> assemblies)
        {
            foreach (string assemblyPath in assemblies)
            {
                try
                {
                    Assembly.ReflectionOnlyLoadFrom(assemblyPath);
                }
                catch (FileNotFoundException)
                {
                    // Continue loading assemblies even if an assembly can not be loaded in the new AppDomain
                }
            }
        }

        private static ModuleInfo CreateModuleInfo(Type type)
        {
            // create ModuleInfo, implementation is not relevant
        }
    }
}

感谢在这方面的任何帮助。

此代码来自 Brian Lagunas 的博客文章

c# wpf prism assemblyloadcontext
1个回答
0
投票

Prism 自 2013 年以来已经发展了很多。使用内置的 DirectoryModuleCatalog 类可以开箱即用地从目录加载模块。

HelloWorld示例应用程序展示了如何通过覆盖

App.CreateModuleCatalog
来使用各种模块目录,例如:

protected override IModuleCatalog CreateModuleCatalog()
{
    return new DirectoryModuleCatalog() { ModulePath = "Modules" };
}
© www.soinside.com 2019 - 2024. All rights reserved.