繁体   English   中英

反射不适用于使用Assembly.LoadFrom加载的程序集

[英]Reflection not working on assembly that is loaded using Assembly.LoadFrom

我有一个包含一些反射代码的库,该代码检查Asp.Net的主程序集,任何引用的程序集并进行出色的处理。 我试图获取相同的代码以在控制台应用程序中执行,同时仍然反映在Asp.Net的程序集上,并且看到了奇怪的结果。 我已经完成所有工作并执行了代码,但是当我在调试器中逐步执行该反射代码时,当我知道反射代码应该返回true时,反射代码将返回false。这使我发疯,我不知道为什么从控制台应用程序运行时,反射表现出不同的行为。

这是一些反射代码的完美示例,该反射代码获取Asp.Net应用程序中所有类型的区域注册( type.IsSubclassOf(typeof(System.Web.Mvc.AreaRegistration)) )。 在Asp.Net应用程序的应用程序域中执行时,对于几种类型返回true,但是在控制台应用程序中执行时,对于那些相同类型返回false,但是仍然反映那些相同的Asp.Net类型。

我也尝试过使用Assembly.ReflectionOnlyLoadFrom方法,但是即使编写了所有代码以手动解析引用的程序集之后,下面显示的反射代码在应返回true的类型上也返回false。

我怎样才能使这项工作?

public static Assembly EntryAssembly { get; set; } // this is set during runtime if within the Asp.Net domain and set manually when called from the console application.

public CodeGenerator(string entryAssemblyPath = null)
{
    if (entryAssemblyPath == null) // running under the Asp.Net domain
        EntryAssembly = GetWebEntryAssembly(); // get the Asp.Net main assembly
    else
    {
        // manually load the assembly into the domain via a file path
        // e:\inetpub\wwwroot\myAspNetMVCApp\bin\myApp.dll
        EntryAssembly = Assembly.LoadFrom(entryAssemblyPath);
    }

    var areas = GetAreaRegistrations(); // returns zero results under console app domain

    ... code ...
}       

private static List<Type> GetAreaRegistrations()
{
    return EntryAssembly.GetTypes().Where(type => type.IsSubclassOf(typeof(System.Web.Mvc.AreaRegistration)) && type.IsPublic).ToList();
}

这与LoadFrom加载程序集的程序集上下文有关。 Load上下文中解析“常规”程序集时,将不使用在LoadFrom加载的依赖项。

相同的应用ReflectionOnly重载,该重载加载到ReflectionOnly上下文中。

有关详细信息,请参见https://stackoverflow.com/a/2493855/292411 ,并避免Assembly.LoadFrom; 而是使用Assembly.Load解决与您类似的LoadFrom问题。

当我遇到这个问题时,我切换到使用Load并要求“插件”程序集与可执行文件位于同一路径中。 我不知道如果程序集位于不同的路径下是否有使工作正常的技巧。

好的,经过大量调试后,我可以正常工作了! 事实证明,即使Nuget和属性窗口要求5.1,我的库项目仍针对Asp.Net MVC 4.0进行编译。 Nuget / MS再次失败。 我的库所反映的Asp.Net MVC应用程序正在使用MVC 5.1,因此,当Assembly.LoadFromAssemblyResolve事件运行时,它将两个版本的System.Web.Mvc.dll加载到LoadFrom上下文(4.0和5.1)中,并且当预期结果应该为true时,这导致IsSubclassOf()方法返回false。

我在调试时在上面的注释中提到的非常奇怪的错误: The type 'System.Web.Mvc.AreaRegistration' exists in both 'System.Web.Mvc.dll' and 'System.Web.Mvc.dll' ,但只有在事实之后。

我最终跟踪此问题的方法是,写出所有需要AssemblyResolve进行解析的程序集,并注意到System.Web.Mvc.dll不在列表中。 我启动了程序集绑定日志查看器 ,很清楚地看到System.Web.Mvc.dll被加载了两次。

