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

时间:2014-08-09 11:21:30

标签: c# reflection

我有一个库,其中包含一些反射代码,用于检查Asp.Net的主要程序集,任何引用的程序集以及很酷的东西。我试图获得在控制台应用程序中执行的完全相同的代码,同时仍然反映在Asp.Net的程序集中,并且我看到奇怪的结果。我已经将所有内容都连线并且代码执行了,但是当我知道它应该返回true时反射代码返回false,因为我在调试器中单步执行它。它让我疯狂而且我无法弄清楚为什么反射在从控制台应用程序运行时表现出不同的行为。

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

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

我可以尝试做些什么?

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();
}

2 个答案:

答案 0 :(得分:0)

这与LoadFrom加载程序集的程序集上下文有关。在解析&#34;常规&#34;时,LoadFrom期间加载的依赖关系将不会被使用。 Load上下文中的程序集。

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

有关详细信息,请参阅https://stackoverflow.com/a/2493855/292411Avoid Assembly.LoadFrom; instead use Assembly.Load,了解与您LoadFrom类似的问题。

当我遇到这个问题时,我转而使用Load并要求&#34;插件&#34;程序集与可执行文件位于同一路径中;如果组件处于不同的路径,我不知道是否有一些技巧可以使工作正常。

答案 1 :(得分:0)

好的,经过大量调试后我才开始工作!原来我的库项目正在编译Asp.Net MVC 4.0,尽管Nuget和属性窗口声称5.1。 Nuget / MS再次失败。我的库正在反映的Asp.Net MVC应用程序正在使用MVC 5.1,因此当Assembly.LoadFromAssemblyResolve事件运行时,它会将System.Web.Mvc.dll的两个版本加载到LoadFrom中} context(4.0&amp; 5.1)这导致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不在列表中。我点燃了Assembly Binding Log Viewer并且显然能够看到System.Web.Mvc.dll被加载了两次。

回想起来,我们应该跳过所有自定义日志记录,只需使用程序集绑定日志查看器来验证每个程序集中只有一个正在加载,并且它是您期望的正确版本。


弄清楚如何正确使用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;
    }
}