The full mix: Autofac, Caliburn.Micro, Silverlight and testing
It is time to finish putting all the bits together. Once this is done, I can start building out the main application functionality again.
To recap, I've added Caliburn.Micro and the Silverlight Test Project (along with the CI build). The last piece is the inversion of control (IOC) container. I'm a fan of StructureMap, but alas there is no Silverlight version, so I'm going to go with Autofac.
This was probably this simplest part of the project so far. Caliburn.Micro comes with an example project of using MEF, so it was literally just a matter of substituting the various parts with calls into Autofac.
Caliburn.Micro Bootstrapper Overrides
So in the AppBootstrapper class we first override the Configure method to constructor the Autofac container and register our types accordingly:
protected override void Configure()
{
var builder = new ContainerBuilder();
builder
.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.SingleInstance()
.AsImplementedInterfaces()
.AsSelf();
container = builder.Build();
}
This is currently a very simple registration process. I may well tweak this later to use more specific conventions, but this just says find all types and register them all with the container and also register the interface implementations - that is for my types where I'm implementing the IAppWidget interface, when querying the container for the IAppWidget implementations, return all in the current assembly.
The next method is the longest - retrieve an object from the container which has a specific type and, optionally, a key:
protected override object GetInstance(Type serviceType, string key)
{
if (string.IsNullOrWhiteSpace(key))
{
if (container.IsRegistered(serviceType))
return container.Resolve(serviceType);
}
else
{
if (container.IsRegisteredWithName(key, serviceType))
container.ResolveNamed(key, serviceType);
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", key ?? serviceType.Name));
}
This is pretty self-explanatory: if there is no key provided, use the Autofac method container.Resolve, otherwise use the container.ResolveNamed method.
Finally, one more override for getting multiple objects from the container which satisfy a given type:
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return container.Resolve(typeof(IEnumerable<>).MakeGenericType(serviceType)) as IEnumerable<object>;
}
This is a little funky because of the lack of generics on the method override, which makes the call into Autofac a bit awkward.
Deleting code
The last thing to do is simplify the ShellViewModel class. I can remove the whole PopulateWidgets method and change the constructor to take the collection of Widgets which Autofac finds:
public ShellViewModel(IEnumerable<IAppWidget> widgets)
{
Widgets = new BindableCollection<IAppWidget>(widgets);
ActivateWidget(Widgets.FirstOrDefault(a => a.Name == "Library"));
}
I also updated the AppBootstrapperTest to make sure Autofac can instantiate the ShellViewModel class. This is green, my CI build is good, and the app still works. Nice.
