0

I have an .net microservice that has a Service A which has an InstancePerLifetimeScope. Service A is injected into MessageService(singleton service).

public class MessageService
{
    private readonly ServiceA serviceA;

    public MessageService(ServiceA serviceA)
    {
        this.serviceA = serviceA;
    }

    public void ProcessItem(ItemClass item)
    {
        serviceA.Proccess(item);
    }
}

In this way ServiceA is becoming a singleton by any thread that calls it. To achieve InstancePerLifetimeScope I would have:

public class MessageService
{
    private readonly IContainer container;

    public MessageService(IContainer container)
    {
        this.container = container;
    }

    public void ProcessItem(ItemClass item)
    {
        using (var scope = container.BeginLifetimeScope())
        {
            scope.Resolve<ServiceA>().Proccess(item);
        }
    }
}

Is this good practice? I read that this is service locator pattern and is considered an antipattern. Also passing container directly into a service as injectable is not the best thing.

public class ServiceAFactory
    {
        private readonly IContainer container;

        public ServiceAFactory(IContainer container)
        {
            this.container = container;
        }

        public IServiceA swapsService CreateService()
        {
            using (var scope = container.BeginLifetimeScope())
            {
                return scope.Resolve<IServiceA>();
            }
        }
    }
pantonis
  • 5,601
  • 12
  • 58
  • 115
  • Read [this](http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) and [this](http://blog.ploeh.dk/2011/08/25/ServiceLocatorrolesvs.mechanics/) to determine what you are doing is okay or not. – Steven Apr 17 '17 at 08:46
  • I read them. It says what most articles I read about service locator as antipattern. How do I overcome my above problem? – pantonis Apr 17 '17 at 08:52

2 Answers2

3

Prevent using the container in your application code and exclusively use it as part of your Composition Root. There are many ways to do this, so there isn't one single answer for this, but typically you want to extract the code that deals with the Container out of your business code.

In your simple example, it seems that all the class does is related to the container, so that means moving it completely to the Composition Root. Since the Composition Root should depend on everything else, while application code should not depend on the Composition Root, it means other code can't call the MessageService. This is easily and elegantly solved by introducing an abstraction: IMessageService:

public class MessageService : IMessageService
{
    ...
}

The interface should be defined at the application level, the implementation inside the Composition Root.

In case MessageService contains business logic as well, things start to get more complicated. You will have separate the business logic from the Container related logic. Here are some ideas how to handle this:

  • Extract the Container stuff out of the MessageService into a new service that is injected into MessageService.
  • Make use of a factory for ServiceA that allows creating a new instance, but do verify whether a factory is the right abstraction.
  • Move the container logic out of MessageService, into a decorator/proxy around ServiceA. This makes lifestyle mismatch oblivious to both ServiceA and MessageService. This involves defining an abstraction around ServiceA, letting MessageService depend on this, and resolve the concrete ServiceA from within the decorator's Proccess method.
Steven
  • 166,672
  • 24
  • 332
  • 435
  • I am using the container as part of the Composition root. Everything is created and resolved there away from the application root. MessageService is a message broker service (thus singleton) which listens on incoming requests. Each request must use its own instance of ServiceA (LifeTimeScope). I already tried the factory to create a lifetime scope but problem is once I called it in MessageService the service is disposed (usings) – pantonis Apr 17 '17 at 09:07
  • Also the MessageService is an application service. I have some more functionaliy there but for simplicity I wanted to show the basic idea of what I am trying to achieve. Service A is a domain service. – pantonis Apr 17 '17 at 09:14
  • @pantonis Your `ServiceAFactory` will not work, because `ServiceA` or one of its dependencies will already be disposed before the factory returns the service. Instead change that factory to become responsible of executing `Proccess` inside the using block, while returning `void`. That's inline with the advise in [this article](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=100). – Steven Apr 17 '17 at 09:32
  • I know it is disposed. Executing Process inside factory violates the factory purpose which is to instantiate a new object (service in this case). – pantonis Apr 17 '17 at 12:24
  • @pantonis: What I am suggesting is to not use a factory. Change its name of that class and the problem is gone. – Steven Apr 17 '17 at 12:26
  • Change name of what class? – pantonis Apr 17 '17 at 12:36
  • Rename ServiceAFactory to ServiceAHandler (or something else) and change its method to `void Process()`. – Steven Apr 17 '17 at 12:38
  • This will cause again to pass IContainer as a dependency and also use service locator pattern. What I am trying to understand is if there is any way to avoid service locator usage when using lifetime scopes – pantonis Apr 17 '17 at 12:50
  • Not when that ServiceAHandler is part of your Composition Root. – Steven Apr 17 '17 at 12:52
  • 1
    I did a major refactoring. I extracted messageservice functionality in the CompositionRoot (think of it like http handler in mvc app) and move all validation, serviceA calls and reply logic to a controller (like web api controllers). Inside messageService I created a lifetime scope for the controller and now I have what I initially needed. – pantonis Apr 17 '17 at 13:38
1

I agree with this other answer in that you should not tie your application code to DI container specifics such as IContainer.

With Autofac, you can inject a factory Func<ServiceA> (instead of a ServiceA) into MessageService. Autofac will auto-generate such a factory function for you as long as you've registered ServiceA with the container.

From Autofac's documentation about the implicit relationship type Func<B>:

"Using an auto-generated factory can let you effectively call [Resolve<B>()] without tying your component to Autofac. […]

"Lifetime scopes are respected using this relationship type. If you register [B] as InstancePerDependency() and call the Func<B> multiple times, you’ll get a new instance each time. However, if you register [B] as SingleInstance() and call the Func<B> to resolve the object more than once, you will get the same object instance every time."

(Slight alterations or omissions by me are put in square brackets.)

Community
  • 1
  • 1
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268