4

I have a solution with several projects (MyApp.Data, MyApp.BLL, MyApp.Web). I register types in Global.asax file in MyApp.Web (main web application):

builder.RegisterType<SomeService1>().As<ISomeService1>().InstancePerHttpRequest();
builder.RegisterType<SomeService2>().As<ISomeService2>().InstancePerHttpRequest();
//...etc

And I wonder whether it's a bad practice to register types and their scope using attributes in the other assemblies (for example, in MyApp.BLL). See below:

[Dependency(typeof(ISomeService1), ComponentLifeStyle.Transient)]
public class SomeService1 : ISomeService1
{
   //methods and properties go here
}
Andrei M
  • 3,429
  • 4
  • 28
  • 35
  • Recent post that might be useful in helping you think around the problem: andreasohlund.net/2011/02/20/container-abstractions-in-nservicebus/ – Ruben Bartelink Feb 21 '11 at 14:50

2 Answers2

4

Using local attributes or other ways to indicate wiring for a DI Container tightly couples the service to the DI Container, so I don't think that's a good idea.

Additionally, it may constrain your future options. If, for example, you specify the lifestyle scope, you can't reuse the service with a different scope.

In general, you should compose the application in a Composition Root (global.asax), which gives you a single location with a clearly defined responsibility where all classes are composed.

That would be much more manageable and maintainable that spreading the configuration data all over your classes.

Community
  • 1
  • 1
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • If you insist on using the component in a different scope, You can re-register it (override) in global.ashx with a different scope. – Paul Knopf Feb 22 '11 at 13:57
  • But that's an Autofac-specific feature, so on the architectural level, that argument doesn't apply. You'd still have a tight coupling to Autofac. – Mark Seemann Feb 22 '11 at 14:36
  • Using attributes like this is coupling but you can leave or delete attributes without any negative effects. You can introduce another IOC tool and still leave the attributes there. If the attributes are written sensibly, there's a separate assembly for them which just describe metadata - it's like XML. It's coupling of-a-kind, but it's not in the same league as writing code that calls into functionality of a DI container. I think I'd call it "metadata coupling" or something. You only get half-a-point deducted... it's a lesser crime ;-) – PandaWood Jul 15 '11 at 05:27
3

As your question implies, it makes some sense to delegate responsibility for registration to the assembly that knows what needs to be registered. For example, if you use the SolrNet library, it provides a method that performs component registration, to encapsulate the knowledge of what needs to be registered and to spare the library's consumer from having to learn all about the library before getting started.

However, there is a potential issue with this approach. Would your registration requirements change if you used the dependent assemblies in other applications? For example, would it make sense to register something as ComponentLifeStyle.HttpRequestScoped and then use it in a non-Web application? By delegating registration to the dependency, you are coupling the dependency to its consumer's registration requirements (and to its choice of IoC container).

Autofac (I can't speak for other IoC containers) provides a way round this. It enables you to override registrations so that the most recently registered component is used when a service is resolved. This means that you can call a library's registration method and then register your own services to override the defaults.

There is another problem with your proposed attribute-based registration - it doesn't enable you to specify a lambda expression as a component creator. How would you implement a registration like this with attributes?

builder.Register(c => new A(c.Resolve<B>()));

It might be preferable to define an IRegistrar interface, and then use reflection to search all loaded assemblies for implementations and invoke them. Perhaps something like this:

public interface IRegistrar
{
    void RegisterComponents();
}
Rich Tebb
  • 6,806
  • 2
  • 23
  • 25
  • Thanks for reply. We're usign Autofac so we can always override previously registered dependencies later. – Andrei M Feb 21 '11 at 09:02
  • Regarding ComponentLifeStyle.HttpRequestScoped. We have ComponentLifeStyle.LifetimeScope which creates instance per HTTP Request (builder.InstancePerHttpRequest) in case we're in web application and creates instance per lifetime scope (builder.InstancePerLifetimeScope) in case we're in non-web application – Andrei M Feb 21 '11 at 09:04