Today, I am going to share a sophisticated composition provider which can be used in any enterprise application with MEF, and will handle all complication involved with different object creations.
Lets start with a definition of ICompositionProvider interface, it defines contract for a composition provider. A sophisticated composition provider should atleast following combination of methods and events.
public interface ICompositionProvider
{
IEnumerable<Lazy<T>> GetExports<T>(string contractName);
IEnumerable<Lazy<T>> GetExports<T>();
IEnumerable<Lazy<T,TMetadata>> GetExports<T,TMetadata>();
IEnumerable<Lazy<T>> GetExports<T>(string contractName, bool enableWildCards);
IEnumerable<T> GetExportedValues<T>();
IEnumerable<T> GetExportedValues<T>(string contractName);
IEnumerable<T> GetExportedValues<T>(string contractName, bool enableWildCards);
IEnumerable<T> GetEntitledExportedValues<T>(string contractName, bool enableWildCards);
Lazy<T> GetExport<T>(string contractName);
Lazy<T> GetExport<T>();
void SatisfyImports(object attributedPart);
void Compose(object attributedPart);
event EventHandler FullyComposed;
event EventHandler CompositionFailed;
}
Lets look at a CompositionProvider class, CompositionProvider class implementation looks like
public class CompositionProvider : ICompositionProvider
{
#region Private constants
private const string CLASS_NAME = "CompositionProvider.";
private const int MAX_TICKS = 100;
private const string ENTITLEMENT_ASSEMBLY_NAME =
"EntitlementModule.dll";
private const string INFRASTRUCTURE_ASSEMBLY_NAME =
"Infrastructure.dll";
private const string AUTHENTICATION_ASSEMBLY_NAME =
"AuthenticationModule.dll";
private const string FAILED_COMPOSITION_MESSAGE = "Failed to compose part {0}";
#endregion
#region Variables
public static CompositionProvider _Singleton;
private readonly AggregateCatalog _aggergate = new AggregateCatalog();
internal CompositionContainer Container { get; private set; }
public event EventHandler FullyComposed;
public event EventHandler CompositionFailed;
#endregion
#region Static Properies
/// <summary>
/// It indicates current instance.
/// </summary>
internal static CompositionProvider Singleton
{
get { return _Singleton; }
}
#endregion
#region Static Constructor
/// <summary>
/// Its a static constructor for CompositionProvider class.
/// </summary>
static CompositionProvider()
{
try
{
_Singleton = new CompositionProvider();
}
catch (Exception e)
{
ErrorHandler.HandleError(e, ErrorTypes.CriticalError);
}
}
#endregion
#region Constructor
/// <summary>
/// Its a constructor for CompositionProvider class.
/// </summary>
public CompositionProvider()
{
_aggergate.Catalogs.Add(new AssemblyCatalog(typeof(CompositionProvider).Assembly));
Container = new CompositionContainer(_aggergate);
Compose(this);
}
#endregion
#region Methods
public static void Start()
{
}
/// <summary>
/// It returns a lazy collection of passed type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IEnumerable<T> GetExportedValues<T>()
{
IEnumerable<T> value = GetExportedValues<T>(null, true);
return value;
}
/// <summary>
/// It returns a collection of passed contract name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="contractName"></param>
/// <returns></returns>
public IEnumerable<T> GetExportedValues<T>(string contractName)
{
IEnumerable<T> value = GetExportedValues<T>(contractName, false);
return value;
}
/// <summary>
/// It returns a collection of passed contract name with wild cards.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="contractName"></param>
/// <param name="enableWildCards"></param>
/// <returns></returns>
public IEnumerable<T> GetExportedValues<T>(string contractName, bool enableWildCards)
{
var value = new List<T>();
try
{
if (contractName == null)
{
value = Container.GetExportedValues<T>().ToList();
}
else
{
if (enableWildCards)
{
Container.GetExportedValues<T>(contractName).ToList().ForEach(e => value.Add(e));
Container.GetExportedValues<T>(SystemConstants.SYSTEM_WILDCARD).ToList().ForEach(
e => value.Add(e));
}
else
{
value = Container.GetExportedValues<T>(contractName).ToList();
}
}
}
catch (ImportCardinalityMismatchException importCardinalityMismatchException)
{
ErrorHandler.HandleError(importCardinalityMismatchException,
ErrorTypes.CriticalError);
}
catch (CompositionException compositionException)
{
ErrorHandler.HandleError(compositionException, ErrorTypes.CriticalError);
}
return value;
}
/// <summary>
/// It returns a collection of passed contract name with wildcards.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="contractName"></param>
/// <param name="enableWildCards"></param>
/// <returns></returns>
public IEnumerable<Lazy<T>> GetExports<T>(string contractName, bool enableWildCards)
{
FrontEndTracing.Singleton.LogEntry(CLASS_NAME + " GetExports<T>(string contractName, bool enableWildCards)");
List<Lazy<T>> value = default(List<Lazy<T>>);
try
{
if (contractName == null)
{
value = Container.GetExports<T>().ToList();
}
else
{
if (enableWildCards)
{
Container.GetExports<T>(contractName).ToList().ForEach(e => value.Add(e));
Container.GetExports<T>(SystemConstants.SYSTEM_WILDCARD).ToList().ForEach(e => value.Add(e));
}
else
{
value = Container.GetExports<T>(contractName).ToList();
}
}
}
catch (ImportCardinalityMismatchException e)
{
ErrorHandler.HandleError(e, ErrorTypes.CriticalError);
}
return value;
}
/// <summary>
/// this will work with the entitlements provider to return a list of values for the type provided
/// using an entitlement attribute check for each value returned
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="contractName"></param>
/// <param name="enableWildCards"></param>
/// <returns></returns>
public IEnumerable<T> GetEntitledExportedValues<T>(string contractName, bool enableWildCards)
{
var itemsToReturn = new List<T>();
if (EntitlementProvider == null)
{
throw new NullReferenceException("The EntitlementsProvider is null so this can't work");
}
List<T> items = GetExportedValues<T>(contractName, enableWildCards).ToList();
IEnumerable<Type> types = items.Select(v => v.GetType());
foreach (T item in items)
{
object[] attributes = item.GetType().GetCustomAttributes(typeof (RequiresEntitlementAttribute), true);
IEnumerable<RequiresEntitlementAttribute> requiresEntitlementAttributes =
attributes.Select(a => a as RequiresEntitlementAttribute);
itemsToReturn.Add(item);
}
return itemsToReturn;
}
/// <summary>
/// It returns lazy collection of passed contract name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="contractName"></param>
/// <returns></returns>
public IEnumerable<Lazy<T>> GetExports<T>(string contractName)
{
IEnumerable<Lazy<T>> value = GetExports<T>(contractName, false);
return value;
}
/// <summary>
/// It returns a object of passed type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IEnumerable<Lazy<T>> GetExports<T>()
{
IEnumerable<Lazy<T>> value = GetExports<T>(null, true);
return value;
}
/// <summary>
/// It returns a object of passed contract name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="contractName"></param>
/// <returns></returns>
public Lazy<T> GetExport<T>(string contractName)
{
Lazy<T> value = default(Lazy<T>);
try
{
value = Container.GetExport<T>(contractName);
}
catch (ImportCardinalityMismatchException e)
{
ErrorHandler.HandleError(e, ErrorTypes.CriticalError);
}
return value;
}
/// <summary>
/// It returns an object of passed type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public Lazy<T> GetExport<T>()
{
Lazy<T> value = default(Lazy<T>);
try
{
value = Container.GetExport<T>();
}
catch (ImportCardinalityMismatchException e)
{
ErrorHandler.HandleError(e, ErrorTypes.CriticalError);
}
return value;
}
public IEnumerable<Lazy<T,TMetadata>> GetExports<T,TMetadata>()
{
return this.Container.GetExports<T, TMetadata>();
}
/// <summary>
/// It composes passed object.
/// </summary>
/// <param name="attributedPart"></param>
public void Compose(object attributedPart)
{
if (attributedPart != null)
{
try
{
var batch = new CompositionBatch();
batch.AddPart(attributedPart);
Container.Compose(batch);
}
catch (CompositionException compositionException)
{
ErrorHandelr.HandeError(compositionException);
}
}
}
/// <summary>
/// This method is called when imports are satisfied.
/// </summary>
/// <param name="attributedPart"></param>
public void SatisfyImports(object attributedPart)
{
if (attributedPart != null)
{
try
{
var batch = new CompositionBatch();
batch.AddPart(attributedPart);
Container.SatisfyImportsOnce(batch);
}
catch (CompositionException compositionException)
{
ErrorHandler.HandleError(compositionException, ErrorTypes.CriticalError);
}
}
}
/// <summary>
/// It authenticate current user.
/// </summary>
public void Authenticate()
{
Assembly infraAssm = Assembly.LoadFrom(Constants.CompositionPath + INFRASTRUCTURE_ASSEMBLY_NAME);
if (infraAssm != null)
{
_aggergate.Catalogs.Add(new AssemblyCatalog(infraAssm));
}
Assembly authAssm = Assembly.LoadFrom(Constants.CompositionPath + AUTHENTICATION_ASSEMBLY_NAME);
if (authAssm != null)
{
_aggergate.Catalogs.Add(new AssemblyCatalog(authAssm));
LoginManager.LoginCompleted += OnLoginCompleted;
LoginManager.Login();
}
else
{
MessageBox.Show("Can't find the Authentication Module");
SendFail();
}
}
/// <summary>
/// It sends fail method for logging.
/// </summary>
private void SendFail()
{
if (CompositionFailed != null)
{
CompositionFailed(this, EventArgs.Empty);
}
}
public void OnLoginCompleted(object sender, LoginCompletedEventArgs e)
{
if (e.Message == "Cancel")
{
SendFail();
}
else if (e.ClientInformation.IsAuthenticated)
{
Current.SetClientContext(e.ClientInformation);
Entitle();
}
}
/// <summary>
/// It calls entitlement provider for required keys.
/// </summary>
public void Entitle()
{
Assembly entitleAssm = Assembly.LoadFrom(Constants.CompositionPath + ENTITLEMENT_ASSEMBLY_NAME);
if (entitleAssm != null)
{
_aggergate.Catalogs.Add(new AssemblyCatalog(entitleAssm));
EntitlementProvider.EntitlementsLoaded += OnEntitlementsLoaded;
EntitlementProvider.GetEntitlements();
if (XamlUtility.IsInDesignMode()) return;
}
else
{
MessageBox.Show("Can't find the Entitlements Module");
SendFail();
}
}
/// <summary>
/// Its called when entitlements are satisfied.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
internal void OnEntitlementsLoaded(object sender, EventArgs e)
{
//this._HasEntitlements = true;
List<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
_aggergate.Catalogs.Clear();
var directoryDlls = new DirectoryCatalog(Constants.CompositionPath, "*.dll");
var directoryExes = new DirectoryCatalog(Constants.CompositionPath, "*.exe");
foreach (string loadedFile in directoryDlls.LoadedFiles)
{
Assembly assm = Assembly.LoadFrom(loadedFile);
assemblies.Remove(assm);
_aggergate.Catalogs.Add(new AssemblyCatalog(assm));
}
foreach (string loadedFile in directoryExes.LoadedFiles)
{
Assembly assm = Assembly.LoadFrom(loadedFile);
assemblies.Remove(assm);
_aggergate.Catalogs.Add(new AssemblyCatalog(assm));
}
assemblies.ForEach(a => _aggergate.Catalogs.Add(new AssemblyCatalog(a)));
if (FullyComposed == null)
{
SendFail();
return;
}
FullyComposed(this, EventArgs.Empty);
}
#endregion
}