交易范围类似的功能
Transaction scope similar functionality
我正在寻找与 t运行saction 范围非常相似的东西,它在服务上创建一个版本,并将 delete/commit 在范围的末尾。 t运行saction 范围内的每个 SQL 语句 运行 在内部查看一些连接池/ t运行saction 存储以确定它是否在范围内并做出适当的反应。调用者不需要将 t运行saction 传递给每个调用。我正在寻找这个功能。
这里有更多关于它的信息:https://blogs.msdn.microsoft.com/florinlazar/2005/04/19/transaction-current-and-ambient-transactions/
这里是基本一次性class:
public sealed class VersionScope : IDisposable
{
private readonly GeodatabaseVersion _version;
private readonly VersionManager _versionManager;
public VersionScope(Configuration config)
{
_versionManager = new VersionManager(config);
_version = _versionManager.GenerateTempVersion();
_versionManager.Create(_version);
_versionManager.VerifyValidVersion(_version);
_versionManager.ServiceReconcilePull();
_versionManager.ReconcilePull(_version);
}
public void Dispose()
{
_versionManager.Delete(_version);
}
public void Complete()
{
_versionManager.ReconcilePush(_version);
}
}
我希望到目前为止我编写的所有代码都没有任何版本概念。我只想包括一个简单的
Version = GetCurrentVersionWithinScope()
在代码的最低级别。
如果内存中同时有多个实例,那么实现这样的东西最安全的方法是什么,并且使用错误版本的风险很小运行。
我非常幼稚的方法是查找进程所在的内存块是否有唯一标识符 运行。然后将当前工作版本存储到全局数组或并发字典中。然后在我需要当前版本的代码中,我使用它的内存块标识符并将它映射到创建的版本。
编辑:
用法示例:
using (var scope = new VersionScope(_config))
{
AddFeature(); // This has no concept of scope passed to it, and could error out forcing a dispose() without a complete()
scope.Complete();
}
像这样使用 IDisposable 有点值得怀疑。 (参见 Is it abusive to use IDisposable and "using" as a means for getting "scoped behavior" for exception safety?)
我自己发现它对某些事情很有用。这是我使用的模式:
class LevelContext
{
private int _level;
public int CurrentLevel
{
get { return _level; }
set { _level = value < 0 ? 0 : value; }
}
public ILevel NewLevel(int depth = 1)
{
return new Level(this, depth);
}
/// <summary>
/// Provides an interface that calling code can use to handle level objects.
/// </summary>
public interface ILevel : IDisposable
{
LevelContext Owner { get; }
int Depth { get; }
void Close();
}
/// <summary>
/// Private class that provides an easy way to scope levels by allowing
/// them to participate in the "using" construct. Creation of a Level results in an
/// increase in owner's level, while disposal returns owner's level to what it was before.
/// </summary>
class Level : ILevel
{
public Level(LevelContext owner, int depth)
{
Owner = owner;
Depth = depth;
PreviousLevel = owner.CurrentLevel;
Owner.CurrentLevel += Depth;
}
public LevelContext Owner { get; private set; }
public int Depth { get; private set; }
public int PreviousLevel { get; private set; }
public void Close()
{
if (Owner != null)
{
Owner.CurrentLevel = PreviousLevel;
Owner = null;
}
}
void IDisposable.Dispose()
{
Close();
}
}
那么调用代码如下所示:
static void Main(string[] args)
{
var lc = new LevelContext();
Console.WriteLine(lc.CurrentLevel);
using (lc.NewLevel())
Console.WriteLine(lc.CurrentLevel);
Console.WriteLine(lc.CurrentLevel);
}
所以在你的情况下,你是对的——你需要创建一些东西来跟踪当前版本。创建和处置 VersionScope 时应该更新某些内容。
最直接的方法是使用ThreadStatic
或ThreadLocal
将当前版本存储在线程本地存储中。这样多个线程就不会互相干扰了。例如假设我们版本 class:
public class Version {
public Version(int number) {
Number = number;
}
public int Number { get; }
public override string ToString() {
return "Version " + Number;
}
}
然后 VersionScope
的实现可以这样进行:
public sealed class VersionScope : IDisposable {
private bool _isCompleted;
private bool _isDisposed;
// note ThreadStatic attribute
[ThreadStatic] private static Version _currentVersion;
public static Version CurrentVersion => _currentVersion;
public VersionScope(int version) {
_currentVersion = new Version(version);
}
public void Dispose() {
if (_isCompleted || _isDisposed)
return;
var v = _currentVersion;
if (v != null) {
DeleteVersion(v);
}
_currentVersion = null;
_isDisposed = true;
}
public void Complete() {
if (_isCompleted || _isDisposed)
return;
var v = _currentVersion;
if (v != null) {
PushVersion(v);
}
_currentVersion = null;
_isCompleted = true;
}
private void DeleteVersion(Version version) {
Console.WriteLine($"Version {version} deleted");
}
private void PushVersion(Version version) {
Console.WriteLine($"Version {version} pushed");
}
}
它会工作,但它不支持嵌套作用域,这不好,所以要修复我们需要在开始新作用域时存储以前的作用域,并在 Complete
或 [=20= 上恢复它]:
public sealed class VersionScope : IDisposable {
private bool _isCompleted;
private bool _isDisposed;
private static readonly ThreadLocal<VersionChain> _versions = new ThreadLocal<VersionChain>();
public static Version CurrentVersion => _versions.Value?.Current;
public VersionScope(int version) {
var cur = _versions.Value;
// remember previous versions if any
_versions.Value = new VersionChain(new Version(version), cur);
}
public void Dispose() {
if (_isCompleted || _isDisposed)
return;
var cur = _versions.Value;
if (cur != null) {
DeleteVersion(cur.Current);
// restore previous
_versions.Value = cur.Previous;
}
_isDisposed = true;
}
public void Complete() {
if (_isCompleted || _isDisposed)
return;
var cur = _versions.Value;
if (cur != null) {
PushVersion(cur.Current);
// restore previous
_versions.Value = cur.Previous;
}
_isCompleted = true;
}
private void DeleteVersion(Version version) {
Console.WriteLine($"Version {version} deleted");
}
private void PushVersion(Version version) {
Console.WriteLine($"Version {version} pushed");
}
// just a class to store previous versions
private class VersionChain {
public VersionChain(Version current, VersionChain previous) {
Current = current;
Previous = previous;
}
public Version Current { get; }
public VersionChain Previous { get; }
}
}
这已经是您可以使用的东西了。示例用法(我使用单线程,但如果有多个线程分别执行此操作 - 它们不会相互干扰):
static void Main(string[] args) {
PrintCurrentVersion(); // no version
using (var s1 = new VersionScope(1)) {
PrintCurrentVersion(); // version 1
s1.Complete();
PrintCurrentVersion(); // no version, 1 is already completed
using (var s2 = new VersionScope(2)) {
using (var s3 = new VersionScope(3)) {
PrintCurrentVersion(); // version 3
} // version 3 deleted
PrintCurrentVersion(); // back to version 2
s2.Complete();
}
PrintCurrentVersion(); // no version, all completed or deleted
}
Console.ReadKey();
}
private static void PrintCurrentVersion() {
Console.WriteLine("Current version: " + VersionScope.CurrentVersion);
}
然而,当您使用异步调用时,这将不起作用,因为 ThreadLocal
绑定到一个线程,但异步方法可以跨越多个线程。但是,有一个名为 AsyncLocal
的类似构造,其值将通过异步调用流动。所以我们可以将构造函数参数添加到 VersionScope
以指示我们是否需要异步流。事务范围以类似的方式工作——有 TransactionScopeAsyncFlowOption
您传递给 TransactionScope
构造函数,指示它是否将流过异步调用。
修改后的版本如下所示:
public sealed class VersionScope : IDisposable {
private bool _isCompleted;
private bool _isDisposed;
private readonly bool _asyncFlow;
// thread local versions
private static readonly ThreadLocal<VersionChain> _tlVersions = new ThreadLocal<VersionChain>();
// async local versions
private static readonly AsyncLocal<VersionChain> _alVersions = new AsyncLocal<VersionChain>();
// to get current version, first check async local storage, then thread local
public static Version CurrentVersion => _alVersions.Value?.Current ?? _tlVersions.Value?.Current;
// helper method
private VersionChain CurrentVersionChain => _asyncFlow ? _alVersions.Value : _tlVersions.Value;
public VersionScope(int version, bool asyncFlow = false) {
_asyncFlow = asyncFlow;
var cur = CurrentVersionChain;
// remember previous versions if any
if (asyncFlow) {
_alVersions.Value = new VersionChain(new Version(version), cur);
}
else {
_tlVersions.Value = new VersionChain(new Version(version), cur);
}
}
public void Dispose() {
if (_isCompleted || _isDisposed)
return;
var cur = CurrentVersionChain;
if (cur != null) {
DeleteVersion(cur.Current);
// restore previous
if (_asyncFlow) {
_alVersions.Value = cur.Previous;
}
else {
_tlVersions.Value = cur.Previous;
}
}
_isDisposed = true;
}
public void Complete() {
if (_isCompleted || _isDisposed)
return;
var cur = CurrentVersionChain;
if (cur != null) {
PushVersion(cur.Current);
// restore previous
if (_asyncFlow) {
_alVersions.Value = cur.Previous;
}
else {
_tlVersions.Value = cur.Previous;
}
}
_isCompleted = true;
}
private void DeleteVersion(Version version) {
Console.WriteLine($"Version {version} deleted");
}
private void PushVersion(Version version) {
Console.WriteLine($"Version {version} pushed");
}
// just a class to store previous versions
private class VersionChain {
public VersionChain(Version current, VersionChain previous) {
Current = current;
Previous = previous;
}
public Version Current { get; }
public VersionChain Previous { get; }
}
}
范围与异步流的示例用法:
static void Main(string[] args) {
Test();
Console.ReadKey();
}
static async void Test() {
PrintCurrentVersion(); // no version
using (var s1 = new VersionScope(1, asyncFlow: true)) {
await Task.Delay(100);
PrintCurrentVersion(); // version 1
await Task.Delay(100);
s1.Complete();
await Task.Delay(100);
PrintCurrentVersion(); // no version, 1 is already completed
using (var s2 = new VersionScope(2, asyncFlow: true)) {
using (var s3 = new VersionScope(3, asyncFlow: true)) {
PrintCurrentVersion(); // version 3
} // version 3 deleted
await Task.Delay(100);
PrintCurrentVersion(); // back to version 2
s2.Complete();
}
await Task.Delay(100);
PrintCurrentVersion(); // no version, all completed or deleted
}
}
private static void PrintCurrentVersion() {
Console.WriteLine("Current version: " + VersionScope.CurrentVersion);
}
我正在寻找与 t运行saction 范围非常相似的东西,它在服务上创建一个版本,并将 delete/commit 在范围的末尾。 t运行saction 范围内的每个 SQL 语句 运行 在内部查看一些连接池/ t运行saction 存储以确定它是否在范围内并做出适当的反应。调用者不需要将 t运行saction 传递给每个调用。我正在寻找这个功能。
这里有更多关于它的信息:https://blogs.msdn.microsoft.com/florinlazar/2005/04/19/transaction-current-and-ambient-transactions/
这里是基本一次性class:
public sealed class VersionScope : IDisposable
{
private readonly GeodatabaseVersion _version;
private readonly VersionManager _versionManager;
public VersionScope(Configuration config)
{
_versionManager = new VersionManager(config);
_version = _versionManager.GenerateTempVersion();
_versionManager.Create(_version);
_versionManager.VerifyValidVersion(_version);
_versionManager.ServiceReconcilePull();
_versionManager.ReconcilePull(_version);
}
public void Dispose()
{
_versionManager.Delete(_version);
}
public void Complete()
{
_versionManager.ReconcilePush(_version);
}
}
我希望到目前为止我编写的所有代码都没有任何版本概念。我只想包括一个简单的
Version = GetCurrentVersionWithinScope()
在代码的最低级别。
如果内存中同时有多个实例,那么实现这样的东西最安全的方法是什么,并且使用错误版本的风险很小运行。
我非常幼稚的方法是查找进程所在的内存块是否有唯一标识符 运行。然后将当前工作版本存储到全局数组或并发字典中。然后在我需要当前版本的代码中,我使用它的内存块标识符并将它映射到创建的版本。
编辑:
用法示例:
using (var scope = new VersionScope(_config))
{
AddFeature(); // This has no concept of scope passed to it, and could error out forcing a dispose() without a complete()
scope.Complete();
}
像这样使用 IDisposable 有点值得怀疑。 (参见 Is it abusive to use IDisposable and "using" as a means for getting "scoped behavior" for exception safety?)
我自己发现它对某些事情很有用。这是我使用的模式:
class LevelContext
{
private int _level;
public int CurrentLevel
{
get { return _level; }
set { _level = value < 0 ? 0 : value; }
}
public ILevel NewLevel(int depth = 1)
{
return new Level(this, depth);
}
/// <summary>
/// Provides an interface that calling code can use to handle level objects.
/// </summary>
public interface ILevel : IDisposable
{
LevelContext Owner { get; }
int Depth { get; }
void Close();
}
/// <summary>
/// Private class that provides an easy way to scope levels by allowing
/// them to participate in the "using" construct. Creation of a Level results in an
/// increase in owner's level, while disposal returns owner's level to what it was before.
/// </summary>
class Level : ILevel
{
public Level(LevelContext owner, int depth)
{
Owner = owner;
Depth = depth;
PreviousLevel = owner.CurrentLevel;
Owner.CurrentLevel += Depth;
}
public LevelContext Owner { get; private set; }
public int Depth { get; private set; }
public int PreviousLevel { get; private set; }
public void Close()
{
if (Owner != null)
{
Owner.CurrentLevel = PreviousLevel;
Owner = null;
}
}
void IDisposable.Dispose()
{
Close();
}
}
那么调用代码如下所示:
static void Main(string[] args)
{
var lc = new LevelContext();
Console.WriteLine(lc.CurrentLevel);
using (lc.NewLevel())
Console.WriteLine(lc.CurrentLevel);
Console.WriteLine(lc.CurrentLevel);
}
所以在你的情况下,你是对的——你需要创建一些东西来跟踪当前版本。创建和处置 VersionScope 时应该更新某些内容。
最直接的方法是使用ThreadStatic
或ThreadLocal
将当前版本存储在线程本地存储中。这样多个线程就不会互相干扰了。例如假设我们版本 class:
public class Version {
public Version(int number) {
Number = number;
}
public int Number { get; }
public override string ToString() {
return "Version " + Number;
}
}
然后 VersionScope
的实现可以这样进行:
public sealed class VersionScope : IDisposable {
private bool _isCompleted;
private bool _isDisposed;
// note ThreadStatic attribute
[ThreadStatic] private static Version _currentVersion;
public static Version CurrentVersion => _currentVersion;
public VersionScope(int version) {
_currentVersion = new Version(version);
}
public void Dispose() {
if (_isCompleted || _isDisposed)
return;
var v = _currentVersion;
if (v != null) {
DeleteVersion(v);
}
_currentVersion = null;
_isDisposed = true;
}
public void Complete() {
if (_isCompleted || _isDisposed)
return;
var v = _currentVersion;
if (v != null) {
PushVersion(v);
}
_currentVersion = null;
_isCompleted = true;
}
private void DeleteVersion(Version version) {
Console.WriteLine($"Version {version} deleted");
}
private void PushVersion(Version version) {
Console.WriteLine($"Version {version} pushed");
}
}
它会工作,但它不支持嵌套作用域,这不好,所以要修复我们需要在开始新作用域时存储以前的作用域,并在 Complete
或 [=20= 上恢复它]:
public sealed class VersionScope : IDisposable {
private bool _isCompleted;
private bool _isDisposed;
private static readonly ThreadLocal<VersionChain> _versions = new ThreadLocal<VersionChain>();
public static Version CurrentVersion => _versions.Value?.Current;
public VersionScope(int version) {
var cur = _versions.Value;
// remember previous versions if any
_versions.Value = new VersionChain(new Version(version), cur);
}
public void Dispose() {
if (_isCompleted || _isDisposed)
return;
var cur = _versions.Value;
if (cur != null) {
DeleteVersion(cur.Current);
// restore previous
_versions.Value = cur.Previous;
}
_isDisposed = true;
}
public void Complete() {
if (_isCompleted || _isDisposed)
return;
var cur = _versions.Value;
if (cur != null) {
PushVersion(cur.Current);
// restore previous
_versions.Value = cur.Previous;
}
_isCompleted = true;
}
private void DeleteVersion(Version version) {
Console.WriteLine($"Version {version} deleted");
}
private void PushVersion(Version version) {
Console.WriteLine($"Version {version} pushed");
}
// just a class to store previous versions
private class VersionChain {
public VersionChain(Version current, VersionChain previous) {
Current = current;
Previous = previous;
}
public Version Current { get; }
public VersionChain Previous { get; }
}
}
这已经是您可以使用的东西了。示例用法(我使用单线程,但如果有多个线程分别执行此操作 - 它们不会相互干扰):
static void Main(string[] args) {
PrintCurrentVersion(); // no version
using (var s1 = new VersionScope(1)) {
PrintCurrentVersion(); // version 1
s1.Complete();
PrintCurrentVersion(); // no version, 1 is already completed
using (var s2 = new VersionScope(2)) {
using (var s3 = new VersionScope(3)) {
PrintCurrentVersion(); // version 3
} // version 3 deleted
PrintCurrentVersion(); // back to version 2
s2.Complete();
}
PrintCurrentVersion(); // no version, all completed or deleted
}
Console.ReadKey();
}
private static void PrintCurrentVersion() {
Console.WriteLine("Current version: " + VersionScope.CurrentVersion);
}
然而,当您使用异步调用时,这将不起作用,因为 ThreadLocal
绑定到一个线程,但异步方法可以跨越多个线程。但是,有一个名为 AsyncLocal
的类似构造,其值将通过异步调用流动。所以我们可以将构造函数参数添加到 VersionScope
以指示我们是否需要异步流。事务范围以类似的方式工作——有 TransactionScopeAsyncFlowOption
您传递给 TransactionScope
构造函数,指示它是否将流过异步调用。
修改后的版本如下所示:
public sealed class VersionScope : IDisposable {
private bool _isCompleted;
private bool _isDisposed;
private readonly bool _asyncFlow;
// thread local versions
private static readonly ThreadLocal<VersionChain> _tlVersions = new ThreadLocal<VersionChain>();
// async local versions
private static readonly AsyncLocal<VersionChain> _alVersions = new AsyncLocal<VersionChain>();
// to get current version, first check async local storage, then thread local
public static Version CurrentVersion => _alVersions.Value?.Current ?? _tlVersions.Value?.Current;
// helper method
private VersionChain CurrentVersionChain => _asyncFlow ? _alVersions.Value : _tlVersions.Value;
public VersionScope(int version, bool asyncFlow = false) {
_asyncFlow = asyncFlow;
var cur = CurrentVersionChain;
// remember previous versions if any
if (asyncFlow) {
_alVersions.Value = new VersionChain(new Version(version), cur);
}
else {
_tlVersions.Value = new VersionChain(new Version(version), cur);
}
}
public void Dispose() {
if (_isCompleted || _isDisposed)
return;
var cur = CurrentVersionChain;
if (cur != null) {
DeleteVersion(cur.Current);
// restore previous
if (_asyncFlow) {
_alVersions.Value = cur.Previous;
}
else {
_tlVersions.Value = cur.Previous;
}
}
_isDisposed = true;
}
public void Complete() {
if (_isCompleted || _isDisposed)
return;
var cur = CurrentVersionChain;
if (cur != null) {
PushVersion(cur.Current);
// restore previous
if (_asyncFlow) {
_alVersions.Value = cur.Previous;
}
else {
_tlVersions.Value = cur.Previous;
}
}
_isCompleted = true;
}
private void DeleteVersion(Version version) {
Console.WriteLine($"Version {version} deleted");
}
private void PushVersion(Version version) {
Console.WriteLine($"Version {version} pushed");
}
// just a class to store previous versions
private class VersionChain {
public VersionChain(Version current, VersionChain previous) {
Current = current;
Previous = previous;
}
public Version Current { get; }
public VersionChain Previous { get; }
}
}
范围与异步流的示例用法:
static void Main(string[] args) {
Test();
Console.ReadKey();
}
static async void Test() {
PrintCurrentVersion(); // no version
using (var s1 = new VersionScope(1, asyncFlow: true)) {
await Task.Delay(100);
PrintCurrentVersion(); // version 1
await Task.Delay(100);
s1.Complete();
await Task.Delay(100);
PrintCurrentVersion(); // no version, 1 is already completed
using (var s2 = new VersionScope(2, asyncFlow: true)) {
using (var s3 = new VersionScope(3, asyncFlow: true)) {
PrintCurrentVersion(); // version 3
} // version 3 deleted
await Task.Delay(100);
PrintCurrentVersion(); // back to version 2
s2.Complete();
}
await Task.Delay(100);
PrintCurrentVersion(); // no version, all completed or deleted
}
}
private static void PrintCurrentVersion() {
Console.WriteLine("Current version: " + VersionScope.CurrentVersion);
}