patterncsharpModerate
Using reflection to connect to an arbitrary backend
Viewed 0 times
reflectionbackendconnectarbitraryusing
Problem
A little background first - I'm working on a server application that requires the ability to use multiple data access class libraries based on the resources available on the server it is installed on (MySQL, SQL Server, XML, etc.). The are currently implemented as modular plugins, where the executable is paired with the appropriate class library on the server and interacts with it through a defined interface (IDataAccess).
The following code is the constructor for a singleton class on the server that exposes the backend:
```
private IDataAccess mDB;
private MudData()
{
List files = new List(Directory.GetFiles(".", "*.dll"));
List accessors = new List();
foreach (String file in files)
{
try
{
Assembly test = Assembly.UnsafeLoadFrom(file);
System.Type[] types = test.GetTypes();
//See if any of the located types implement IDataAccess.
foreach (System.Type candidate in types)
{
if (candidate.GetInterface("IDataAccess") != null)
{
accessors.Add(candidate);
}
}
}
catch
{
//Eat this on purpose - still need to check the rest of the matches.
}
}
if (accessors.Count == 0)
{
throw new ApplicationException("No supported data access .dll was found.");
}
//TODO: Select from multiples if found, but for now just take the first match.
PropertyInfo target = accessors.First().GetProperty("Instance");
MethodInfo getter = target.GetGetMethod();
//Create a delegate for the reflected assembly so any exceptions propagate correctly.
Func dataDelegate = (Func)Delegate.CreateDelegate(typeof(Func), null, getter);
Object found = dataDelegate();
if (found != null)
{
mDB = (IDataAccess)found;
}
else
{
throw new ApplicationException("Attempt to load an instance of IDataAccess failed.");
}
The following code is the constructor for a singleton class on the server that exposes the backend:
```
private IDataAccess mDB;
private MudData()
{
List files = new List(Directory.GetFiles(".", "*.dll"));
List accessors = new List();
foreach (String file in files)
{
try
{
Assembly test = Assembly.UnsafeLoadFrom(file);
System.Type[] types = test.GetTypes();
//See if any of the located types implement IDataAccess.
foreach (System.Type candidate in types)
{
if (candidate.GetInterface("IDataAccess") != null)
{
accessors.Add(candidate);
}
}
}
catch
{
//Eat this on purpose - still need to check the rest of the matches.
}
}
if (accessors.Count == 0)
{
throw new ApplicationException("No supported data access .dll was found.");
}
//TODO: Select from multiples if found, but for now just take the first match.
PropertyInfo target = accessors.First().GetProperty("Instance");
MethodInfo getter = target.GetGetMethod();
//Create a delegate for the reflected assembly so any exceptions propagate correctly.
Func dataDelegate = (Func)Delegate.CreateDelegate(typeof(Func), null, getter);
Object found = dataDelegate();
if (found != null)
{
mDB = (IDataAccess)found;
}
else
{
throw new ApplicationException("Attempt to load an instance of IDataAccess failed.");
}
Solution
You're loading every DLL in the directory even if you find what you want in the first one.
I'd guess that loading an assembly is what takes more time and resources than looking for what you want in the assembly-after-it's-loaded.
You code will fail if there's a type which implements IDataAccess but which doesn't have an Instance property.
You might want to load into a different AppDomain if you want to unload the assembly which you loaded -- see Why isn't there an Assembly.Unload method?
I'd guess that loading an assembly is what takes more time and resources than looking for what you want in the assembly-after-it's-loaded.
You code will fail if there's a type which implements IDataAccess but which doesn't have an Instance property.
You might want to load into a different AppDomain if you want to unload the assembly which you loaded -- see Why isn't there an Assembly.Unload method?
Context
StackExchange Code Review Q#41524, answer score: 10
Revisions (0)
No revisions yet.