1

I have a wcf api and wish to wrap all requests inside a transaction

Currently my code looks like this in each endpoint

public MyCompleteList ReadOrganisations()
    {
        MyCompleteList resp = new MyCompleteList ();
        try
        {
            using (TransactionScope scope = new TransactionScope())
            {
                if (HttpContext.Current.User.Identity.IsAuthenticated)
                {
                    DC_Base browser_request = new DC_Base(PROJECT);
                    browser_request.cmd_user_id = coreDb.GetUserIDFromLoginName(PROJECT,
                        HttpContext.Current.User.Identity.Name);
                    resp =
                        new MyCompleteList (coreSc.User_Read_All_Organisations(browser_request, utils,
                            validation, coreSc, coreDb));
                    scope.Complete();
                }
                else
                {
                    resp.SetResponseNotLoggedIn();
                }

            }
        }
        catch (TransactionAbortedException ex)
        {
            resp.SetResponseServerError();
        }
        catch (ApplicationException ex)
        {
            resp.SetResponseServerError();
        }
        return resp;
    }

As you can see if I am to use the "using" transaction scope part in every endpoint (approx 300) its going to be a lot of duplicated code.

is there anyway to reduce the amount of duplication?

7
  • Yeah create a parent class, move your code in there. Commented Jul 7, 2017 at 10:50
  • how do you mean? can you provide some more details currently this wcf service inherits from an interface to base class to then to a top level class what do you mean by adding a parent class? these functions are called by external clients aka via json Commented Jul 7, 2017 at 10:52
  • Your question is not very clear - how is "using" causing duplication of code? Commented Jul 7, 2017 at 10:54
  • if i want to use a transaction in each endpoint call from a client, then I will need to put using (....) in every endpoint which is alot of duplication the sample is just one of many endpoints offered by our api Commented Jul 7, 2017 at 10:56
  • 1
    Also, please don't use SERIALIZABLE transactions with SQL Server. Change the TransactionScope to READ COMMITTED. See blogs.msdn.microsoft.com/dbrowne/2010/06/03/… Commented Jul 7, 2017 at 13:25

2 Answers 2

2

You can write a helper method, that handles the transaction logic while calling your actual code as a lambda.

    public static T Execute<T>(Func<T> func, TransactionExecutionOptions options = null)
    {
        options = options ?? TransactionExecutionOptions.Default;

        T res;
        using (var tx = new TransactionScope(options))
        {
            res = func();
            tx.Complete();
        }

        return res;
    }

Depending on your needs you can provide additional arguments to the Func argument; for example, the Execute method could also open a database connection and pass that to the func (then having Func<IDbConnection, T> as parameter type). YMMV.

For your example:

public MyCompleteList ReadOrganisations()
{
    MyCompleteList resp = new MyCompleteList ();
    try
    {
        resp = Execute(() => {
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                DC_Base browser_request = new DC_Base(PROJECT);
                browser_request.cmd_user_id = coreDb.GetUserIDFromLoginName(PROJECT,
                    HttpContext.Current.User.Identity.Name);
                resp =
                    new MyCompleteList (coreSc.User_Read_All_Organisations(browser_request, utils,
                        validation, coreSc, coreDb));
                scope.Complete();
            }
            else
            {
                resp.SetResponseNotLoggedIn();
            }
       });
    }
    catch (TransactionAbortedException ex)
    {
        resp.SetResponseServerError();
    }
    catch (ApplicationException ex)
    {
        resp.SetResponseServerError();
    }
    return resp;
}

If possible, you can also factor the SetResponse*() methods out into a base class or interface (say IMyResponse), thus making it possible to handle this aspect inside the Execute method as well.

    public static T Execute<T>(Func<T> func, TransactionExecutionOptions options = null) where T : IMyResponse
    {
        options = options ?? TransactionExecutionOptions.Default;

        T res;
        try
        {
            using (var tx = new TransactionScope(options))
            {
                res = func();
                tx.Complete();
            }
        }
        catch (TransactionAbortedException ex)
        {
            res.SetResponseServerError();
        }
        catch (ApplicationException ex)
        {
            res.SetResponseServerError();
        }
        return res;
    }
Sign up to request clarification or add additional context in comments.

2 Comments

ok this looks like what I was thinking about I can give it a test thanks for the answer.
yes i have the responses already in interfaces which all responses inherit from so this looks even better
1

1- Create a ServiceBase class as follows

public class ServiceBase
    {

       protected void ExecuteOperation(Action codetoExecute)
        {                               
            try
            {               
                 using (TransactionScope scope = new TransactionScope())
                {                   
                    codetoExecute.Invoke();             
                    scope.Complete();                
                }                           
            }
            catch (TransactionAbortedException ex)
            {
                // handle exception
            }
            catch (ApplicationException ex)
            {
                // handle exception
            }            
        }
    }

2- Each new service must inherits from ServiceBase and call ExecuteOperation instead. Code as follows:

ExecuteOperation(() =>
            {                 
                 // Custom code here
            });

3- Atomic transactions are useful when executing operations that dont expect results in return.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.