Is there a way I can use Autofac as my primary container for Catel? I've seen that there is support for Unity, Ninject, MEF, Windsor, and Unity, but there is no mention of Autofac integration.
2 Answers
We haven't created an helper for Autofac, nor are we planning to write one.
You have the following options:
1) Write the Helper class yourself (see the others for examples) and register it, then the ServiceLocator can sync it
2) In the latest prerelease of Catel (3.7 prerelease) we have introduced the IDependencyResolver. You can implement this interface on your Autofac container and register that in the ServiceLocator. Note though that we don't have full support for this so it might cause side-effects.
3) Use the ServiceLocator in Catel
edit on 2013-09-14
4) If you are interested in the latest version of Catel (nightly build), you can now replace the default service locator with your implementation:
Issue details: https://catelproject.atlassian.net/browse/CTL-175

- 5,689
- 1
- 18
- 32
-
Just noticed that you enabled this scenario. I'm going to give using Autofac a shot using this method (4). Thanks! – Damian Oct 09 '13 at 17:48
-
The IServiceLocator interface is quite involved, is there any guidance on how to implement the interface or any examples of an implemntation? – Damian Oct 10 '13 at 11:26
-
That's why we introduced the IDependencyResolver which should make stuff easier. In 3.8 all code will use the DependencyResolver instead of the ServiceLocator to retrieve its dependencies so it is easier for you to implement (no need to implement the whole IServiceLocator). – Geert van Horrik Oct 10 '13 at 15:21
-
It would be great to see a blog post when you are done though ;-) – Geert van Horrik Oct 10 '13 at 15:23
-
Any plans on putting 3.8 out on Nuget before the final release? – Damian Oct 10 '13 at 15:45
-
We are still working on 3.7, but I guess we should release that soon. Then I will focus on this feature first. – Geert van Horrik Oct 10 '13 at 17:49
Here is my solution for Catel 3.8 and Autofac (should work for other 3rd party containers too).
I wrote an implementation for IDependencyResolver
backed by an Autofac container and an implementation for IServiceLocator
. The former contains the applications 3rd party IoC configuration and logic. The latter is used to grab all type and instance registrations of Catels modules and foreward them to Autofac.
The basic strategy is
- Create and configure a 3rd party container
- Use it to instanciate (partially customized) Catel IoC components and introduce them to Catels IoC configuration
- Let Catel do its type and instance registration using the previously created (customized) implementations and foreward them to the third party container
So here is my implementation for the IServiceLocator
:
internal class CustomServiceLocator : IServiceLocator
{
private readonly CustomDependencyResolver _dependencyResolver;
public CustomServiceLocator(CustomDependencyResolver dependencyResolver)
{
_dependencyResolver = dependencyResolver;
}
public object GetService(Type serviceType)
{
throw new NotImplementedException();
}
public RegistrationInfo GetRegistrationInfo(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public bool IsTypeRegistered(Type serviceType, object tag = null)
{
return _dependencyResolver.CanResolve(serviceType, tag);
}
public bool IsTypeRegisteredAsSingleton(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public void RegisterInstance(Type serviceType, object instance, object tag = null)
{
var builder = new ContainerBuilder();
IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle> registrationBuilder = builder.RegisterInstance(instance);
if (tag != null)
{
registrationBuilder.Keyed(tag, serviceType);
}
_dependencyResolver.UpdateContainer(builder);
}
public void RegisterType(Type serviceType, Type serviceImplementationType, object tag = null, RegistrationType registrationType = RegistrationType.Singleton,
bool registerIfAlreadyRegistered = true)
{
var builder = new ContainerBuilder();
IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle> registrationBuilder = builder.RegisterType(serviceImplementationType).As(serviceType);
if (tag != null)
{
registrationBuilder.Keyed(tag, serviceType);
}
switch (registrationType)
{
case RegistrationType.Singleton:
registrationBuilder.SingleInstance();
break;
case RegistrationType.Transient:
registrationBuilder.InstancePerDependency();
break;
default:
registrationBuilder.InstancePerDependency();
break;
}
_dependencyResolver.UpdateContainer(builder);
TypeRegistered(this, new TypeRegisteredEventArgs(serviceType, serviceImplementationType, tag, registrationType));
}
public object ResolveType(Type serviceType, object tag = null)
{
// Must be implemented. Catels ViewModelBase resolves the DependencyResolver in ctor using the ServiceLocator...Why???
return _dependencyResolver.Resolve(serviceType, tag);
}
public IEnumerable<object> ResolveTypes(Type serviceType)
{
throw new NotImplementedException();
}
public void RemoveInstance(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public void RemoveAllInstances(Type serviceType)
{
throw new NotImplementedException();
}
public void RemoveAllInstances(object tag = null)
{
throw new NotImplementedException();
}
public bool IsExternalContainerSupported(object externalContainer)
{
throw new NotImplementedException();
}
public void RegisterExternalContainer(object externalContainer)
{
throw new NotImplementedException();
}
public void RegisterExternalContainerHelper(IExternalContainerHelper externalContainerHelper)
{
throw new NotImplementedException();
}
public void ExportInstancesToExternalContainers()
{
throw new NotImplementedException();
}
public void ExportToExternalContainers()
{
throw new NotImplementedException();
}
public bool AreAllTypesRegistered(params Type[] types)
{
return _dependencyResolver.CanResolveAll(types);
}
public object[] ResolveAllTypes(params Type[] types)
{
return _dependencyResolver.ResolveAll(types);
}
public bool AutomaticallyKeepContainersSynchronized { get; set; }
public bool CanResolveNonAbstractTypesWithoutRegistration { get; set; }
public bool SupportDependencyInjection { get; set; }
public bool AutoRegisterTypesViaAttributes { get; set; }
public bool IgnoreRuntimeIncorrectUsageOfRegisterAttribute { get; set; }
public event EventHandler<MissingTypeEventArgs> MissingType;
public event EventHandler<TypeRegisteredEventArgs> TypeRegistered;
}
And here the implementation for IDepencyResolver
.
internal class CustomDependencyResolver : IDependencyResolver
{
private readonly IContainer _container;
public CustomDependencyResolver()
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies());
builder.RegisterInstance(this).SingleInstance(); // dependency of CustomServiceLocator
builder.RegisterInstance(this).As<IDependencyResolver>().SingleInstance(); // Dependency of ViewModelBase. Catels ViewModelBase resolves the DependencyResolver in ctor using the ServiceLocator...Why???
builder.RegisterType<CustomServiceLocator>().As<IServiceLocator>().SingleInstance(); // dependency of TypeFactory (subscribes to TypeRegistered event to clear its cache)
builder.RegisterType<TypeFactory>().As<ITypeFactory>().SingleInstance(); // dependency of ViewModelFactory
_container = builder.Build();
}
public bool CanResolve(Type type, object tag = null)
{
return _container.IsRegistered(type);
}
public bool CanResolveAll(Type[] types)
{
return types.All(type => _container.IsRegistered(type));
}
public object Resolve(Type type, object tag = null)
{
object obj;
if (tag == null)
{
if (_container.TryResolve(type, out obj))
return obj;
}
else
{
if (_container.TryResolveKeyed(tag, type, out obj))
return obj;
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", tag ?? type.Name));
}
public object[] ResolveAll(Type[] types, object tag = null)
{
var objects = new ArrayList();
if (tag == null)
{
foreach (Type type in types)
{
object obj;
if (_container.TryResolve(type, out obj))
{
objects.Add(obj);
}
else
{
throw new Exception(string.Format("Could not locate any instances of contract {0}.", type.Name));
}
}
}
else
{
foreach (Type type in types)
{
object obj;
if (_container.TryResolveKeyed(tag, type, out obj))
{
objects.Add(obj);
}
else
{
throw new Exception(string.Format("Could not locate any instances of contract {0}.", tag));
}
}
}
return objects.ToArray();
}
public void UpdateContainer(ContainerBuilder builder)
{
builder.Update(_container);
}
}
Putting it all together at startup:
public partial class App : Application
{
/// <summary>
/// Raises the <see cref="E:System.Windows.Application.Startup"/> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.StartupEventArgs"/> that contains the event data.</param>
protected override void OnStartup(StartupEventArgs e)
{
#if DEBUG
Catel.Logging.LogManager.AddDebugListener();
#endif
StyleHelper.CreateStyleForwardersForDefaultStyles();
// create the DependencyResolver and do Catel IoC configuration
CustomDependencyResolver dependencyResolver = new CustomDependencyResolver();
DependencyResolverManager.Default.DefaultDependencyResolver = dependencyResolver;
IoCConfiguration.DefaultDependencyResolver = dependencyResolver;
IoCConfiguration.DefaultServiceLocator = dependencyResolver.Resolve<IServiceLocator>();
IoCConfiguration.DefaultTypeFactory = dependencyResolver.Resolve<ITypeFactory>();
// let Catel register its dependencies
Catel.Core.ModuleInitializer.Initialize();
Catel.MVVM.ModuleInitializer.Initialize();
base.OnStartup(e);
}
}
Tradoffs and side effects: In my opinion Catels IoC implementation is a litte bit fuzzy. e.g. Sometimes the ServiceLocator
is used to resolve the DependencyResolver
, sometimes the other way around. I tried to figure out the most common internally used ways and cover them with my solution. A possible side effect may occure, if a type is registered to Autofacs container after application startup and the TypeFactory
is not notified to clear its cache (i did not analyse the TypeFactory
itself). I suggest an implementation for a TypeRegistered event in CustomDependencyResolver
, subscribed by the CustomServiceLocator
to foreward it to the TypeFactory
.

- 2,724
- 31
- 32