ioc-containerdevforce

Ideablade's Cocktail Composition Container for WCF projects


I recently upgraded an application I am working on from Cocktail 1.4 to Cocktail 2.6 (Punch). I have adjusted my bootstrapper class for the wpf project which now loads with no issues. However, on my WCF / Web projects, I am receiving a runtime exception with the following error when attempting to call Composition.GetInstance:

"You must first set a valid CompositionProvider by using Composition.SetProvider."

After digging into the issue a bit, it appears the composition container is automatically configured when your bootstrapper inherits from CocktailMefBootstrapper. I currently do not have bootstrapper classes at all for non-wpf projects. Prior to the upgrade, all I had to do was call the configure method on the Composition class to configure the composition container, but it appears that it has been deprecated:

Composition.Configure();

I noticed that you can also call Composition.SetProvider(), however I am a little unsure on how to satisfy the method signature exactly. The DevForce Punch documentation states that the generic type for the bootstrapper class should be a viewmodel, and there are no views / view models in a service project. This leaves me in limbo on what to do as I don't want to rip cocktail out of these WCF projects. Is there still a way to use Cocktail's composition container without a bootstrapper for a project in Cocktail (Punch) 2.6?

UPDATE

I found this on the DevForce forums. So it appears that I ought to learn how to configure a multi threaded ICompositionProvider and call Composition.SetProvider() as mentioned above. Any recommended articles to achieving this?


