Resolve Anything
Ward Bell, one of the most diligently pragmatic programmers out there, just wrote about bootstrapping Prism with containers other than Unity.
One of the pain points that Ward observes is Prism’s use of “resolve anything” - a Unity feature that causes the container to act as though all concrete types are registered.
// Works, even though Foo isn't registered,
// because Foo is concrete.
var container = new UnityContainer();
var foo = container.Resolve<Foo>();
Other containers have different behaivours - by default, Autofac will throw ComponentNotRegisteredException
in this case.
I’m not a fan of Unity’s behaviour - I rarely use concrete types as services, and generally would want to configure them first anyway.
However, for those with different views, there are simple ways to make this work with Autofac.
Autofac 1.4 - RegisterTypesMatching()
Autofac 1.4 includes a configuration method that can help here. The parameter is a predicate on a requested Type
, and if the result is true
the container will happily resolve instances of the type.
var builder = new ContainerBuilder();
builder.RegisterTypesMatching(t => t.IsClass && !t.IsAbstract);
var container = builder.Build();
// Now works
var foo = container.Resolve<Foo>();
Autofac 2.1 - ResolveAnythingSource
Autofac 2 doesn’t have an equivalent to RegisterTypesMatching()
. This is because Autofac 2’s assembly scanner, RegisterAssemblyTypes()
does a much better job in similar scenarios.
Those who’ve been following along with this blog lately will have seen how registration sources provide a simple way to extend the container to understand new component types (like Lazy Dependencies.)
Well, the registration source to implement “resolve anything” is just a few lines, included here in its entirety. If you need this behaviour, feel free to include it in your project.
class ResolveAnythingSource : IRegistrationSource
{
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var ts = service as TypedService;
if (ts != null && !ts.ServiceType.IsAbstract && ts.ServiceType.IsClass)
{
var rb = RegistrationBuilder.ForType(ts.ServiceType);
return new[] { RegistrationBuilder.CreateRegistration(rb) };
}
return Enumerable.Empty<IComponentRegistration>();
}
}
Configuring the container to use the new source is easy.
var builder = new ContainerBuilder();
builder.RegisterSource(new ResolveAnythingSource());
var container = builder.Build();
// Works now
var foo = container.Resolve<Foo>();
One Small Caveat
You should probably be aware of one small gotcha that both of these samples exhibit. If you actually do register an implementation of Foo
, then resolve all instances of Foo
, you’ll find that both your registered component, plus the automatically-generated one, will be there. This is a very unlikely situation, but it might pay to keep it in mind.
If you found your way here looking for Autofac’s Prism integration, good news is right around the corner! The source for AutofacContrib.Prism is in the trunk, and just needs a few updates before it will get a proper release.