[英]Transactions for C# objects?
只是好奇,是否有对纯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。 注意,我并不是说应该将其用于生产;-)
微软正在努力。 阅读有关软件事务存储的信息。
他们使用几种不同的语法:
// 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还是有一点出路的,我强烈建议您不要自己动手做。
幸运的是,您可以通过精心设计类来获得所需的功能。 特别是,不可变类支持开箱即用的类似事务的行为。 由于每次设置属性时,不可变对象都会返回其自身的新副本,因此如有必要,您始终会对历史记录进行完整的更改以进行回滚。
Juval Lowy撰写了有关此内容的文章。 这是他原始的MSDN文章的链接(我在他出色的WCF书中第一次听说了这个想法)。 这是MSDN的代码示例,看起来像您想要实现的:
public class MyClass
{
Transactional<int> m_Number = new Transactional<int>(3);
public void MyMethod()
{
Transactional<string> city = new Transactional<string>("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,则可以从副本中还原原始对象的所有属性。
这个建议有很多警告。
综上所述,我几年前从事的一个项目确实做了这样的事情。 在非常严格的限制下,它可以很好地工作。 我们仅支持内部业务层数据对象。 而且它们都必须继承自基本接口,该接口提供了有关属性类型的其他一些元数据,并且存在有关可以从属性设置程序触发哪些事件的规则。 我们将开始事务,然后将对象绑定到GUI。 如果用户单击“确定”,则事务刚刚关闭,但如果单击“取消”,则事务管理器将其与GUI解除绑定,并回滚该对象上的所有更改。
不,目前对香草管理对象不存在这种支持。
同样,这里的简单解决方案是首先不要让您的对象进入无效状态。 然后,您无需回滚任何内容,也无需调用“验证”等。如果您删除了设置器,并开始考虑将消息发送给对象以对内部数据执行某些操作,则无需设置属性,会发现有关您域的细微之处,否则您就不会发现。
这是我刚才写的解决方案:)应该也可以用于数组和任何引用类型。
public sealed class ObjectTransaction:IDisposable
{
bool m_isDisposed;
Dictionary<object,object> sourceObjRefHolder;
object m_backup;
object m_original;
public ObjectTransaction(object obj)
{
sourceObjRefHolder = new Dictionary<object,object>();
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<object,object>();
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<object,object> 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<objSourceArray.Length;++i)
{
object arrayItemTarget = processRecursive(objSourceArray.GetValue(i), processRefHolder, targetResolver);
objTargetArray.SetValue(arrayItemTarget,i);
}
}
else
{
IEnumerable<FieldInfo> 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<object,object> m_refHolder;
public DictionaryRefResolver(Dictionary<object,object> 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<FieldInfo> 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;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.