Resolving components by best constructor

12 views
Skip to first unread message

mthird

unread,
Dec 31, 2009, 10:41:27 PM12/31/09
to Autofac
I've wrapped my brain into knots trying to figure out the best way to
do this in Autofac 1. Below is my current approach.

I am working on a ServiceBus implementation that uses RabbitMQ for
transport based on Contract-First principals. As such, a particular
command has corresponding request and response classes. All of the
commands are registered anonymously (not named) with the container as
ICommand.

The client submits a request but doesn't know which command class can
handle the request, so I was hoping the container could find the
instance within a set of ICommand registrations that would match.

using (IContainer inner = _container.CreateInnerContainer
())
{
var operation = inner.Resolve<ICommand>(new
TypedParameter(requestType, request));
object response = operation.Execute();
}

The above fails because it picks the default ICommand implementation
and attempts to inject the parameter. This only works when the
default ICommand's ctor matches. I saw another post that registers
types that close a generic interface which is close. Perhaps another
pattern would work better?

-- relevant code snippets
public interface ICommand
{
object Execute();
}

public interface ICommand<T1,T2> : ICommand
{
T2 Execute(T1 request);
}

public abstract class BaseCommand<T1, T2> : ICommand<T1, T2>
{
protected T1 _request;

protected BaseCommand(T1 request)
{
_request = request;
}

public object Execute()
{
return Execute(_request);
}

public abstract T2 Execute(T1 request);
}

public class GetJobDetailsCommand :
BaseCommand<GetJobDetailsRequest, GetJobDetailsResponse>
{
public GetJobDetailsCommand(GetJobDetailsRequest request) :
base(request)
{
}

public override GetJobDetailsResponse Execute
(GetJobDetailsRequest request)
{
return new GetJobDetailsResponse();
}
}

Alex Meyer-Gleaves

unread,
Jan 1, 2010, 9:53:31 AM1/1/10
to Autofac
Do you know what all of your ICommand implementation types are in
advance, or will you need to discover the implementation types at
runtime based on the interface?

mthird

unread,
Jan 4, 2010, 8:49:50 AM1/4/10
to Autofac
Ideally I would discover them at runtime.

On Jan 1, 9:53 am, Alex Meyer-Gleaves <alex.meyerglea...@gmail.com>
wrote:

Nicholas Blumhardt

unread,
Jan 4, 2010, 4:12:18 PM1/4/10
to aut...@googlegroups.com
Michael, sounds to me like the essence of your problem is mapping the request types to their handlers, correct?

The best current approach is probably the ASP.NET MVC IControllerFactory way - use named registrations for the request types (e.g. "handler.FooRequest") and register a 'router' as ICommand:

// (in the real world need to automate the translation of types -> names)

builder.Register<FooCommand>().Named("handler.Foo");
builder.Register<BarCommand>().Named("handler.Bar");
builder.Register((c, p) => c.Resolve<ICommand>("handler." + p.TypedAs<Type>().Name)).ExternallyOwned();

Resolving ICommand and passing the request type as a typed parameter should now route to the correct component.

Make sense?


2010/1/4 mthird <mth...@gmail.com>
--

You received this message because you are subscribed to the Google Groups "Autofac" group.
To post to this group, send email to aut...@googlegroups.com.
To unsubscribe from this group, send email to autofac+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/autofac?hl=en.



mthird

unread,
Jan 7, 2010, 7:58:05 AM1/7/10
to Autofac
Thanks, everyone. This is the approach I went with (forgot about
using the compound lambda syntax!).

On Jan 4, 4:12 pm, Nicholas Blumhardt <nicholas.blumha...@gmail.com>
wrote:

> > autofac+u...@googlegroups.com<autofac%2Bunsu...@googlegroups.com>

Alex Meyer-Gleaves

unread,
Jan 7, 2010, 11:18:36 AM1/7/10
to Autofac
Hi Michael,

If you still want to discover them at runtime and are able to use the
super cool Autofac 2, using the AsClosedTypesOf method to discover
your closing types, and registering them as a named service would be
possible.

This example uses the type name of the request as the service name, as
this is the only useful information that you seem to have when the
request comes in. It assumes that you have added an IRequest interface
to your request types so they can be identified accurately.

[Test]
public void RequestParameterIsIRequest()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(typeof(ICommand<,>).Assembly)
.AsClosedTypesOf(typeof(ICommand<,>))
.Named(type => (from c in type.GetConstructors()
from p in c.GetParameters()
from i in p.ParameterType.GetInterfaces()
where i == typeof(IRequest)
select p.ParameterType).First().FullName);

IContainer container = builder.Build();

// The request would be the one coming from the client.
GetJobDetailsRequest request = new GetJobDetailsRequest();

TypedParameter parameter = new TypedParameter(request.GetType(),
request);
ICommand command = container.Resolve<ICommand>(request.GetType
().FullName, parameter);

Assert.That(command, Is.InstanceOf<GetJobDetailsCommand>());
Assert.That(command.Execute(), Is.InstanceOf<GetJobDetailsResponse>
());
}

A similar approach would be to use the ordinal position of the request
parameter instead of the interface if you are sure that it is the
first parameter. This makes a lot of assumptions though and would be
very fragile!

[Test]
public void RequestParameterIsFirstParameter()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(typeof(ICommand<,>).Assembly)
.AsClosedTypesOf(typeof(ICommand<,>))
.Named(type => (from c in type.GetConstructors()
from p in c.GetParameters()
select p.ParameterType).First().FullName);

IContainer container = builder.Build();

// The request would be the one coming from the client.
GetJobDetailsRequest request = new GetJobDetailsRequest();

TypedParameter parameter = new TypedParameter(request.GetType(),
request);
ICommand command = container.Resolve<ICommand>(request.GetType
().FullName, parameter);

Assert.That(command, Is.InstanceOf<GetJobDetailsCommand>());
Assert.That(command.Execute(), Is.InstanceOf<GetJobDetailsResponse>
());
}

If possible I would go with the interface approach as it is much more
reliable. You could also update your interface and abstract class to
include IRequest and IReponse as type parameter constraints. Your
types would end up looking something like this.

public interface ICommand
{
object Execute();
}

public interface IRequest
{
}

public interface IResponse
{
}

public interface ICommand<TReponse, TRequest> : ICommand
where TReponse : IRequest
where TRequest : IResponse
{
TRequest Execute(TReponse request);
}

public abstract class BaseCommand<TRequest, TResponse> :
ICommand<TRequest, TResponse>
where TRequest : IRequest
where TResponse : IResponse
{
protected TRequest Request;

protected BaseCommand(TRequest request)
{
Request = request;
}

public object Execute()
{
return Execute(Request);
}

public abstract TResponse Execute(TRequest request);
}

public class GetJobDetailsResponse : IResponse
{
}

public class GetJobDetailsRequest : IRequest
{
}

public class GetJobDetailsCommand : BaseCommand<GetJobDetailsRequest,
GetJobDetailsResponse>
{
public GetJobDetailsCommand(GetJobDetailsRequest request) : base
(request)
{
}

public override GetJobDetailsResponse Execute(GetJobDetailsRequest
request)
{
return new GetJobDetailsResponse();
}
}

You could also use BaseCommand<> as the open generic type in the
AsClosedTypesOf method if you prefer. There might be other ways of
doing this but that is what has comes to mind.

Cheers,

Alex.

Reply all
Reply to author
Forward
0 new messages