2013-02-28 40 views
3

我们正在为少数客户构建应用程序,每个客户都有自己的需求以及类似的需求。我们还希望将所有代码保留在同一个应用程序中,而不是将其分支,并且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(); 
} 
+2

MEF似乎升这里是个不错的选择。 http://msdn.microsoft.com/en-us/library/dd460648.aspx – spender 2013-02-28 18:29:25

+0

@spender但不可用的预览版4,我敢肯定有“少企业”或“少这种方式”的替代品。 – 2013-02-28 18:30:33

+0

你有没有考虑过使用依赖注入?您可以在应用程序启动时根据给定的客户配置依赖关系。 – jrummell 2013-02-28 18:31:00

回答

1

下面是与DI使用Unity处理的一种方式,我们可以加载的程序集。

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


// ... 

ISomeInterface instance = conainer.Resolve<ISomeInterface>(); 

如果每个客户都有一个客户特定的命名空间自己实施ISomeInterface

1

您可以从装配创建外部类型的实例是这样的:

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

你会存储组件和类型的名称在您的配置文件或其他一些适当的地方。

+0

我们正在寻求实现同一类型的情况,我们在一个程序集中包含“标准”代码,然后通过创建包含子类的客户特定程序集来为某些客户定制它。子类将实现标准程序集的接口。我们计划将汇编和类型信息存储在数据库中,以便支持人员可以为每个客户进行配置。然后在运行时,我们可以加载正确的程序集(如果在数据库中指定)并实例化正确的子类型。 – 2014-12-02 15:36:45

4

是否每个客户都得到他们自己的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(); 
5

我这是怎么加载插件(插件)到我的项目之一:

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并调用此方法,则可以自动加载所有可用的插件。

通常你将有至少三个组件:含有一个接口定义,主组件引用该接口组件和至少一个组件执行(当然引用的)此接口。

+2

使用MEF和StructureMap后,我非常自在地通过反射来加载程序集和扩展。这太简单了。我不向任何人推荐MEF,除非它是RYO装配管理框架的一个大问题。 – IAbstract 2013-02-28 19:39:15

+0

在这种情况下,什么文件名包含?一个.dll文件?文件夹名称? – 2017-11-16 16:08:38

+0

DLL的完整路径,例如'“C:\ Program Files \ MyApp \ MyPlugIn.dll”'。 – 2017-11-16 16:39:28

0

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

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

2

也许这个例子将有助于

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