文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>数据库操作的“事务安全区”

数据库操作的“事务安全区”

时间:2010-08-22  来源:

前言

------------------- 

前天突然收到顾客的电话,说系统的单据操作后,找不到了。我查看了日志,发现有表被锁的情况,立刻感觉到是系统升级后,有事务处理的问题。

 

晚上检查代码,发现了原来有事务开启之后,没有关闭的代码存在,导致了数据的丢失。这个简直是超级郁闷。幸好没有造成太大的损失。不过这种情况以后还会出现,怎样才能保证一个事务操作是安全的呢?

 

事务安全区

-------------------- 

事务安全区是我自己想出来的,含义就是:在这个区域里面操作事务是绝对安全的,任何代码上的bug都不会对系统数据造成影响。

一个理想的例子:

代码     class TransactionBusiness
    {
        //delcare transaction safe area

        public void TransactionProcess()
        {
            // open transaction here.
        }

        // check transaction here.
    }

 

当方法体离开之后,有一种机制能够检测事务是否被开启过,如果是,则自动回滚,并记录日志。 

 

从技术角度如何解决这个问题呢?我想到了四个方案。

 

事务安全区技术实现四剑客(伪代码)

---------------------------- 

1. 小白模式 

 代码

    class TransactionBusiness
    {
        public void TransactionProcess()
        {
            DbCommand command = new DbCommand();

            try
            {
                command.Open();

                command.Execute();

                //oops!! Forgot commit!
            }
            finally
            {
                if (command.IsOpen)
                    command.Rollback();
            }
        }
    }


    class DbCommand
    {
        bool isOpen = false;

        public void Open()
        {
            //open transaction here.

            isOpen = true;
        }

        public bool IsOpen
        {
            get
            {
                return isOpen;
            }
        }

        public void Execute()
        {
            //execute command
        }

        public void Rollback()
        {
            //rollback transaction

            isOpen = false;
        }

        public void Commit()
        {
            //commit transaction

            isOpen = false;
        }
    }

这个嘛。。我就不解释了,是属于事务操作的入门级别规范了。是个写代码的人都应该掌握的。

  

2. 使用using关键字。

代码     class TransactionBusiness
    {
        public void TransactionProcess()
        {
            using (DbCommand command = new DbCommand())
            {
                command.Open();

                command.Execute();

                //oops!! Forgot to commit here!!
            }
        }
    }

    class DbCommand : IDisposable
    {
        bool isOpen = false;

        public void Open()
        {
            //open transaction here.

            isOpen = true;
        }

        public bool IsOpen
        {
            get
            {
                return isOpen;
            }
        }

        public void Execute()
        {
            //execute command
        }

        public void Rollback()
        {
            //rollback transaction

            isOpen = false;
        }

        public void Commit()
        {
            //commit transaction

            isOpen = false;
        }

        public void Dispose()
        {
            if (this.IsOpen)
            {
                this.Rollback();
            }
        }
    }

 

使用了using关键字后,当离开using的区域,系统自动调用Dispose,这样,就能够检测是否关闭了事务。

个人感觉是一种合格的选择。 

 

2. 使用匿名代理

代码     class TransactionBusiness
    {
        public void TransactionProcess()
        {
            DbCommand command = new DbCommand();

            command.OpenSafely(delegate()
            {
                command.Execute();

                //oops!! Forgot to commit here!!
            }
            );
        }
    }

    class DbCommand
    {
        bool isOpen = false;

        public delegate void SafeTransactionArea();

        public void OpenSafely(SafeTransactionArea area)
        {
            isOpen = true;

            area();

            if (isOpen)
            {
                Console.WriteLine("transaction is not commit. rollback.");

                Rollback();
            }
        }

        public void Rollback()
        {
            isOpen = false;
        }

        public void Commit()
        {
            isOpen = false;
        }

        internal void Execute()
        {
            //execute to database
        }
    }

 

在匿名代理里面,操作command,是安全的。我个人认为这种写法没有using漂亮,但是也能够解决问题。

他唯一的优点是,当用户需要使用事务的时候,强迫了一种安全的代码格式,而using如果忘记标识,同样会造成事务处理异常。

 

4. 使用静态编译 + 反射检测

 

