1

Say, for example, I have many methods for calculating the square root of a number.

One developer gives me his own .dll (maths1.dll), another one gives me his too (maths2.dll) and maybe a third one (maths3.dll).

All of them contains the same class, implementing the same interface.

Assembly 1 Maths1.dll

public class Maths : IMaths {
    public static string Author = "Author1";
    public static SquareRoot(int number) {
        // Implementation method 1
    }
}

Assembly 2 Maths2.dll

public class Maths : IMaths {
    public static string Author = "Author2";
    public static SquareRoot(int number) {
        // Implementation method 2
    }
}

etc. etc.

And I have a console application wich must be aware of all the dlls dynamically at runtime.

Looking for .dll files in code is undesirable.

// DON'T WANT THIS
DirectoryInfo di = new DirectoryInfo("bin");
FileInfo[] fi = di.GetFiles("*.dll");

My idea is to manage them from the app.config file with a custom configuration section.

<configuration>

    <configSections>
        <section name="MathsLibraries" type="MyMathsLibrariesSectionClass, ApplicationAssembly" />
    </configSections>

    <MathsLibraries>
        <Library author="Author1" type="MathsClass, Maths1Assembly" /><!-- Maths1.dll -->
        <Library author="Author2" type="MathsClass, Maths2Assembly" /><!-- Maths2.dll -->
        <Library author="Author3" type="MathsClass, Maths3Assembly" /><!-- Maths3.dll -->
    </MathsLibraries>

</configuration>

Considering I will manually copy the library file Maths1.dll to my application's bin folder.Then, the only thing I would have to do is, add a line to my app.config file in the MathsLibraries section.

I need an example code for the console application's Main, presenting the user all the dynamically linked .dll's and allowing him to calculate the square root of a number with the chosen library.

// NOT WORKING CODE, JUST IDEA OF WHAT IS NEEDED
public static void Main(string[] args) {

    // Show the user the linked libraries
    MathsLibraries libs = MathsLibraries.GetSection();
    Console.WriteLine("Available libraries:");
    foreach (MathLibrary lib in libs.Libraries) {
        Console.WriteLine(lib.Author);
    }

    // Ask for the library to use
    Console.Write("Which do you want to use?");
    selected_option = Console.Read();

    IMaths selected_library;
    // since we don't know wich class would be,
    // declare a variable using the interface we know they al implement.

    // Assign the right class to the variable
    if (selected_option == '1') {
        selected_library = Assembly1.Maths;    
    } else if (selected_option == '2') {
        selected_library = Assembly2.Maths;
    }
    // other options...

    // Invoke the SquareRoot method of the dynamically loaded class
    float sqr_result = selected_library.SquareRoot(100);
    Console.WriteLine("Result is {0}", sqr_result);

    Console.WriteLine("Press Enter key to exit");
    Console.Read();
}

Please, can any one help me in this task of loading assemblies from app.config.
Detailed code would be appreciated.
Thanks!

SamuGG
  • 479
  • 2
  • 8
  • 20
  • This all sounds *very* unrealistic. Creating a user interface where the user could make an informed decision about what assembly is most suitable to get a job done is a UI that's next to impossible to write and use. Don't reinvent this wheel, .NET plug-in frameworks are common. MEF is very common. – Hans Passant Jul 13 '13 at 12:19

3 Answers3

1

Assuming they all implement the same interface (actually the same one, declared in the same assembly, not just the same definition in individual namespaces), you could use dependency injection like ms unity, which can be managed in config file, to register all implementations of this interface, create concrete implementations of all at run time, and execute them.

EDIT

Wrote a sample app, I'll post the meat here, and will provide a link to git hub or something when I get it uploaded.

I have an interface, IMasterInterface, and 3 implementations in separate assemblies 'UnityContainer.MasterImplementation', 'Satellite1.Implementation1' and 'Satellite2.Implementation2'. UnityConfiguration is a console app, and I have referenced unity using NuGet. For convenience, I have configured the build paths of all 3 assemblies to the same Build directory for Debug, so the 2 satellite assemblies are available to the console app.

IMasterInterface has a single method GetResult(): string.

  1. Edit web config with the following:

    
        <configuration>
            <configSections>
                <section name="unity"
                   type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
                         Microsoft.Practices.Unity.Configuration, Version=3.0.0.0,
                         Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            </configSections>
            <unity>
                <typeAliases>
                    <typeAlias alias="IMasterInterface"           type="UnityInjection.IMasterInterface, UnityInjection" />
                    <typeAlias alias="MasterImp"                  type="UnityInjection.MasterImplementation, UnityInjection" />
                    <typeAlias alias="SatelliteOneImplementation" type="Satellite1.Implementation1, Satellite1" />
                    <typeAlias alias="SatelliteTwoImplementation" type="Satellite2.Implementation2, Satellite2" />
                </typeAliases>
                <containers>
                    <container name="containerOne">
                        <types>
                            <type type="IMasterInterface" mapTo="MasterImp" name="Master" />
                            <type type="IMasterInterface" mapTo="SatelliteOneImplementation" name="One" />
                            <type type="IMasterInterface" mapTo="SatelliteTwoImplementation" name="Two" />
                        </types>
                    </container>
                </containers>
            </unity>
        </configuration>
    
  2. Configure the container

    
        //Set up the dependency container
        IUnityContainer myContainer = new UnityContainer();
        var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
        section.Configure(myContainer, "containerOne");
    
  3. Resolve All implementations

    
        //create all implementations of out interface
        var implementations = myContainer.ResolveAll<IMasterInterface>();
    
    
    //call the method we are interested in for all implementations
    foreach (var implementation in implementations)
    {
        Console.WriteLine(implementation.GetResult());
    }
    
  4. Resolve a specific named implementation

    
        //Get a particular one
        var specific = myContainer.Resolve<IMasterInterface>("Master");
        Console.WriteLine(specific.GetResult());
    
  • Can you post any link to see that dependency injection? – SamuGG Jul 13 '13 at 13:46
  • Im on a mobile device, all I can give yoi now is a link to the msdn page on it, there are links to developer notes and code samples. Its perhaps a little overkill, its usually something you do application wide to make things like unit tests easier, but it would work. [unity](http://msdn.microsoft.com/en-us/library/dn170416.aspx) – David Bennington Jul 13 '13 at 15:25
  • I've added a code sample, but the formatting is all wrong! I'll see if I can fix it, if StackOverflow lets you see my email address, email me if you want the project. – David Bennington Jul 13 '13 at 20:22
0

Possible duplicate of C# - Correct Way to Load Assembly, Find Class and Call Run() Method

    var asm = Assembly.LoadFile(@"YourMathAssembly.dll");
   var type = asm.GetType("Maths");
     var sqrRoot = Activator.CreateInstance(Maths) as IMaths;
 if (sqrRoot == null) 
    throw new Exception("broke");
        sqrRoot .SquareRoot(100);
Community
  • 1
  • 1
Ehsan
  • 31,833
  • 6
  • 56
  • 65
0

You can use reflection to load selected library and create instance of required type.

var assembly = Assembly.LoadFrom("selected_math_library.dll");
var types = assembly.GetTypes();

var mathType = (from type in types
                      where type.GetInterface("IMath") != null && !type.IsAbstract
                      select type).ToList();

if (mathType.Count > 0)
{
    IMath math = (IMath)Activator.CreateInstance(mathType);
    // call methods from math

}
SergeyIL
  • 575
  • 5
  • 11