如何处理范围性质的服务,如DI和IOC环境中的事务

时间:2011-05-03 08:30:43

标签: c# transactions dependency-injection scope ioc-container

假设您的代码是通过构造函数注入任何依赖项而为DI和IOC正确设计的。然后,在组合根处是否使用IOC容器或DI是否对此问题无关紧要。我想。

无论如何,我一次又一次地发现自己在如何处理基于范围的服务(例如交易或其他明显的短暂操作)方面的精神上的斗争。我想遵守一些限制因素:

  • 不要让依赖接口为IDisposable - 这是一个漏洞的抽象,只有实际的实现类型(和组成根处的小提琴手)应该关心。
  • 不要在图表的深处使用静态服务定位器类型来解决依赖关系 - 只能通过构造函数注入和解析。
  • 不要将IOC容器(如果有)作为图形中的依赖项传递。

为了能够使用using,我们需要IDisposable,但由于依赖关系接口不应该是IDisposable,您如何绕过它来获取范围行为?

4 个答案:

答案 0 :(得分:4)

在这种情况下,我会注入一个服务工厂来创建那些作用域服务,并让服务接口派生自IDisposable。这样,工厂将负责创建适当的服务实例,以及决定返回哪个服务实现的唯一点。您不需要在任何地方注入范围服务。

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

答案 1 :(得分:1)

可能会推出自己的“垃圾收集器”吗?定期检查Dictionary<Transaction>的IsComplete和/或LastAccessed属性并浪费“旧”属性的东西。这是“行走内存泄漏”,但要么你明确清理(比如通过IDisposable),要么你自己锻炼如何清理。

可能有一个AOP解决方案可以启动“gc”......提交/回滚听起来像是一个很好的切割地点......也许你甚至根本不需要GC ......在从提交或回滚备份callstack的路上清理事务。

祝你好运。我很想知道其他人提出的解决方案(和想法)。

干杯。基思。

答案 2 :(得分:1)

我想你可以使用的另一个选择是用一次性类型包装你的实例,这样它可以自动处理类型的处理,无论该类型是否实际上是一次性的。例如,我可以定义类似的东西:

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;
    }
}

(希望有更多的错误处理!)

鉴于我知道在处理时该类型是否是一次性的,我可以相应地调用它。我还可以提供一个隐式运算符来从中强制转换回内部类型。有了上面的,还有一个漂亮的扩展方法:

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

让我们假设我有一个服务,我注入一个类型,它可能是:

public interface IUserService
{
    IUser GetUser();
}

我可以做类似的事情:

public HomeController(IUserService service)
{
    using (var disposable = service.Wrap())
    {
        var user = service.GetUser();

        // I can even grab it again, implicitly.
        IUserService service2 = disposable;
    }
}

现在无论IUserService的具体实施是否是一次性的,我仍然可以安全地假设无关紧要。

另一个快速控制台示例:

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");
    }
}

答案 3 :(得分:1)

如今,大多数IoC容器都对这种性质的工作单元提供了大量内置支持。

在Autofac中,最符合您要求的机制是Owned<T>关系类型。您可以通过this article看到它的实际效果(并获得更多资料)。

希望这有帮助,

尼克