C#对象的事务?

只是好奇,是否支持普通C#对象上的事务? 喜欢

using (var transaction = new ObjectTransaction(obj)) { try { obj.Prop1 = value; obj.Prop2 = value; obj.Recalculate(); // may fire exception transaction.Commit(); // now obj is saved } except { transaction.Rollback(); // now obj properties are restored } } 

只是为了让答案更有用;-)其他语言有什么相似之处吗?

STM更新:这是它声称的内容:

 atomic { x++; y--; throw; } 

将保持x / y不变,包括链式方法调用。 看起来像我要求的。 至少它非常有趣。 我认为这已足够接近了。 此外,其他语言也有类似的东西,例如Haskell STM。 注意我不说它应该用于生产;-)

微软正在努力。 阅读有关软件事务内存的信息。

  • STM.NET
  • STM.NET团队博客
  • 频道9video: STM.NET:谁。 什么。 为什么。
  • 关于STM的论文

他们使用了一些不同的语法:

 // For those who like arrows Atomic.Do(() => { obj.Prop1 = value; obj.Prop2 = value; obj.Recalculate(); }); // For others who prefer exceptions try { obj.Prop1 = value; obj.Prop2 = value; obj.Recalculate(); } catch (AtomicMarker) { } // we may get this in C#: atomic { obj.Prop1 = value; obj.Prop2 = value; obj.Recalculate(); } 

对于它的价值,一个成熟的STM是一个小小的出路,我强烈建议不要滚动你自己。

幸运的是,您可以通过仔细设计类来获得所需的function。 特别是,不可变类支持开箱即用的类似事务的行为。 由于每次设置属性时,不可变对象都会返回自己的新副本,因此如果需要,您始终可以进行完全历史记录更改以进行回滚。

Juval Lowy写了这篇文章。 这是他原来的MSDN文章的链接(我在他出色的WCF书中首次听到了这个想法)。 这是来自MSDN的代码示例,它看起来像您想要实现的:

 public class MyClass { Transactional m_Number = new Transactional(3); public void MyMethod() { Transactional city = new Transactional("New York"); using(TransactionScope scope = new TransactionScope()) { city.Value = "London"; m_Number.Value = 4; m_Number.Value++; Debug.Assert(m_Number.Value == 5); //No call to scope.Complete(), transaction will abort } } 

您可以在执行方法和设置属性之前制作对象的副本。 然后,如果您不喜欢结果,则只需“回滚”到副本即可。 当然,假设你没有副作用来应对。

不,目前没有内置到.net或C#中的任何内容。

但是,根据您的使用要求,您可以实施适合您的工作。

您的ObjectTransaction类将序列化(或仅复制)对象并在事务期间保留副本。 如果您调用了commit则可以删除该副本,但如果您调用了rollback,则可以从该副本还原原始对象上的所有属性。

这个建议有很多警告。

  • 如果你必须使用reflection来获取属性(因为你希望它处理任何对象类型),它将非常慢。 同样用于序列化。
  • 如果你有一个对象树而不仅仅是一个具有一些属性的简单对象,那么对所有对象类型一般处理类似的东西可能会非常棘手。
  • 作为数据列表的属性也很棘手。
  • 如果任何属性获取/设置方法触发具有效果的更改(或事件),则可能会在应用程序的其他位置导致实际问题。
  • 它只对公共财产有效。

总而言之,几年前我工作的一个项目确实做了类似的事情。 在非常严格的限制下,它可以很好地工作。 我们只支持内部业务层数据对象。 他们都必须从一个基本接口inheritance,该接口提供了一些关于属性类型的额外元数据,并且有关于可以从属性设置器触发哪些事件的规则。 我们将启动事务,然后将对象绑定到GUI。 如果用户点击ok,则事务刚刚关闭,但如果他们点击取消,则事务管理器从GUI取消绑定并回滚对象上的所有更改。

不,对于vanilla托管对象,今天不存在这种类型的支持。

而这里再一次简单的解决方案是不允许您的对象首先进入无效状态。 然后你不需要回滚任何东西,你不需要调用“validation”等。如果你删除你的setter并开始考虑向对象发送消息以对内部数据做某事,而不是设置属性,你’ ll会发现关于你域名的微妙内容,否则你就不会。

这是我刚才写的解决方案:)也应该使用数组和任何引用类型。

 public sealed class ObjectTransaction:IDisposable { bool m_isDisposed; Dictionary sourceObjRefHolder; object m_backup; object m_original; public ObjectTransaction(object obj) { sourceObjRefHolder = new Dictionary(); m_backup = processRecursive(obj,sourceObjRefHolder,new CreateNewInstanceResolver()); m_original = obj; } public void Dispose() { Rollback(); } public void Rollback() { if (m_isDisposed) return; var processRefHolder = new Dictionary(); var targetObjRefHolder = sourceObjRefHolder.ToDictionary(x=>x.Value,x=>x.Key); var originalRefResolver = new DictionaryRefResolver(targetObjRefHolder); processRecursive(m_backup, processRefHolder, originalRefResolver); dispose(); } public void Commit() { if (m_isDisposed) return; //do nothing dispose(); } void dispose() { sourceObjRefHolder = null; m_backup = null; m_original = null; m_isDisposed = true; } object processRecursive(object objSource, Dictionary processRefHolder, ITargetObjectResolver targetResolver) { if (objSource == null) return null; if (objSource.GetType()==typeof(string) || objSource.GetType().IsClass == false) return objSource; if (processRefHolder.ContainsKey(objSource)) return processRefHolder[objSource]; Type type = objSource.GetType(); object objTarget = targetResolver.Resolve(objSource); processRefHolder.Add(objSource, objTarget); if (type.IsArray) { Array objSourceArray = (Array)objSource; Array objTargetArray = (Array)objTarget; for(int i=0;i fieldsInfo = FieldInfoEnumerable.Create(type); foreach(FieldInfo f in fieldsInfo) { if (f.FieldType==typeof(ObjectTransaction)) continue; object objSourceField = f.GetValue(objSource); object objTargetField = processRecursive(objSourceField, processRefHolder, targetResolver); f.SetValue(objTarget,objTargetField); } } return objTarget; } interface ITargetObjectResolver { object Resolve(object objSource); } class CreateNewInstanceResolver:ITargetObjectResolver { public object Resolve(object sourceObj) { object newObject=null; if (sourceObj.GetType().IsArray) { var length = ((Array)sourceObj).Length; newObject = Activator.CreateInstance(sourceObj.GetType(),length); } else { //no constructor calling, so no side effects during instantiation newObject = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(sourceObj.GetType()); //newObject = Activator.CreateInstance(sourceObj.GetType()); } return newObject; } } class DictionaryRefResolver:ITargetObjectResolver { readonly Dictionary m_refHolder; public DictionaryRefResolver(Dictionary refHolder) { m_refHolder = refHolder; } public object Resolve(object sourceObj) { if (!m_refHolder.ContainsKey(sourceObj)) throw new Exception("Unknown object reference"); return m_refHolder[sourceObj]; } } } class FieldInfoEnumerable { public static IEnumerable Create(Type type) { while(type!=null) { var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach(FieldInfo fi in fields) { yield return fi; } type = type.BaseType; } } }