First, sorry for the vague question title. I couldn\'t come up with a more precise one.
Given these types:
What you are asking is not possible without own coding.
Basically, you are asking the following: If the type I tried to resolve isn't found, return another type that can be converted to it, e.g. if you try to resolve IEnumerable
return a type that is registered for ICollection
.
This is not supported.
One simple solution would be the following:
Register FooCommandHandler
as a handler for ICommandHandler<SpecialFooCommand>
. For this to work, ICommandHandler needs to be contravariant:
interface ICommand { }
class FooCommand : ICommand { }
class SpecialFooCommand : FooCommand { }
interface ICommandHandler<in T> where T : ICommand
{
void Handle(T command);
}
class FooCommandHandler : ICommandHandler<FooCommand>
{
public void Handle(FooCommand command)
{
// ...
}
}
var builder = new ContainerBuilder();
builder.RegisterType<FooCommandHandler>()
.As<ICommandHandler<SpecialFooCommand>>()
.As<ICommandHandler<FooCommand>>();
var container = builder.Build();
var fooCommand = new FooCommand();
var specialCommand = new SpecialFooCommand();
container.Resolve<ICommandHandler<FooCommand>>().Handle(fooCommand);
container.Resolve<ICommandHandler<FooCommand>>().Handle(specialCommand);
container.Resolve<ICommandHandler<SpecialFooCommand>>().Handle(specialCommand);
BTW: The way you are using the container, you apply the Service locator anti-pattern. This should be avoided.
Not really a fair answer, as I've extended Autofac since you posted the question... :)
As per Daniel's answer, you'll need to add the in
modifier to the TCommand
parameter of ICommandHandler
:
interface ICommandHandler<in TCommand>
{
void Handle(TCommand command);
}
Autofac 2.5.2 now includes an IRegistrationSource
to enable contravariant Resolve()
operations:
using Autofac.Features.Variance;
var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
With this source registered, services represented by a generic interface with a single in
parameter will be looked up taking variant implementations into account:
builder.RegisterType<FooCommandHandler>()
.As<ICommandHandler<FooCommand>>();
var container = builder.Build();
container.Resolve<ICommandHandler<FooCommand>>();
container.Resolve<ICommandHandler<SpecialFooCommand>>();
Both calls to Resolve()
will successfully retrieve the FooCommandHandler
.
If you can't upgrade to the latest Autofac package, grab the ContravariantRegistrationSource
from http://code.google.com/p/autofac/source/browse/src/Source/Autofac/Features/Variance/ContravariantRegistrationSource.cs - it should compile against any recent Autofac build.
I like to add an alternative approach, which also works without C# 4.0 variance support.
You can create a special decorator / wrapper that allows executing a command as its base type:
public class VarianceHandler<TSubCommand, TBaseCommand>
: ICommandHandler<TSubCommand>
where TSubCommand : TBaseCommand
{
private readonly ICommandHandler<TBaseCommand> handler;
public VarianceHandler(ICommandHandler<TBaseCommand> handler)
{
this.handler = handler;
}
public void Handle(TSubCommand command)
{
this.handler.Handle(command);
}
}
With this in place, the following line of code would allow you to handle SpecialFooCommand
as its base type:
builder.Register<FooCommandHandler>()
.As<ICommandHandler<FooCommand>>();
builder.Register<VarianceHandler<SpecialFooCommand, FooCommand>>()
.As<ICommandHandler<SpecialFooCommand>>();
Note that the use of such VarianceHandler
works for most DI containers.