Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically loading classes (with custom behavior) from different assemblies?

We are building an app for few customers, each has its own requirements along with the similar ones. We also want to keep all the code in the same app, not branch it, and the IFs is not good choice since it will be all over places.

I plan having the base classes for all. Then each customer will have its own class in which the override methods will do special logic.

How can we load the assemblies when compiling instead of doing this

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();
}
like image 773
mycoffee Avatar asked Dec 09 '22 17:12

mycoffee


2 Answers

This is how I load plug-ins (add-ins) into one of my projects:

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

I assume that each plug-in implements IMyPlugin. You can define this interface any way you want. If you loop through all DLLs contained in a plug-ins folder and call this method, you can automatically load all the available plug-ins.

Usually you would have at least three assemblies: One containing the interface definition, the main assembly referencing this interface assembly and at least one assembly implementing (and of course referencing) this interface.

like image 161
Olivier Jacot-Descombes Avatar answered Dec 11 '22 09:12

Olivier Jacot-Descombes


Does each customer get their own exe and config file, and there is a shared dll? Or is there a shared exe, and each customer has their own dll?

You can put the full type name in the configuration like this:

Shared.exe.config:

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

And put AssemblyForJohn.dll in the same folder as your Shared.exe.

Then you can load it dynamically in code like this:

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();
like image 42
Steven Padfield Avatar answered Dec 11 '22 09:12

Steven Padfield