Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with scope-natured services like transactions in a DI and IOC environment

Suppose that your code is properly designed for DI and IOC through constructor injection of any dependencies. Then whether an IOC container or DI-by-hand is used or not at the composition root doesn't matter much for this problem. I think.

Anyway, I find myself over and over again in a mental struggle with how I should best deal with scope-based services, like transactions or other obviously transient operations. There are constraints that I want to abide to:

  • Don't let dependency interfaces be IDisposable - it's a leaky abstraction that only the actual implementing type (and the fiddler sitting at the composition root) should care about.
  • Don't use static service locator types deep down the graph to resolve a dependency - only inject and resolve through the constructor.
  • Don't pass the IOC container, if any, as a dependency down the graph.

To be able to use using, we need IDisposable, but since a dependency interface shouldn't be IDisposable, how do you get around it to get scoped behavior?

like image 932
Johann Gerell Avatar asked May 03 '11 08:05

Johann Gerell


People also ask

When using DI in controller shall I call IDisposable on any injected service?

Instead, you should only place IDisposable on the implementation. This frees any consumer of the abstraction from the doubts whether it should or shouldn't call Dispose (because there is no Dispose method to call on the abstraction).

What is scoped service in .NET Core?

In Asp.Net Core each request has in own service scope. Database and repository services are often registered as scoped services. Default registration of DbContext in EntityFramework Core is also scoped. Scoped lifetime ensures that all the services created within the request shares the same DbContext.

What is IoC and DI in spring?

Spring IoC (Inversion of Control) Container is the core of Spring Framework. It creates the objects, configures and assembles their dependencies, manages their entire life cycle. The Container uses Dependency Injection(DI) to manage the components that make up the application.


1 Answers

In cases like this I would inject a service factory that creates those scoped services and let the service interface derive from IDisposable. This way the factory would be responsible to create the appropriate service instances and the only point that decides which service implementation to return. You would not need to inject the scoped service anywhere.

public interface ITransaction : IDisposable
{
}

public interface ITransactionFactory 
{
    ITransaction CreateTransaction();
}

public class Foo
{
    private readonly ITransactionFactory transactionFactory;

    public Foo(ITransactionFactory transactionFactory)
    {
        this.transactionFactory = transactionFactory;            
    }

    public void DoSomethingWithinTransaction()
    {
        using(ITransaction transaction = this.transactionFactory.CreateTransaction())
        {
            DoSomething();
        }
    }
}
like image 84
Florian Greinacher Avatar answered Oct 11 '22 12:10

Florian Greinacher