代码     class TransactionBusiness
    {
        public void TransactionProcess()
        {
            DbCommand command = new DbCommand();

            command.Open();

            command.Execute();

            // oops!! forgot to commit!!
        }
    }


    class DbCommand
    {
        bool isOpen = false;

        public void Open()
        {
            //open transaction here.

            isOpen = true;
        }

        public bool IsOpen
        {
            get
            {
                return isOpen;
            }
        }

        public void Execute()
        {
            //execute command
        }

        public void Rollback()
        {
            //rollback transaction

            isOpen = false;
        }

        public void Commit()
        {
            //commit transaction

            isOpen = false;
        }
    }

 

 

代码部分来看,和普通的写法没有任何不同,关键部分在:

代码     class SafeTransactionVerification
    {
        public void Verify(Assembly assembly)
        {
            foreach (Type type in assembly.GetTypes())
            {
                foreach (MethodInfo info in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    bool hasOpen = false;

                    bool hasClose = false;

                    MethodBodyReflector reflector = new MethodBodyReflector(info);

                    foreach (ILInstruction il in reflector.Instructions)
                    {
                        if (il.GetMethodInfo().DeclaringType == typeof(DbCommand) && il.GetMethodInfo().Name.Equals("OPEN", StringComparison.OrdinalIgnoreCase))
                        {
                            hasOpen = true;

                            continue;
                        }

                        if (il.GetMethodInfo().DeclaringType == typeof(DbCommand) && il.GetMethodInfo().Name.Equals("COMMIT", StringComparison.OrdinalIgnoreCase))
                        {
                            hasClose = true;

                            continue;
                        }
                    }

                    if (hasOpen && hasClose)
                        continue;

                    throw new Exception("Transaction is not COMMITTED safely!!");
                }
            }
        }
    }

    public class MethodBodyReflector
    {
        private List<ILInstruction> instructions = new List<ILInstruction>();

        ILGlobals global = new ILGlobals();

        public MethodBodyReflector(MethodBase info)
        {
            global.LoadOpCodes();

            if (info.GetMethodBody() != null)
            {
                ConstructInstructions(info);
            }
        }

        /// <summary>
        /// 所有指令集合
        /// </summary>
        public List<ILInstruction> Instructions
        {
            get
            {
                return instructions;
            }
        }

        private void ConstructInstructions(MethodBase info)
        {
            byte[] il = info.GetMethodBody().GetILAsByteArray();

            int position = 0;

            while (position < il.Length)
            {
                ILInstruction instruction = ConstructInstruction(info.Module, info, il, ref position);

                instructions.Add(instruction);
            }
        }

        private ILInstruction ConstructInstruction(Module module, MethodBase mi, byte[] il, ref int position)
        {
            ILInstruction instruction = new ILInstruction();

            // get the operation code of the current instruction
            OpCode code = OpCodes.Nop;
            ushort value = il[position++];
            if (value != 0xfe)
            {
                code = global.SingleByteOpCodes[(int)value];
            }
            else
            {
                value = il[position++];
                code = global.MultiByteOpCodes[(int)value];
                value = (ushort)(value | 0xfe00);
            }
            instruction.Code = code;
            instruction.Offset = position - 1;
            int metadataToken = 0;
            // get the operand of the current operation
            switch (code.OperandType)
            {
                case OperandType.InlineBrTarget:
                    metadataToken = ReadInt32(il, ref position);
                    metadataToken += position;
                    instruction.Operand = metadataToken;
                    break;
                case OperandType.InlineField:
                    metadataToken = ReadInt32(il, ref position);
                    if (mi is ConstructorInfo)
                    {
                        instruction.Operand = module.ResolveField(metadataToken,
                            mi.DeclaringType.GetGenericArguments(), null);
                    }
                    else
                    {
                        instruction.Operand = module.ResolveField(metadataToken,
                            mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                    }
                    break;
                case OperandType.InlineMethod:
                    metadataToken = ReadInt32(il, ref position);
                    try
                    {
                        if (mi is ConstructorInfo)
                        {
                            instruction.Operand = module.ResolveMethod(metadataToken,
                                mi.DeclaringType.GetGenericArguments(), null);
                        }
                        else
                        {
                            instruction.Operand = module.ResolveMethod(metadataToken,
                                mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                        }
                    }
                    catch
                    {
                        if (mi is ConstructorInfo)
                        {
                            instruction.Operand = module.ResolveMember(metadataToken,
                                mi.DeclaringType.GetGenericArguments(), null);
                        }
                        else
                        {
                            instruction.Operand = module.ResolveMember(metadataToken,
                                mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                        }
                    }
                    break;
                case OperandType.InlineSig:
                    metadataToken = ReadInt32(il, ref position);
                    instruction.Operand = module.ResolveSignature(metadataToken);
                    break;
                case OperandType.InlineTok:
                    metadataToken = ReadInt32(il, ref position);
                    try
                    {
                        if (mi is ConstructorInfo)
                        {
                            instruction.Operand = module.ResolveType(metadataToken,
                                mi.DeclaringType.GetGenericArguments(), null);
                        }
                        else
                        {
                            instruction.Operand = module.ResolveType(metadataToken,
                                mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                        }
                    }
                    catch
                    {
                    }
                    break;
                case OperandType.InlineType:
                    metadataToken = ReadInt32(il, ref position);
                    if (mi is MethodInfo)
                    {
                        instruction.Operand = module.ResolveType(metadataToken,
                        mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
                    }
                    else if (mi is ConstructorInfo)
                    {
                        instruction.Operand = module.ResolveType(metadataToken,
                           mi.DeclaringType.GetGenericArguments(), null);
                    }
                    else
                    {
                        instruction.Operand = module.ResolveType(metadataToken);
                    }
                    break;
                case OperandType.InlineI:
                    {
                        instruction.Operand = ReadInt32(il, ref position);
                        break;
                    }
                case OperandType.InlineI8:
                    {
                        instruction.Operand = ReadInt64(il, ref position);
                        break;
                    }
                case OperandType.InlineNone:
                    {
                        instruction.Operand = null;
                        break;
                    }
                case OperandType.InlineR:
                    {
                        instruction.Operand = ReadDouble(il, ref position);
                        break;
                    }
                case OperandType.InlineString:
                    {
                        metadataToken = ReadInt32(il, ref position);
                        instruction.Operand = module.ResolveString(metadataToken);
                        break;
                    }
                case OperandType.InlineSwitch:
                    {
                        int count = ReadInt32(il, ref position);
                        int[] casesAddresses = new int[count];
                        for (int i = 0; i < count; i++)
                        {
                            casesAddresses[i] = ReadInt32(il, ref position);
                        }
                        int[] cases = new int[count];
                        for (int i = 0; i < count; i++)
                        {
                            cases[i] = position + casesAddresses[i];
                        }
                        break;
                    }
                case OperandType.InlineVar:
                    {
                        instruction.Operand = ReadUInt16(il, ref position);
                        break;
                    }
                case OperandType.ShortInlineBrTarget:
                    {
                        instruction.Operand = ReadSByte(il, ref position) + position;
                        break;
                    }
                case OperandType.ShortInlineI:
                    {
                        instruction.Operand = ReadSByte(il, ref position);
                        break;
                    }
                case OperandType.ShortInlineR:
                    {
                        instruction.Operand = ReadSingle(il, ref position);
                        break;
                    }
                case OperandType.ShortInlineVar:
                    {
                        instruction.Operand = ReadByte(il, ref position);
                        break;
                    }
                default:
                    {
                        throw new Exception("Unknown operand type.");
                    }
            }
            return instruction;
        }

        private int ReadInt16(byte[] _il, ref int position)
        {
            return ((_il[position++] | (_il[position++] << 8)));
        }

        private ushort ReadUInt16(byte[] _il, ref int position)
        {
            return (ushort)((_il[position++] | (_il[position++] << 8)));
        }

        private int ReadInt32(byte[] _il, ref int position)
        {
            return (((_il[position++] | (_il[position++] << 8)) | (_il[position++] << 0x10)) | (_il[position++] << 0x18));
        }

        private ulong ReadInt64(byte[] _il, ref int position)
        {
            return (ulong)(((_il[position++] | (_il[position++] << 8)) | (_il[position++] << 0x10)) | (_il[position++] << 0x18) | (_il[position++] << 0x20) | (_il[position++] << 0x28) | (_il[position++] << 0x30) | (_il[position++] << 0x38));
        }

        private double ReadDouble(byte[] _il, ref int position)
        {
            return (((_il[position++] | (_il[position++] << 8)) | (_il[position++] << 0x10)) | (_il[position++] << 0x18) | (_il[position++] << 0x20) | (_il[position++] << 0x28) | (_il[position++] << 0x30) | (_il[position++] << 0x38));
        }

        private sbyte ReadSByte(byte[] _il, ref int position)
        {
            return (sbyte)_il[position++];
        }

        private byte ReadByte(byte[] _il, ref int position)
        {
            return (byte)_il[position++];
        }

        private Single ReadSingle(byte[] _il, ref int position)
        {
            return (Single)(((_il[position++] | (_il[position++] << 8)) | (_il[position++] << 0x10)) | (_il[position++] << 0x18));
        }
    }

    public class ILGlobals
    {
        private OpCode[] multiByteOpCodes;

        private OpCode[] singleByteOpCodes;

        /// <summary>
        /// Loads the OpCodes for later use.
        /// </summary>
        public void LoadOpCodes()
        {
            singleByteOpCodes = new OpCode[0x100];
            multiByteOpCodes = new OpCode[0x100];
            FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
            for (int num1 = 0; num1 < infoArray1.Length; num1++)
            {
                FieldInfo info1 = infoArray1[num1];
                if (info1.FieldType == typeof(OpCode))
                {
                    OpCode code1 = (OpCode)info1.GetValue(null);
                    ushort num2 = (ushort)code1.Value;
                    if (num2 < 0x100)
                    {
                        singleByteOpCodes[(int)num2] = code1;
                    }
                    else
                    {
                        if ((num2 & 0xff00) != 0xfe00)
                        {
                            throw new Exception("Invalid OpCode.");
                        }
                        multiByteOpCodes[num2 & 0xff] = code1;
                    }
                }
            }
        }

        /// <summary>
        /// Retrieve the friendly name of a type
        /// </summary>
        /// <param name="typeName">
        /// The complete name to the type
        /// </param>
        /// <returns>
        /// The simplified name of the type (i.e. "int" instead f System.Int32)
        /// </returns>
        public static string ProcessSpecialTypes(string typeName)
        {
            string result = typeName;
            switch (typeName)
            {
                case "System.string":
                case "System.String":
                case "String":
                    result = "string"; break;
                case "System.Int32":
                case "Int":
                case "Int32":
                    result = "int"; break;
            }
            return result;
        }


        public OpCode[] MultiByteOpCodes
        {
            get { return multiByteOpCodes; }
        }

        public OpCode[] SingleByteOpCodes
        {
            get { return singleByteOpCodes; }
        }
    }

    public class ILInstruction
    {
        private OpCode code;
        private object operand;
        private byte[] operandData;
        private int offset;

        public ILInstruction()
        {
        }


        /// <summary>
        /// 获取指令的类型
        /// </summary>
        public ILInstructionType InstructionType
        {
            get
            {
                if (operand == null)
                    return ILInstructionType.Unknown;


                switch (code.OperandType)
                {
                    case OperandType.InlineField:
                        return ILInstructionType.Field;

                    case OperandType.InlineMethod:
                        {
                            if (operand is System.Reflection.MethodInfo)
                            {
                                return ILInstructionType.Method;
                            }
                            else if (operand is System.Reflection.ConstructorInfo)
                            {
                                return ILInstructionType.Ctor;
                            }
                            else
                            {
                                return ILInstructionType.Unknown;
                            }
                        }

                    case OperandType.ShortInlineBrTarget:
                    case OperandType.InlineBrTarget:
                        return ILInstructionType.Unknown;

                    case OperandType.InlineType:
                        return ILInstructionType.Unknown;

                    case OperandType.InlineString:
                        return ILInstructionType.String;

                    case OperandType.ShortInlineVar:
                        return ILInstructionType.Variable;

                    case OperandType.InlineI:
                    case OperandType.InlineI8:
                    case OperandType.InlineR:
                    case OperandType.ShortInlineI:
                    case OperandType.ShortInlineR:
                        return ILInstructionType.Number;

                    case OperandType.InlineTok:
                        return ILInstructionType.Reference;
                }


                return ILInstructionType.Unknown;
            }
        }

        /// <summary>
        /// 获取对应的方法
        /// </summary>
        /// <returns></returns>
        public MethodInfo GetMethodInfo()
        {
            if (operand == null)
                return null;

            if (InstructionType == ILInstructionType.Method)
                return (System.Reflection.MethodInfo)operand;

            return null;
        }

        /// <summary>
        /// 获取构造函数
        /// </summary>
        /// <returns></returns>
        public ConstructorInfo GetConstructorInfo()
        {
            if (operand == null)
                return null;

            if (InstructionType == ILInstructionType.Ctor)
                return (System.Reflection.ConstructorInfo)operand;

            return null;
        }

        /// <summary>
        /// 获取域反射
        /// </summary>
        /// <returns></returns>
        public FieldInfo GetFieldInfo()
        {
            if (operand == null)
                return null;

            if (InstructionType == ILInstructionType.Field)
                return ((System.Reflection.FieldInfo)operand);

            return null;
        }

        /// <summary>
        /// 获取属性
        /// </summary>
        /// <returns></returns>
        public string GetString()
        {
            return operand.ToString();
        }


        /// <summary>
        /// Add enough zeros to a number as to be represented on 4 characters
        /// </summary>
        /// <param name="offset">
        /// The number that must be represented on 4 characters
        /// </param>
        /// <returns>
        /// </returns>
        private string GetExpandedOffset(long offset)
        {
            string result = offset.ToString();
            for (int i = 0; result.Length < 4; i++)
            {
                result = "0" + result;
            }
            return result;
        }


        public OpCode Code
        {
            get { return code; }
            set { code = value; }
        }

        public object Operand
        {
            get { return operand; }
            set { operand = value; }
        }

        public byte[] OperandData
        {
            get { return operandData; }
            set { operandData = value; }
        }

        public int Offset
        {
            get { return offset; }
            set { offset = value; }
        }

        public override string ToString()
        {
            return string.Format("{0} {1} {2}", offset, code.ToString(), operand);
        }
    }

    public enum ILInstructionType
    {
        Unknown,

        /// <summary>
        /// 内部调用的对象
        /// </summary>
        Field,
        /// <summary>
        /// 内部调用的方法
        /// </summary>
        Method,
        /// <summary>
        /// 内部调用的构造函数方法
        /// </summary>
        Ctor,
        /// <summary>
        /// 内部调用的字符串
        /// </summary>
        String,
        /// <summary>
        /// [没有确定]内部调用的值对象
        /// </summary>
        Number,
        /// <summary>
        /// [没有确定]内部调用的变量
        /// </summary>
        Variable,
        /// <summary>
        /// [没有确定]内部调用的引用
        /// </summary>
        Reference,


        /// <summary>
        /// 集合初始化
        /// </summary>
        ArrayCtor,
        /// <summary>
        /// 对比表达式
        /// </summary>
        Compare,
        /// <summary>
        /// 加载值对象
        /// </summary>
        LoadValue,
        /// <summary>
        /// 加载数组
        /// </summary>
        LoadArray,
        /// <summary>
        /// 根据位置 加载本地变量
        /// </summary>
        LoadLocalVariable,
    }

 

 

这一大片代码,实际上就干了一件事情:

1. 加载需要检测事务的Assembly

2. 对这个Assembly的所有方法进行方法体的遍历

3. 如果方法体内出现了开启事务,但是没有关闭事务,则抛出异常。 

当然,这个方法使用了DotNet传说中的潘多拉魔盒——IL语言。 

 

本人认为,这个是最佳选择, 也是最高级别的方式。当代码编译完成后,首先整个DLL交给这个检测器进行静态反射检测,如果发现了异常,则表示有事务没有完成的地方。

 

后续

------------------------

本文提供了四种方法,保证事务操作的绝对安全。

 

如果是入门级别的,使用1、2即可。 

 

如果是架构师级别的,使用3.

 

如果是骨灰级别的,使用4. 并且非常希望骨灰级别的您,能共享一下您的源代码解决方案。小弟在此谢过了,哈哈哈。

 

 

排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载