【问题标题】:Dynamically loading classes (with custom behavior) from different assemblies?从不同的程序集中动态加载类(具有自定义行为)?
【发布时间】:2013-02-15 01:54:45
【问题描述】:

我们正在为少数客户构建应用程序,每个客户都有自己的要求以及类似的要求。我们还希望将所有代码保留在同一个应用程序中,而不是对其进行分支,并且 IF 不是好的选择,因为它会遍布各处。

我计划为所有人提供基类。然后每个客户都有自己的类,覆盖方法将在其中执行特殊逻辑。

我们如何在编译时加载程序集而不是这样做

public class BaseClass {
    public string getEventId()
}

public class ClassForJohn:BaseClass {
    [override]
    public string getEventId()
}

public class ClassForAdam:BaseClass {
    [override]
    public string getEventId()
}

void UglyBranchingLogicSomewhere() {
   BaseClass  eventOject;
   if("John"==ConfigurationManager.AppSettings["CustomerName"]){
        eventOject = new ClassForJohn();


   }else if("Adam"==ConfigurationManager.AppSettings["CustomerName"]){
        eventOject = new ClassForAdam();


   }else{
        eventOject = new BaseClass ();

   }
  eventId = eventOject.getEventId();
}

【问题讨论】:

  • MEF 在这里似乎是一个不错的选择。 msdn.microsoft.com/en-us/library/dd460648.aspx
  • 你考虑过使用依赖注入吗?您可以在应用程序启动时根据给定客户配置依赖关系。
  • @jrummell 但是哪个 DI 提供商;和/或,有一个例子? DI 和动态加载是两个不同的东西——这个问题的核心可以用工厂 + 动态加载来回答。那么,回到开头:如何从附属程序集中动态加载类型?
  • 关于如何加载程序集/类型和创建实例:stackoverflow.com/questions/1803540/…stackoverflow.com/questions/1137781/… - 我已经提到这是“最低要求”,尽管 DI 和组合框架可以 帮助整体设计。至少我肯定会推荐 DI,但请参阅这些问题以获得直接答案。

标签: c# .net dynamic-loading


【解决方案1】:

这就是我将插件(加载项)加载到我的一个项目中的方式:

const string PluginTypeName = "MyCompany.MyProject.Contracts.IMyPlugin";

/// <summary>Loads all plugins from a DLL file.</summary>
/// <param name="fileName">The filename of a DLL, e.g. "C:\Prog\MyApp\MyPlugIn.dll"</param>
/// <returns>A list of plugin objects.</returns>
/// <remarks>One DLL can contain several types which implement `IMyPlugin`.</remarks>
public List<IMyPlugin> LoadPluginsFromFile(string fileName)
{
    Assembly asm;
    IMyPlugin plugin;
    List<IMyPlugin> plugins;
    Type tInterface;

    plugins = new List<IMyPlugin>();
    asm = Assembly.LoadFrom(fileName);
    foreach (Type t in asm.GetExportedTypes()) {
        tInterface = t.GetInterface(PluginTypeName);
        if (tInterface != null && (t.Attributes & TypeAttributes.Abstract) !=
            TypeAttributes.Abstract) {

            plugin = (IMyPlugin)Activator.CreateInstance(t);
            plugins.Add(plugin);
        }
    }
    return plugins;
}

我假设每个插件都实现了IMyPlugin。您可以根据需要以任何方式定义此接口。如果循环遍历插件文件夹中包含的所有DLL并调用此方法,则可以自动加载所有可用的插件。

通常您将拥有至少三个程序集:一个包含接口定义,主程序集引用此接口程序集,以及至少一个程序集实现(当然也引用)此接口。

【讨论】:

  • 使用过 MEF 和 StructureMap,我对自己通过反射加载程序集和扩展感到非常满意。这太容易了。我不会向任何人推荐 MEF,除非它对 RYO 装配管理框架来说是一个大问题。
  • 在这种情况下,文件名包含什么?单个 .dll 文件?文件夹名称?
  • DLL 的完整路径,例如"C:\Program Files\MyApp\MyPlugIn.dll".
【解决方案2】:

每个客户都有自己的 exe 和配置文件,并且有一个共享的 dll 吗?还是有共享的exe,每个客户都有自己的dll?

您可以像这样将完整的类型名称放在配置中:

Shared.exe.config:

<appSettings>
  <add key="CustomerType" value="NamespaceForJohn.ClassForJohn, AssemblyForJohn"/>
</appSettings>

并将 AssemblyForJohn.dll 放在与您的 Shared.exe 相同的文件夹中。

然后你可以像这样在代码中动态加载它:

Shared.exe:

var typeString = ConfigurationManager.AppSettings["CustomerType"];
var parts = typeString.Split(',');
var typeName = parts[0];
var assemblyName = parts[1];
var instance = (BaseClass)Activator.CreateInstance(assemblyName, typeName).Unwrap();

【讨论】:

    【解决方案3】:

    也许这个例子会有所帮助

    public MyInterface GetNewType() { 
           Type type = Type.GetType( "MyClass", true ); 
           object newInstance = Activator.CreateInstance( type ); 
           return newInstance as MyInterface; 
        } 
    

    【讨论】:

      【解决方案4】:

      这是使用Unity 处理DI 的一种方法。

      IUnityContainer container = new UnityContainer();
      string customerNamespace = ConfigurationManager.AppSettings["CustomerNamespace"];
      container.RegisterType(typeof(ISomeInterface), 
                             Type.GetType(customerNamespace+".SomeImplementation"));
      
      
      // ...
      
      ISomeInterface instance = conainer.Resolve<ISomeInterface>();
      

      每个客户在客户特定的命名空间中都有自己的ISomeInterface 实现。

      【讨论】:

        【解决方案5】:

        您可以通过这种方式从程序集中创建外部类型的实例:

        object obj = Activator.CreateInstance( 
            "External.Assembly.Name", "External.Assembly.Name.TypeName");
        BaseClass b = (BaseClass) obj;
        b.getEventId();
        

        您将存储程序集的名称并在您的配置文件或其他适当的地方键入。

        【讨论】:

        • 我们正在寻求实现相同类型的情况,即我们在一个程序集中包含“标准”代码,然后通过创建包含子类的客户特定程序集来为某些客户定制它。子类将实现标准程序集的接口。我们计划将组件和类型信息存储在数据库中,以便支持人员可以为每个客户配置它。然后在运行时我们可以加载正确的程序集(如果在数据库中指定)并实例化正确的子类型。
        【解决方案6】:

        我会使用 Unity,但作为一个简单工厂。

        Unity Framework: How to Instantiate two classes from the same Interface?

        你可以存储你的

        我正在使用 Unity.2.1.505.2(以防万一)。

          <configSections>
            <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
          </configSections>
        
        
              <unity>
                <container>
                  <register type="IVehicle" mapTo="Car" name="myCarKey" />
                  <register type="IVehicle" mapTo="Truck" name="myTruckKey" />
                </container>
              </unity>
        

        这是 DotNet 代码。

        UnityContainer container = new UnityContainer();
        
        UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
                        section.Configure(container);
        
        string myKey = "John";  /* read from config file */ /* with this example, the value should be "myCarKey" or "myTruckKey"  */
        
        IVehicle v1 = container.Resolve<IVehicle>(myKey); 
        

        见:

        http://msdn.microsoft.com/en-us/library/ff664762(v=pandp.50).aspx

        http://www.sharpfellows.com/post/Unity-IoC-Container-.aspx

        【讨论】:

        • 你可以存储你的吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多