Solution

  • After digging through Punch's source code and looking at Ideablade's MefCompositionContainer, which implements ICompositionProvider, I created my own thread safe implementation of ICompositionProvider. Below is the code I used. Basically, it's the same code for Ideablade's MefCompositionContainer which can be found here in their repository. The only change is that I am passing a bool flag of true into the CompositionContainer's constructor. MSDN lists the pros and cons of making the container thread safe

    internal partial class ThreadSafeCompositionProvider : ICompositionProvider
    {
        static ThreadSafeCompositionProvider()
        {
            CompositionHost.IgnorePatterns.Add("Caliburn.Micro*");
            CompositionHost.IgnorePatterns.Add("Windows.UI.Interactivity*");
            CompositionHost.IgnorePatterns.Add("Cocktail.Utils*");
            CompositionHost.IgnorePatterns.Add("Cocktail.Compat*");
            CompositionHost.IgnorePatterns.Add("Cocktail.dll");
            CompositionHost.IgnorePatterns.Add("Cocktail.SL.dll");
            CompositionHost.IgnorePatterns.Add("Cocktail.WinRT.dll");
        }
    
        public IEnumerable<Assembly> GetProbeAssemblies()
        {
            IEnumerable<Assembly> probeAssemblies = CompositionHost.Instance.ProbeAssemblies;
            var t = GetType();
    
            // Add Cocktail assembly
            probeAssemblies = probeAssemblies.Concat(GetType().GetAssembly());
    
            return probeAssemblies.Distinct(x => x);
        }
    
        private List<Assembly> _probeAssemblies;
        private AggregateCatalog _defaultCatalog;
        private ComposablePartCatalog _catalog;
        private CompositionContainer _container;
    
        public ComposablePartCatalog Catalog
        {
            get { return _catalog ?? DefaultCatalog; }
        }
    
        public ComposablePartCatalog DefaultCatalog
        {
            get
            {
                if (_defaultCatalog == null)
                {
                    _probeAssemblies = GetProbeAssemblies().ToList();
                    var mainCatalog = new AggregateCatalog(_probeAssemblies.Select(x => new AssemblyCatalog(x)));
                    _defaultCatalog = new AggregateCatalog(mainCatalog);
    
                    CompositionHost.Recomposed += new EventHandler<RecomposedEventArgs>(OnRecomposed)
                        .MakeWeak(x => CompositionHost.Recomposed -= x);
                }
                return _defaultCatalog;
            }
        }
    
        internal void OnRecomposed(object sender, RecomposedEventArgs args)
        {
            if (args.HasError) return;
    
            var newAssemblies = GetProbeAssemblies()
                .Where(x => !_probeAssemblies.Contains(x))
                .ToList();
    
            if (newAssemblies.Any())
            {
                var catalog = new AggregateCatalog(newAssemblies.Select(x => new AssemblyCatalog(x)));
                _defaultCatalog.Catalogs.Add(catalog);
                _probeAssemblies.AddRange(newAssemblies);
            }
    
            // Notify clients of the recomposition
            var handlers = Recomposed;
            if (handlers != null)
                handlers(sender, args);
        }
    
    
        public CompositionContainer Container
        {
            get { return _container ?? (_container = new CompositionContainer(Catalog, true)); }
        }
    
    
    
        public Lazy<T> GetInstance<T>() where T : class
        {
            var exports = GetExportsCore(typeof(T), null).ToList();
            if (!exports.Any())
                throw new Exception(string.Format("Could Not Locate Any Instances Of Contract", typeof(T).FullName));
    
            return new Lazy<T>(() => (T)exports.First().Value);
        }
    
        public T TryGetInstance<T>() where T : class
        {
            if (!IsTypeRegistered<T>())
                return null;
    
            return GetInstance<T>().Value;
        }
    
        public IEnumerable<T> GetInstances<T>() where T : class
        {
            var exports = GetExportsCore(typeof(T), null);
            return exports.Select(x => (T)x.Value);
        }
    
        public Lazy<object> GetInstance(Type serviceType, string contractName)
        {
            var exports = GetExportsCore(serviceType, contractName).ToList();
            if (!exports.Any())
                throw new Exception(string.Format("Could Not Locate Any Instances Of Contract",
                                                  serviceType != null ? serviceType.ToString() : contractName));
    
            return new Lazy<object>(() => exports.First().Value);
        }
    
        public object TryGetInstance(Type serviceType, string contractName)
        {
            var exports = GetExportsCore(serviceType, contractName).ToList();
            if (!exports.Any())
                return null;
    
            return exports.First().Value;
        }
    
        public IEnumerable<object> GetInstances(Type serviceType, string contractName)
        {
            var exports = GetExportsCore(serviceType, contractName);
            return exports.Select(x => x.Value);
        }
    
        public ICompositionFactory<T> GetInstanceFactory<T>() where T : class
        {
            var factory = new ThreadSafeCompositionFactory<T>();
            Container.SatisfyImportsOnce(factory);
            if (factory.ExportFactory == null)
                throw new CompositionException(string.Format("No export found.", typeof(T)));
    
            return factory;
        }
    
        public ICompositionFactory<T> TryGetInstanceFactory<T>() where T : class
        {
            var factory = new ThreadSafeCompositionFactory<T>();
            Container.SatisfyImportsOnce(factory);
            if (factory.ExportFactory == null)
                return null;
    
            return factory;
        }
    
        public void BuildUp(object instance)
        {
            // Skip if in design mode.
            if (DesignTime.InDesignMode())
                return;
    
            Container.SatisfyImportsOnce(instance);
        }
    
        public bool IsRecomposing { get; internal set; }
    
        public event EventHandler<RecomposedEventArgs> Recomposed;
    
        internal bool IsTypeRegistered<T>() where T : class
        {
            return Container.GetExports<T>().Any();
        }
    
        public void Configure(CompositionBatch compositionBatch = null, ComposablePartCatalog catalog = null)
        {
            _catalog = catalog;
    
            var batch = compositionBatch ?? new CompositionBatch();
            if (!IsTypeRegistered<IEventAggregator>())
                batch.AddExportedValue<IEventAggregator>(new EventAggregator());
    
            Compose(batch);
        }
    
        public void Compose(CompositionBatch compositionBatch)
        {
            if (compositionBatch == null)
                throw new ArgumentNullException("compositionBatch");
    
            Container.Compose(compositionBatch);
        }
    
        private IEnumerable<Lazy<object>> GetExportsCore(Type serviceType, string key)
        {
            return Container.GetExports(serviceType, null, key);
        }
    }
    

    After setting up that class, I added a configuration during startup to instantiate my new thread safe composition provider and to set it as the provider for Punch's Composition class:

            if (createThreadSafeCompositionContainer)
            {             
                var threadSafeContainer = new ThreadSafeCompositionProvider();
                Composition.SetProvider(threadSafeContainer);
            }
    

    Seems to be working like a charm!