5

I have a application that uses IOC and DI to create and inject services.

I have a service layer that handles some business logic, in the service layer I have a repository that communicates with the database. That repository is using a DataContext which is not thread safe.

I want to run some functions on the service asynchronously using background tasks but know that this will cause issues with the repository. Thus I want the repository to be created for every background thread created. How is this achieved? I'm using StructureMap as the IoC.

public class Service : IService
{
    IRepository _repository;

    public Service(IRepository repository)
    {
        this._repository = repository;
    }

    public void DoSomething()
    {
        // Do Work
        _repository.Save();
    }
}


public class Controller
{
    IService _service;

    public Controller(IService service)
    {
        this._service = service;
    }

    public Action DoSomethingManyTimes()
    {
       for(int i =0; i < numberOfTimes; i++)
       {
           Task.Factory.StartNew(() =>
           {  
               _service.DoSomething();
           });
       }
    }
}
Chad Kapatch
  • 585
  • 1
  • 4
  • 16
  • Related: http://stackoverflow.com/questions/14591422/why-is-perthreadlifetimemanager-used-in-this-example – Steven Apr 04 '14 at 06:38

1 Answers1

7

Some DI Containers (e.g. (IIIRC) StructureMap) actually support Per Thread lifetime styles, but that's probably not going to help you, because it would inject IService into Controller on one thread, and then use it on a number of other threads.

What I would suggest is to leave the Controller implementations as is, because the fact that a particular implementation of IService isn't thread-safe, is an implementation detail.

Instead, create a thread-safe Adapter/Decorator of IService and inject that into Controller. Something like this:

public ThreadSafeService : IService
{
    private readonly IServiceFactory factory;

    public ThreadSafeService(IServiceFactory factory)
    {
        this.factory = factory;
    }

    public void DoSomething()
    {
        this.factory.Create().DoSomething();
    }
}

IServiceFactory could be declared like this:

public interface IServiceFactory
{
    IService Create();
}

If you implement IServiceFactory so that it creates a new instance of IService for every call to Create, you have a thread-safe implementation on hand, because there's no shared state.

This approach is container-agnostic and prevents Leaky Abstractions.

Community
  • 1
  • 1
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Thanks Mark! Maybe the better question is: Should I be looking at ways to make the repository more thread safe instead of having to adapt the whole service? Other services that use the IRepository would also have to be done this way as well. – Chad Kapatch Apr 04 '14 at 20:30
  • 1
    Thread-safe services are easier to configure, so if you can make them thread-safe, it will make other things easier. The easiest way to make services thread-safe is often to make them stateless. Still, if you rely on something like the Entity Framework, there's not a lot you can do. – Mark Seemann Apr 05 '14 at 07:04
  • 2
    However, if you need to implement the above pattern for lots of different interfaces, you may want to look into Dynamic Interception (AKA AOP); here's an overview talk I gave about this: http://channel9.msdn.com/Events/GOTO/GOTO-2011-Copenhagen/CPH-Aspect-Oriented-Programming-with-Dependency-Injection – Mark Seemann Apr 05 '14 at 07:06
  • Hi @MarkSeemann. What about the dependencies of IServiceFactory? Let's say I have 6 dependencies for IService. That means I need to create 6 abstract factories? – Vin Shahrdar May 02 '18 at 13:31
  • @ShervinShahrdar I've written [a more formal description](http://blog.ploeh.dk/2014/08/24/decoraptor) of that technique since then. Does that help? – Mark Seemann May 02 '18 at 14:29
  • @MarkSeemann Thanks, Mark. I believe I am doing something similar. My only problem is having too many dependencies. Please see my post( https://stackoverflow.com/questions/50137520/instantiating-an-object-in-a-worker-thread-with-dependency-injection) for more details. – Vin Shahrdar May 02 '18 at 14:51