How to work with cloud services, such as transactions in DI and IOC

Suppose your code is correctly designed for DI and IOC by installing any dependency constructor. Then, regardless of whether the IOC or DI-by-hand container is used or not in the root of the composition, this does not really matter for this problem. I think.

In any case, I again and again face a mental struggle over how best to deal with services based on areas such as transactions or other clearly transient operations. There are restrictions that I want to comply with:

  • Do not allow dependency interfaces to be IDisposable - this is a fuzzy abstraction that only the actual type of implementation (and the violinist sitting at the root of the composition) should take care of.
  • Do not use the static types of service locators in the depth of the graph to resolve dependencies - just insert and enable the constructor.
  • Do not skip the IOC container, if any, as a graph dependency.

To use using , we need IDisposable , but since the dependency interface should not be IDisposable , how do you get around it to get visible behavior?

+4
source share
4 answers

In such cases, I would introduce a factory service that will create these services with scope, and let the service interface be obtained from IDisposable . Thus, the factory will be responsible for creating the appropriate service instances and the only thing that decides which service implementation will return. You do not need to enter a restricted service.

 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(); } } } 
+4
source

Perhaps your own “garbage collector”? Something that periodically checks IsComplete and / or the LastAccessed Dictionary<Transaction> attribute and discards the "old" ones. This is a "leaking memory leak", but you either explicitly clean up (for example, through IDisposable), or you train automatic cleaning.

There might be an AOP solution to run “gc” ... commit / rollback sounds like a good place to cut ... and you might not even need GC at all ... just clear the transaction in the way of backing up the stop code from commit or rollback.

Good luck to you. I will be interested to know what solutions (and ideas) other people have come up with.

Greetings. Whale.

+1
source

I suggest that another alternative that you can use is to wrap your instances with a one-time type so that it can automatically handle the type deletion regardless of whether this type is actually a one-time type. For example, I could define something like:

 public class DisposableWrapper<T> : IDisposable { private readonly T _instance; private readonly IDisposable _disposable; public DisposableWrapper(T instance) { _instance = instance; _disposable = instance as IDisposable; } public void Dispose() { if (_disposable != null) _disposable.Dispose(); } public static implicit operator T(DisposableWrapper<T> disposableWrapper) { return disposableWrapper._instance; } } 

(Hopefully with a slightly bigger mistake!)

Given that I know, in terms of deletion, whether the type is disposable, I can name it accordingly. I can also provide an implicit statement to return to the internal type. With the above and excellent extension method:

 public static class DisposableExtensions { public static DisposableWrapper<T> Wrap<T>(this T instance) { return new DisposableWrapper<T>(instance); } } 

Suppose I have a service that I injected into a type, it could be:

 public interface IUserService { IUser GetUser(); } 

I could do something like:

 public HomeController(IUserService service) { using (var disposable = service.Wrap()) { var user = service.GetUser(); // I can even grab it again, implicitly. IUserService service2 = disposable; } } 

Now, regardless of whether this particular implementation of IUserService available or not, I can still safely work on the assumption that it does not matter.

Another quick console example:

 class Program { static void Main(string[] args) { using (var instance = new ClassA().Wrap()) { ClassA instanceA = instance; } using (var instance = new ClassB().Wrap()) { ClassB instanceB = instance; } Console.ReadKey(); } } public class ClassA { } public class ClassB : IDisposable { public void Dispose() { Console.Write("Disposed"); } } 
+1
source

Most IoC containers today have substantial built-in support for such work.

In Autofac, the mechanism that best suits your needs is the Owned<T> relationship type. You can see it in action (and get some more materials) through this article .

Hope this helps,

Nick

+1
source

All Articles