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

C#中在上下文/范围中包装操作的最佳方法,c#,.net,C#,.net,我想实现某种共享的可支配资源——范围或上下文——对于包装在该范围内的几个操作应该是相同的。例如: using(var ctx = Context.Create()) { var obj = ReadSmth(); ChangeObject(obj); SomeActionWithReadWriteInside(); Write(obj); } Read、Change、Write应该在其内部使用完全相同的上下文对象实例(不直接传递它,就像我们使用Transacti

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

using(var ctx = Context.Create())
{
    var obj = ReadSmth();
    ChangeObject(obj);
    SomeActionWithReadWriteInside();
    Write(obj);
}
Read、Change、Write应该在其内部使用完全相同的上下文对象实例(不直接传递它,就像我们使用TransactionScope一样)。也可能存在这样的情况:在ChangeObject方法内部发生了其他读\写操作,它们也应该包含在此上下文中。但是另一个线程上的另一个using(ctx)块不应该干扰该上下文

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

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

UPD2:似乎有几种现有的解决方案都有其自身的问题,因此它不能作为行业标准使用,但最好从以下方面入手:

    • 这不是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();
          }
      }
      
      那么要使用它,您只需参考

       Contexts.CurrentContext
      

      然而,这充满了错误。例如,在多线程代码中,或者在使用async/await的代码中,这会失败得很厉害。我们所做的就是维护与调用堆栈等价的东西,有一种非常简单、支持良好的方法,可以将一些东西放到调用堆栈中,这就是方法参数

      使用线程本地存储?我想让代码更干净,而不在所有方法和方法的方法中传递上下文等等,因为里面可能有很多嵌套方法。丹尼尔A.怀特:可能是,但这是多线程环境中所有情况的最佳解决方案吗?“更干净”是一个观点。如果代码准确地告诉您它期望的内容,我会说代码更干净。在这种情况下,没有迹象表明该方法需要任何类型的上下文,这意味着您现在必须“记住”才能正确地执行操作。在我看来,更清楚的是,代码实际上要求你正确地做事。是的,我同意。关键问题是源代码的大小——我的示例只是一个抽象。想象一下,您有很多由具有很多嵌套级别的人编写的遗留代码,这些代码正在读\写一些东西,并且非常需要实现事务性支持。这就是我要找的谢谢你,史蒂夫!这是一个很好的开始。但我认为我不会使用ThreadStatic来控制它的整个生命周期,也不会使用某种字典来存储上下文。线程静态/堆栈模式反映了函数调用的情况。想知道字典给了你什么吗?ThreadStatic在ASP.NET环境和ThreadPool中都没有意义,所以我想为不同的平台开发不同的技术。我认为Dictionary和Stack都是一种开销,所以我可以创建一个带有自身链接的对象来存储内部作用域。另外,看起来我找到了一个适合的实现,并进行了一些改进(去掉dictionary,使用HttpContext.Current.Items来实现ASP.NET实现,而不是ThreadStatic)——我想您可以一次解决一个问题,但我在这方面做了几次尝试,您经常会发现问题(例如,当有人使用async/await,线程在方法中途切换时?)另外,您的所有代码现在都被固定在用于存储变量的全局字典式结构上,因此很难进行测试。值得注意的是,Common Lisp将这些“动态变量”称为前置变量,但通常语言更喜欢词法作用域。@SteveCooper这在Task.Run中内部使用。
       Contexts.CurrentContext