2013-02-28 42 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; 
    } 
相關問題