1

I've read a lot on the subject of IoC containers and particularly Mark Seemann's blog posts, where he emphasises the importance of convention over configuration. I understand and agree with his point, but I wonder how best to mark out a class for an irregular lifetime?

For example, most of my services are registered with a Transient lifetime - but I want a particular service to be registered as a Singleton because it does some useful caching.

I have thought about using some custom attributes, but I have read arguments against this (because it puts composition logic in the class).

I have had this same debate with, for example, dependencies on configuration primitives. Ultimately I ended up using a parameter attribute because it worked in my case, but I feel that perhaps I didn't see the 'hidden dangers'.

Alex
  • 7,639
  • 3
  • 45
  • 58

1 Answers1

2

It is common for an IoC container to allow you to specify the lifestyle of an object. SimpleInjector, for example, provides Transient and Singleton out of the box (and also allows the user to create a custom lifestyle class). With SimpleInjector it's as simple as:

container.Register<ISvc, Impl>(Lifestyle.Singleton);

or

container.Register<ISvc, Impl>(Lifestyle.Transient);

No need for polluting your classes with composition logic.

As for configuration primitives, a common practice is to put those behind an interface. I put my connection string behind an IDatabaseConfiguration interface, for example, and its implementation reads the value from web.config. I then inject that interface into my data services.

EDIT: Disregard above. Keeping it in so that comments make sense.

The following doesn't actually answer the question, but it may solve the problem that prompted the question. You said that the class does some useful caching, which clues me in on that it is taking on an additional responsibility. I would therefore recommend that you do not attempt to register that single implementation as a singleton, but instead create a decorator class around that implementation.

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(CommandHandlerCacheDecorator<>), 
    Lifestyle.Transient, 
    x => { return /*some logic that looks for and 
                    finds that one class you want 
                    to decorate*/
         });

The decorator needs to be a transient, because it will have a transient reference to the command it decorates. However, it could then access a separate singleton class (via dependency injection of course) or cache object that handles the actual caching.

EDIT: Examples to single out the one class to decorate. There are plenty of options depending on just how specific you want to be. Again this is with SimpleInjector, but I'm sure other containers have their own analogues.

//decorate a specific class
x => { return x.ImplementationType.FullName == "My.Commands.Web.SomeName"; }

//decorate all classes that share a certain namespace
x => { return x.ImplementationType.Namespace.EndsWith("Commands.Web"); }

//decorate all classes that implement the same interface
x => { return x.ImplementationType.IsInstanceOfType(typeof(ICouldCache)); }
Facio Ratio
  • 3,373
  • 1
  • 16
  • 18
  • 1
    I don't think this answers the question, since your code is no example of *convention over configuration* (CoC). An example of CoC is this: `container.RegisterManyForOpenGeneric(typeof(IValidator<>), assembly)`. Here all validators are registered in one line, using the convention that they implement a certain interface. And this example directly shows the problem, since now all types are registered as transient. But what if one of those validators must be registered as singleton? That's what the question is about. – Steven Nov 12 '13 at 18:04
  • Thanks Steven for clearing that up and thanks Facio for your insight, I think you are entirely right in that ultimately this is a singleton decorator of a class that could be transient. However I'm still not sure of how to mark out this one class as decorated without being explicit - perhaps there is no suitable answer! For thie time being, I'll leave it open in the hope that there is. – Alex Nov 13 '13 at 13:55
  • I added a few examples. In the end, if you can explain in human language what you want, you should be able to come up with a convention for it. – Facio Ratio Nov 13 '13 at 17:57