回想起来,应该只跳过所有自定义日志记录,而仅使用Assembly Binding Log Viewer来验证每个程序集中只有一个正在加载并且它是您期望的正确版本。


弄清楚如何正确使用AssemblyResolve是一场噩梦,所以这是我未完成的但后代有效的代码。

public class CodeGenerator
{
    public static string BaseDirectory { get; set; }
    public static string BinDirectory { get; set; }

    static CodeGenerator()
    {
        BinDirectory = "bin";
        // setting this in a static constructor is best practice
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    public CodeGenerator(string entryAssemblyPath = null, string baseDirectory = null, string binDirectory = null)
    {
        if (string.IsNullOrWhiteSpace(baseDirectory))
            BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
        else
            BaseDirectory = baseDirectory;

        if (string.IsNullOrWhiteSpace(binDirectory) == false)
            BinDirectory = binDirectory;

        if (entryAssemblyPath == null) // running under the Asp.Net domain
            EntryAssembly = GetWebEntryAssembly(); // get the Asp.Net main assembly
        else
        {
            // manually load the assembly into the domain via a file path
            // e:\inetpub\wwwroot\myAspNetMVCApp\bin\myApp.dll
            EntryAssembly = Assembly.LoadFrom(entryAssemblyPath);
        }

        var areas = GetAreaRegistrations(); // reflect away!

        ... code ...
    }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        try
        {
            if (args == null || string.IsNullOrWhiteSpace(args.Name))
            {
                Logger.WriteLine("cannot determine assembly name!", Logger.LogType.Debug);
                return null;
            }

            AssemblyName assemblyNameToLookFor = new AssemblyName(args.Name);
            Logger.WriteLine("FullName is {0}", Logger.LogType.Debug, assemblyNameToLookFor.FullName);

            // don't load the same assembly twice!
            var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            var skipLoading = false;
            foreach (var dAssembly in domainAssemblies)
            {
                if (dAssembly.FullName.Equals(assemblyNameToLookFor.FullName))
                {
                    skipLoading = true;
                    Logger.WriteLine("skipping {0} because its already loaded into the domain", Logger.LogType.Error, assemblyNameToLookFor.FullName);
                    break;
                }
            }
            if (skipLoading == false)
            {
                var requestedFilePath = Path.Combine(Path.Combine(BaseDirectory, BinDirectory), assemblyNameToLookFor.Name + ".dll");
                Logger.WriteLine("looking for {0}...", Logger.LogType.Warning, requestedFilePath);
                if (File.Exists(requestedFilePath))
                {
                    try
                    {
                        Assembly assembly = Assembly.LoadFrom(requestedFilePath);
                        if (assembly != null)
                            Logger.WriteLine("loaded {0} successfully!", Logger.LogType.Success, requestedFilePath);
                        // todo: write an else to handle load failure and search various probe paths in a loop
                        return assembly;
                    }
                    catch (FileNotFoundException)
                    {
                        Logger.WriteLine("failed to load {0}", Logger.LogType.Error, requestedFilePath);
                    }
                }
                else
                {
                    try
                    {
                        // ugh, hard-coding, but I need to get on with the real programming for now
                        var refedAssembliesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1");
                        requestedFilePath = Path.Combine(refedAssembliesPath, assemblyNameToLookFor.Name + ".dll");
                        Logger.WriteLine("looking for {0}...", Logger.LogType.Warning, requestedFilePath);
                        Assembly assembly = Assembly.LoadFrom(requestedFilePath);
                        if (assembly != null)
                            Logger.WriteLine("loaded {0} successfully!", Logger.LogType.Success, requestedFilePath);
                        // todo: write an else to handle load failure and search various probe paths in a loop
                        return assembly;
                    }
                    catch (FileNotFoundException)
                    {
                        Logger.WriteLine("failed to load {0}", Logger.LogType.Error, requestedFilePath);
                    }
                }
            }
        }
        catch (Exception e)
        {
            Logger.WriteLine("exception {0}", Logger.LogType.Error, e.Message);
        }
        return null;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM