C#在上下文/范围中包装操作的最佳方法

时间:2016-08-23 15:58:11

标签: c# .net

我想实现一些某种共享的可支配资源 - 范围或上下文 - 对于包含在该范围内的几个操作应该是相同的。例如:

using(var ctx = Context.Create())
{
    var obj = ReadSmth();
    ChangeObject(obj);
    SomeActionWithReadWriteInside();
    Write(obj);
}

Read,Change,Write应该在其中使用完全相同的上下文对象实例(没有直接传递它,就像我们使用的是TransactionScope)。当ChangeObject方法内部发生其他读/写操作时,也可能出现这种情况,它们也应该包含在这个上下文中。但另一个线程上的另一个使用(ctx)块不应该干扰该上下文。

UPD1:我理解引入超全局对象的可能问题,ASP.NET线程切换和异步/等待部分切换和测试问题的问题。但在某些情况下,在您的解决方案中有一定的限制,这种方法有权存在。我只是想问一下这种模式的结构。

在.NET / C#中实现此类行为的最佳方法是什么?

UPD2:似乎有几个现有的解决方案都有自己的问题,所以它不能用作行业标准,但是开始的好方法是:

1 个答案:

答案 0 :(得分:2)

这不是C#支持naurally的东西所以如果你想这样做,你将不得不推出自己的解决方案。这是一种方法,但我不想在生产中使用它 - 传递一个变量不是那么难以复杂化是值得的。此外,代码不是“干净”,因为你要做的就是引入一个臭名昭着的全局(你的'当前上下文'),现在你已经编写了更难测试和隔离的代码。

然而,考虑一下这很有意思,所以这是一种方法......

首先,创建一个包含每个上下文堆栈的ThreadStatic变量;

public static class Contexts
{
    [ThreadStatic]
    public Stack<Context> contexts;

    public Context CurrentContext
    {
        get
        {
            if (contexts == null || contexts.Count == 0) { return null; }
            return contexts.Peek();
        }
    }

    public void ContextCreated(Context obj)
    {
        if (contexts == null) { contexts = new Stack<Context>(); }
        contexts.Push(obj);
    }

    public void ContextDisposed()
    {
        if (contexts == null) { contexts = new Stack<Context>(); }
        contexts.Pop();
    }
}

然后,无论何时创建新上下文,将其添加到堆栈中,并在处置时,将其从堆栈中弹出;

public class Context: IDisposable
{
    public Context
    {
        Contexts.ContextCreated(this);
    }

    public void Dispose
    {
        Contexts.ContextDisposed(this);
    }
}

然后使用它,你只需参考

 Contexts.CurrentContext

但这充满了错误。例如,在多线程代码或使用async / await的代码中,这种情况非常糟糕。我们所做的就是保持与调用堆栈等效的东西,并且有一个非常简单,支持良好的方式,你可以在调用堆栈上获得一些东西,这就是方法参数。