繁体   English   中英

C#对象的交易?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM