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

ehxuflar  于 2023-11-21  发布在  .NET
关注(0)|答案(1)|浏览(149)

我正在寻找社区指导,以帮助我翻译下面提到的代码到一个新的.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's blog post

pobjuy32

pobjuy321#

自2013年以来,Prism已经发展了很多。这篇文章是为一个完全不同的运行时和一个完全不同的Prism版本而写的。当时.NET Framework中没有内置DI,所以Prism最终是一个“有点”的重新设计。
使用内置的DirectoryModuleCatalog类可以从目录中加载模块。该类根本不使用AssemblyLoadContext。实际上,代码看起来与原始文章非常相似,因此必须执行diff才能找到不同之处。
HelloWorld示例应用展示了如何通过覆盖App.CreateModuleCatalog来使用各种模块目录,例如:

public partial class App
{
    protected override Window CreateShell()
    {
        return Container.Resolve<MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSharedSamples();
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        return new DirectoryModuleCatalog() { ModulePath = "Modules" }; 
    };
}

字符串
ConfigureModuleCatalogCreateModuleCatalog替换,CreateModuleCatalogModules文件夹添加编译模块

相关问题