Showing posts with label MEF. Show all posts
Showing posts with label MEF. Show all posts

Wednesday, May 9, 2012

MEF : Beyond Export and Import

I am going to describe some of advanced features of MEF

1) Container.SatisfyImportOnce Or Container.ComposeParts methods satisfies import/export only when they are called, any further changes in catalogs doesn't impact old created object.
eg.
AssemblyCatalog catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
CompositionContainer container = new CompositionContainer(catalog);
container.SatisfyImportsOnce(this);
 
2) AllowRecomposition attribute is used to stop any changes in old composed objects if there is any changes in Container's catalog items.
eg.
[ImportMany(AllowRecomposition = false)]
public IEnumerable<ICustomType> Strings { get; set; } 


3) PartCreationPolicyis used to specify whether to create a Singleton instance of required object or multiple instance based on request. You can RequiredCreattionPolicy attribute to specify PartCreationPolicy for a particular type.

eg. Following line will give error as it will fail to satisfy PartCreationPolicy.
[Export, PartCreationPolicy(CreationPolicy.NonShared)] public class CustomClass {..}
[Import(RequiredCreationPolicy = CreationPolicy.Shared)] CustomClass _inst1;


4) AllowDefault attribute can be used when you don't have any export to satisfy for a required import. If you dont set this attribute as true, you will get an exception if there is no export to satisfy an import.

5) InhertitedExport attribute can be used with a base class / interface to specify that all its child classes will export base-class or interface types.

eg. In following example Type1 and Type2 will export ICustomType as ICustomType is marked with InheritedExport attribute.

    [InheritedExport(typeof (ICustomType))]
    public interface ICustomType
    {
    }
 
    public class Type1 : ICustomType {}
    public class Type2 : ICustomType {}
 
6) You can implement IPartImportsSatisfiedNotification interface for your class, 
when you want to be notified about Imports satisfied event.
 
7) You can use ImportingConstructor attribute, if you are using parametrized constructors 
and have a constructor parameter which can which needs to be imported. 

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 { 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
 
    }