Thursday, April 19, 2012

MEF : A sophisticated CompositionProvider

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 { getprivate 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>(nulltrue);
            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>(nulltrue);
            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(thisEventArgs.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(thisEventArgs.Empty);
        }
 
        #endregion
 
    } 

No comments:

Post a Comment