2
\$\begingroup\$

Because the ambient transaction isn't supported with informix, I pass the transaction and the connection through my methods.

I want to ask about three things:

  1. Is the following code written well? I mean, no redundant steps and no logical errors.
  2. Is calling this transaction method with many null parameters in a specific case okay?
  3. Is there a better way to handle this problem?

 public static int Insert(string processMethod, object[] processParameters, Type processType, object process, UserTransactionDTO transObj, string spPostConfirm, int toEmpNum,int confirmState)
        {
            int affectedRows = -7;
            using (IfxConnection conn = new IfxConnection(ConfigurationManager.ConnectionStrings["crms"].ToString() + " Enlist=true;"))
            {
                if (conn.State == ConnectionState.Closed)
                {
                    conn.Open();
                }
                using (IfxTransaction tran = conn.BeginTransaction())
                {

                    if (!string.IsNullOrEmpty(processMethod))//business Method
                    {
                        processParameters[1] = conn;
                        processParameters[2] = tran;
                        MethodInfo theMethod = processType.GetMethod(processMethod, new[] { processParameters.First().GetType(), typeof(IfxConnection), typeof(IfxTransaction) });
                        object res = theMethod.Invoke(process, processParameters);
                        transObj.ValuesKey = res.ToString();
                    }
                    if (!string.IsNullOrEmpty(transObj.ValuesKey))
                    {
                        affectedRows = RunPreConfirm(transObj.TaskCode, transObj.UserStateCode, transObj.ValuesKey, conn, tran, confirmState);//sp_confirm
                        if (affectedRows != 1)
                        {
                            tran.Rollback();
                            tran.Dispose();//Dispose
                            conn.Close();
                            conn.Dispose();
                            return -1;//Fail
                        }
                        affectedRows = InsertTrans(transObj, conn, tran);//MainTransaction --->df2usertrans

                        if (affectedRows == 1)//Success
                        {

                            if (!string.IsNullOrEmpty(spPostConfirm))
                            {
                                affectedRows = RunPostConfirm(spPostConfirm, transObj.ValuesKey, conn, tran);//sp_post_confirm
                                if (affectedRows != 0)
                                {
                                    tran.Rollback();
                                    tran.Dispose();//Dispose
                                    conn.Close();
                                    conn.Dispose();
                                    return -2;//Fail 
                                }

                            }

                            affectedRows = RunAfterTrans(transObj.TaskCode, transObj.OldStatusCode, transObj, toEmpNum, conn, tran);//sp_after_trans
                            if (affectedRows != 1)
                            {
                                tran.Rollback();
                                tran.Dispose();//Dispose
                                conn.Close();
                                conn.Dispose();
                                return -3;//Fail
                            }

                            tran.Commit();
                            tran.Dispose();
                            conn.Close();
                            conn.Dispose();
                            return 1;

                        }
                        else
                        {
                            tran.Rollback();
                            tran.Dispose();//Dispose
                            conn.Close();
                            conn.Dispose();
                            return -1;//Fail 
                        }
                    }
                    else
                    {
                        tran.Rollback();
                        tran.Dispose();//Dispose
                        conn.Close();
                        conn.Dispose();
                        return -1;//Fail 
                    }
                }
            }
            return affectedRows;
        }

Examples for calling:

int res = DocumentFlowModuleDAL.UserTransactionDAL.Insert("InsertRequest", reqObj, typeof(EnhancementRequest), new EnhancementRequest(), transObj, string.Empty, 0,0);

result = UserTransactionDAL.Insert(string.Empty, null, null, null, obj, sp_PostConfirm, x, 0);
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Do not return error code if possible. If the method fails to do what it needs to do, just throw an exception. In your case, do not return -1, -2, etc, you should create new type of exception and wrap your error code inside.

The using clause guarantees that Dispose() of the object you use will be called , even in case of exception, before exiting the enclosed block, so all Close() and Dispose() in your code is redundant.

In C#, transaction usage usually goes like

using(var tran = conn.BeginTransaction()) {
    try {
        ...
        // your code here
        ...
        tran.Commit();
    }
    catch {
        tran.Rollback();
        throw;
    }
}

This will work well when you do not return error code.

Edit: Here is an example of the exception class

public enum YourErrorCode {
    Unknown,
    ErrorCode1,
    ErrorCode2,
}

public class YourException : Exception {
    public YourErrorCode ErrorCode { get; private set; }

    public YourException()
    {
    }

    public YourException(string message)
        : base(message)
    {
    }

    public YourException(string message, Exception inner)
        : base(message, inner)
    {
    }

    public YourException(YourErrorCode errorCode) : this(errorCode, null)
    {
    }

    public YourException(YourErrorCode errorCode, Exception inner)
        : base("The operation failed with error code " + errorCode.ToString(), inner)
    {
        this.ErrorCode = errorCode;
    }

}

And throw it like

throw new YourException(YourErrorCode.ErrorCode1);

instead of returning error code.

\$\endgroup\$
3
  • \$\begingroup\$ thanks ,i know that The using clause is an automated way to call Dispose() on the object you use but if my internal method failed to insert before } does the connection and the transaction dispose or not ? Please explain with code what you mean by you should create new type of exception and wrap your error code inside. \$\endgroup\$ Commented Mar 8, 2015 at 19:00
  • 1
    \$\begingroup\$ I added an example of the exception, and fix using explanation. And yes the using clause guarantees that Dispose() will be called even in case of exceptions. \$\endgroup\$
    – tia
    Commented Mar 8, 2015 at 23:51
  • \$\begingroup\$ thanks a lot,i return value because i show the end user error message according to the label , how to show the user error message after throwing exception ? \$\endgroup\$ Commented Mar 17, 2015 at 12:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.