Transactions for C# objects?

前端 未结 8 807
青春惊慌失措
青春惊慌失措 2020-12-01 09:26

Just curious, is there any support for transactions on plain C# objects? Like

using (var transaction = new ObjectTransaction(obj))
{
  try
  {
    obj.Prop1          


        
8条回答
  •  爱一瞬间的悲伤
    2020-12-01 10:13

    Here is my solution that I just wrote:) Should work also with arrays and any reference types.

    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;
            }            
        }
    }
    

提交回复
热议问题