26

I have some classes like UserQuery, CustomerQuery implementing interfaces like IUserQuery, ICustomerQuery respectively. In my binding configuration I need to bind every interface with the respectively query:

builder.RegisterType<UserQuery>().As<IUserQuery>().InstancePerRequest();
builder.RegisterType<CustomerQuery>().As<ICustomerQuery>().InstancePerRequest();

This is working pretty fine, but I was wondering if there is a way to make a convention-based binding in place of binding every single classe XXX[Query] -> [I]XXX[Query].

I'm using AutoFac as my DI container.

3 Answers3

37

I'm not a AutoFac experienced user. However after some research a tested the code below with success:

var assembly = Assembly.GetExecutingAssembly();

builder
    .RegisterAssemblyTypes(assembly)
    .AssignableTo<IQuery>()
    .AsImplementedInterfaces()
    .InstancePerRequest();

The IQuery interface above is just a tag interface that should be inherited from every query interface that you have. Using your example:

Interfaces

IUserQuery: IQuery
ICustomerQuery: IQuery

Classes

UserQuery: IUserQuery
CustomerQuery: CustomerQuery
gustavodidomenico
  • 4,640
  • 1
  • 34
  • 50
  • This is a clean way. But It is a common pratice to have an empty interface? –  Jan 30 '13 at 12:33
  • 4
    @Matt yes, it is a common partice to have an empty marker interface. So if you own your IUserQuery and ICustomerQuery you can and definitely should use a marker interface which makes the container registration cleaner. But sometimes you cannot have this option when you implementing third party interfaces which you cannot change. – nemesv Jan 30 '13 at 12:38
  • 1
    There are a couple of patterns that you can research that illustrates the use of empty interfaces (I remember the Marker pattern right now). I'm particulary comfortable with empty interfaces, if there is a purpose of course. – gustavodidomenico Jan 30 '13 at 12:39
  • 2
    Note that if you're using FxCop they'll nail you on marker interfaces. They'll recommend using attributes (and I do, too) so the metadata (the stuff you're querying on) stays metadata. You can use a .Where() clause to query for the custom attributes rather than using AssignableTo. – Travis Illig Jan 31 '13 at 15:56
11

You need to use the assembly scanning feature of Autofac.

If your interfaces or your implementations are sharing a base class (e.g QueryBase) or a base interface (e.g. IQuery) then you can use the convince methods for the registration like: AssignableTo<>, etc. see gustavodidomenico's answer.

However sometimes you can't have a common base interface/class in that case you can use the Where method where you can have any custom logic for detecting which classes should be registered and how.

In that case your registration should roughly look like this:

builder.RegisterAssemblyTypes(yourAssembly)
       .Where(t => t.IsClass && t.Name.EndsWith("Query"))
       .As(t => t.GetInterfaces().Single(i => i.Name.EndsWith(t.Name)));

It will register all the types from a given assembly which name ends with "Query" with the matching interface, so it will register SomeNiceQuery with the interface ISomeNiceQuery

Community
  • 1
  • 1
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • 4
    Great info, but the condition `Single(i => i.Name.EndsWith(t.Name))` allows registering `Sample` as `ISomethingSample`. To be more exact and only allow `ISample`, something like `Single(i => i.Name == "I" + t.Name)` or `Single(i => i.Name.Length == t.Name.Length + 1 && i.Name.EndsWith(t.Name))` would work. – Nelson Rothermel Nov 11 '13 at 02:23
0

Alternatively, you can just register each type as either it's first implemented interface or it's own self.

builder.RegisterAssemblyTypes(new[] { Assembly.GetAssembly(typeof(IUserQuery))})
        .Where(x=> x.IsClass && x.Name.EndsWith("Query"))
        .As(t => t.GetInterfaces().Any() ? t.GetInterfaces()[0] : t);
arviman
  • 5,087
  • 41
  • 48