我想实现一些某种共享的可支配资源 - 范围或上下文 - 对于包含在该范围内的几个操作应该是相同的。例如:
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:似乎有几个现有的解决方案都有自己的问题,所以它不能用作行业标准,但是开始的好方法是:答案 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的代码中,这种情况非常糟糕。我们所做的就是保持与调用堆栈等效的东西,并且有一个非常简单,支持良好的方式,你可以在调用堆栈上获得一些东西,这就是方法参数。