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?
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!