Reputation: 479
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!
Upvotes: 1
Views: 2704
Reputation: 356
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.
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>
Configure the container
//Set up the dependency container
IUnityContainer myContainer = new UnityContainer();
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Configure(myContainer, "containerOne");
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());
}
Resolve a specific named implementation
//Get a particular one
var specific = myContainer.Resolve<IMasterInterface>("Master");
Console.WriteLine(specific.GetResult());
Upvotes: 1
Reputation: 595
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
}
Upvotes: 0
Reputation: 32711
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);
Upvotes: 0