I've been using MEF & Silverlight quite a while. While doing some code review, I went back to my WCF template to see if I could utilize MEF or any other IOC container in place of the ProviderFactory I created.
First I tried with MEF but quickly remembered you can't put parameters in the constructor of a WCF service by default.
So then I started searching for how others have attacked this. After a little searching, all I was turning up were solutions that require quite a bit of code just to get this to work.
It made me realize that what I had was good enough and I thought I would share it with others. The solution is dynamic and objects your BLL classes can be instantiated anywhere, even in the construstor.
Here is the code for a very simple way to provide DI and it is designed to be configurable via a config file.
Code:
public static class ProviderFactory<T>
{
private static volatile Dictionary<string, Dictionary<string, T>> _providerCache = new Dictionary<string, Dictionary<string, T>>();
static object _lock = new object();
#region Private Fields
#endregion
public static T GetProvider(string assemblyName, string className)
{
T _provider = default(T);
try
{
Dictionary<string, T> _existingAssembly = new Dictionary<string, T>();
// Check to see if this assembly has been added to the dictionary before
lock (_lock)
{
if (_providerCache.ContainsKey(assemblyName))
{
_existingAssembly = _providerCache[assemblyName];
// See if className in the assembly has already been loaded, if so assign to _provider to return
if (_existingAssembly.ContainsKey(className))
{
_provider = _existingAssembly[className];
}
else
{
_provider = CreateProvider(assemblyName, className);
_existingAssembly.Add(className, _provider);
}
}
else
{
if (!_existingAssembly.ContainsKey(assemblyName))
{
_provider = CreateProvider(assemblyName, className);
_existingAssembly.Add(className, _provider);
_providerCache.Add(assemblyName, _existingAssembly);
}
}
}
}
catch (Exception ex)
{
throw new Exception(string.Format("ProviderFactory.GetProvider(): {0}", ex.Message), ex);
}
return _provider;
}
/// <summary>
/// Create a provider based on the assemblyName and className passed in. Note the assembly must be in the bin or GAC.
/// </summary>
/// <param name="assemblyName">name of assembly to load</param>
/// <param name="className">class within the assembly to load.</param>
/// <returns></returns>
private static T CreateProvider(string assemblyName, string className)
{
// Create provider specified in config
Assembly assemb = Assembly.Load(new AssemblyName(assemblyName));
if (assemb == null)
{
throw new Exception(string.Format("Could not create instance of {0}", assemblyName));
}
object providerObj = assemb.CreateInstance(className, true);
// See if there's a problem creating the instance
if (providerObj == null)
{
throw new Exception(string.Format("Could not create instance of {0}", className));
}
// Throw an exception if object does not implement IDataProvider
T provider = (T)providerObj;
if (provider == null)
{
throw new Exception(string.Format("{0} does not implement {1}.", assemblyName, className));
}
// Return the provider
return provider;
}
}
To make use of the ProviderFactory, personally, I use the AppSettings of the web.config file to define the assembly name of my BLL and the class inside the assembly.
The entries would look like this.
Code:
<addkey="RoleProviderAssembly"value="Credentials.BLL"/>
<addkey="RoleProviderClass"value="Credentials.BLL.RoleProvider"/>
The Credentials.BLL.RoleProvider implements ICredentialProvider. I use a ProviderContracts Assembly to store the interfaces. Then I set a reference from my BLL as well as my ServiceImplementation to this assembly.
In our ServiceImplementation assembly, we have a class called CredentialService. It implements an ICredentialService interface. The ICredentialService interface defines all of our service Operation Contracts.
Our CredentialService class will look something like this.
Code:
public class CredentialService : ICredentialService
{
ICredentialProvider _provider;
#region ctor
public SmartLynxCredentialService()
{
try
{
_provider = ProviderFactory<ICredentialProvider>.GetProvider(ConfigurationManager.AppSettings["CredentialProviderAssembly"], ConfigurationManager.AppSettings["CredentialProviderClass"]);
}
catch (Exception ex)
{
Log.Error(CredentialHelpers.ExceptionStrings.CredentialSvcException + ex.ToString(), this.GetType().ToString()); }
}
#endregion
}
As you can see, with this factory, you have a way to Dependency Inject any BLL in your service.
Also, after your service is in production, if you ever have the need to refactor your BLL you can dynamically do without any downtime. All you have to do is create a new BLL assembly. Make sure the class in the assembly implements the provider interface. This would be the ICredentialProvider interface in this case. Once you implemented all the endpoints of the interface, and it compiles. You can take the assembly and plug it into the bin folder of the web directory.
If you created an assembly with the same name and class name, then remove the old assembly and put in the new one. (This would require downtime.) If you gave the assembly a new name, then you just update the settings in the web.config file to reflect this.
Now for a scenario where this may be relevant. You decide to start out your BLL and use ADO.net in your DAL. Then Linq To SQL and Entity Framework come out and you're not which sure which one meets your need. No matter which one you pick, if you want to change, you now have the ability to go back, create a new BLL and use whatever technology you like.
Once the new BLL is complete, again you copy it to your bin folder, update the config settings and you're done. You've added a nice level of flexibility to your application.







Section Widget
Category Widget (bottom-up)

Rate this article