【问题标题】:How to get custom attributes from an assembly that is not (really) loaded如何从未(真正)加载的程序集中获取自定义属性
【发布时间】:2010-11-30 09:09:49
【问题描述】:

我们都知道可以使用 GetCustomAttributes 方法来查询程序集的属性。我想用它来识别我的应用程序的扩展模块。但是,为了避免加载每个程序集,我更喜欢防御性方法:

  1. 使用 Assembly.ReflectionOnlyLoadFrom 获取有关程序集的更多详细信息(它是我的 ModuleAttribute 吗?)

  2. 如果找到 ModuleAttribute,我最终会使用 Assembly.LoadFrom 加载它

不幸的是,似乎没有办法从程序集中获取属性,即加载到仅反射上下文中:

myAssembly.GetCustomAttributes(typeof(ModuleAttribute), false)

InvalidOperationException 失败

反射通过 ReflectionOnlyGetType 加载的 Type 的自定义属性是非法的

CustomAttributeData.GetCustomAttributes(myAssembly)

由于未加载依赖程序集,ReflectionTypeLoadException 失败。

那么如何在没有的情况下获取属性

  1. 通过调用 Assembly.LoadFrom 用无用(可能有害)的类型污染我的应用程序域
  2. 需要加载所有引用的程序集
  3. 需要单独的应用程序域(试一试,闻起来更像 PITA)

?

【问题讨论】:

    标签: c# plugins assemblies attributes


    【解决方案1】:

    如果我是你,我会将程序集元数据缓存在例如 XML 文件中。使用这种方法,您完全无需加载任何内容。您甚至可以存储信息,以计算您需要执行哪些耗时的操作。

    您可以通过扫描所有现有程序集预先生成或即时生成 XML 文件。

    【讨论】:

    • 不喜欢这种方法,因为它依赖于额外的元数据。这并不比硬编码具有固定文件名的程序集必须位于哪个目录中好多少。
    • 它依赖于缓存的元数据,而不是额外的。您从程序集中提取元数据并将它们存储在缓存中(XML 文件或其他文件)。它比阅读程序集更快更好。此外,这种方法更具可扩展性,因为一段时间后你会得到一些东西,如果不加载它,你将无法从程序集中提取。
    • 也许我不明白,但谁在创建元数据缓存?它要求创建者提供有效的元数据描述,或者我在尝试加载模块之前先创建它。在这两种方式中,都需要有人这样做,而问题是完全相同的
    • “或者我在尝试加载模块之前先创建它” - 是的。你只需要这样做一次。例如,在软件安装期间。之后,当您从生成的缓存中读取元数据时,性能不会受到影响。
    【解决方案2】:

    你看过微软插件框架吗?

    它允许在不同的应用程序域和进程中加载​​模块(AddIns),并通过接口查询特定的属性数据。

    它可能不是你想要的,但值得一看。

    http://msdn.microsoft.com/en-us/library/bb384200.aspx

    string[] warnings = AddInStore.Update(Environment.CurrentDirectory);
    Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(YourType), Environment.CurrentDirectory)
    
    AddInEnvironment addInEnvironment = new AddInEnvironment(AppDomain.CurrentDomain);
    YourType module = tokens[0].Activate<YourType>(addInEnvironment);
    

    你可以用一点 LINQ 来查询它

    AddInToken myToken = tokens.FirstOrDefault(currentAddInToken => currentAddInToken.Name.Equals(moduleName))
    

    【讨论】:

    • MAF 甚至 MEF 似乎是一种选择,是的。不幸的是,这需要改变插件的设计,这不是我们直接(也不是强制性)开发的。
    • 如果插件不易打包,可能是个问题
    • 但是如果你需要重新编译外部插件,我想包装它会违背你的需要
    【解决方案3】:

    在检查所有答案并进行更多研究之后,似乎根本没有办法做我想做的事:在将程序集加载到应用程序域之前检查它是否是有效的扩展模块。

    要么我必须将应检查的程序集加载到另一个应用程序域中,在那里检查它,当它成功将其再次加载到我当前的应用程序域中时,或者我需要将程序集的元数据存储在程序集本身之外并信任它元数据。由于架构限制,选项一是不可能的,选项二只是转移了问题,但没有解决它。

    可能最好的替代方法是使用托管可扩展性框架,但不幸的是,在当前设置中这并不容易。

    我最终相信模块目录中没有任何“坏”并加载所有内容(通过一些检查以确保不超过最大文件大小并且尚未加载)。

    不过,感谢您的想法。

    【讨论】:

      【解决方案4】:

      可以加载(仅反射)具有依赖关系的程序集。

      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => Assembly.ReflectionOnlyLoad(e.Name);

      会在加载时修复失败,假设它们都在同一个文件夹中。

      【讨论】:

      • 您能在这里详细说明一个更好的答案吗?听起来你找到了解决办法……但我无法让它发挥作用。
      • 这不是一种解决方法,而是一种有效的解决方案。当在仅反射上下文中无法发现包含所需类型的程序集时,将触发 ReflectionOnlyAssemblyResolve 事件。然后您可以手动加载程序集。
      【解决方案5】:

      如果您正在远程处理或使用代理配置,您将有类似以下内容:

      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => OnReflectionOnlyResolve(e, directory); 
      

      但是,如果您在整个域中遇到异常,它可能看起来像这样:

      {System.InvalidOperationException: 反省是非法的 通过 ReflectionOnlyGetType 加载的类型的自定义属性(参见 Assembly.ReflectionOnly) -- 改用 CustomAttributeData。

      这是触发第一行的代码。

      object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
      return attributes.Length == 0 ? "" : ((AssemblyTitleAttribute) attributes[0]).Title;
      

      您需要将其更改为使用 CustomAttributeData,如下所示:

      foreach (CustomAttributeData cad in assembly.GetCustomAttributesData().Where(a => a.AttributeType == typeof (AssemblyTitleAttribute)))
        return cad.ConstructorArguments.FirstOrDefault().Value as String;
      return String.Empty;
      

      【讨论】:

        【解决方案6】:

        真的老问题,但自 .NET 2.0 以来这已经成为可能。问题是加载到仅反射加载上下文中的所有内容都已实现,因此您不会意外触发将程序集加载到当前 AppDomain 中。反映程序集中定义的自定义属性类型就是其中之一。

        您需要使用CustomAttributeData 来查看这些自定义属性。

        var assy = Assembly.ReflectionOnlyLoadFrom("MuhPlugin.dll");
        // gets assembly-level attributes, but you get the idea
        var attrs = CustomAttributeData.GetCustomAttributes(assy);
        

        CustomAttributeData 实例包含有关属性的信息,包括其类型、构造函数参数和命名参数。请注意,对象图中的类型是仅反射的,因此如果您尝试对它们执行会触发加载程序集的操作,它将引发熟悉的异常。

        您可以使用此关于您的元数据的元数据来确定反射的程序集是否符合您的需求。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-06-18
          • 1970-01-01
          相关资源
          最近更新 更多