Click here to Skip to main content
15,885,216 members
Articles / Programming Languages / C#

Combining higher order functions, to get aspect oriented programming benefits in C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
16 Sep 2018CPOL3 min read 8K   5   1
We will use functional programming style of c# to achieve our goal

Introduction

In this article, we will take a look at a technique, which allows to get the benefit of aspect oriented programming in C#. Tools such as PostSharp allows us to do the same, however the tool is not free. So lets roll our implementation using construct provided by C#. We will make use of functional delegates, extention methods construct of C#.

Background

Let's say we are developing a web service(.asmx) which will be consumed by external systems. Below are the non-functional requirement of our system. 

  1. Whenever a request made to our service, we should check whether the client is authenticated using username and password provided in the soap header.
  2. Whenver a service call succeeds, we should return a predefined succeess code to client in soap header.
  3. Whenever an unexpected error happens, we should return a predefined error code to client in soap header.

Naive way of solving the problem:

C#
public class MyService
{
        public SoapHeaderIn header { get; set; }
        public SoapHeaderOut outHeader { get; set; }

        [SoapHeader("header", Direction = SoapHeaderDirection.In)]
        [SoapHeader("outHeader", Direction = SoapHeaderDirection.Out)]
        [WebMethod(Description = "Get MyData")]
        public MyResponse GetMyData(MyRequest request)
        {
            try
            {
                if(IsValidLogin())
                {
                    //dummy function logic, the logic is not very important, ignore it
                    var data = new ServiceManager()
                    .GetMyData(request)
                    .Select(m => m.ConvertToDto())
                    .ToList();

                    return new MyResponse
                    {
                        MyData = data
                    };
                }
                else
                {
                    outHeader = GetServiceResponseForResponseCode(CLIENT_NOTVALID_CREDENTIAL);
                    return null;
                }
            }
            catch(Exception e)
            {
                outHeader = GetServiceResponseForResponseCode(SERVER_ERR_GENERIC);
                return null;
            }
        }
}

Above code meets the requirement, however you are repeating yourself throughout the codebase, any changes in requirement, will make you modify all of your codebase. 

We will try to solve the problem, with the use of higher order functions. Lets define a base service class, which will implement our non-functional requirement as two functions.

Solving the problem using Higher order function:

C#
    public abstract class ServiceBase : System.Web.Services.WebService
    {
        public virtual SoapHeaderIn header { get; set; }
        public virtual SoapHeaderOut outHeader { get; set; }

        public ServiceBase()
        {
            header = new SoapHeaderIn();
            outHeader = new SoapHeaderOut();
        }

        public Func<T> TryCatchWrapper<T>(Func<T> functionToExcute) where T : class
        {
            Func<T> tryBlockWrapper = () =>
            {
                try
                {
                    return functionToExcute();
                }
                catch (Exception e)
                {
                    Log(e);
                    outHeader = GetServiceResponseForResponseCode(ResponseCodeEnum.SERVER_ERR_GENERIC);
                    return null;
                }

            };

            return tryBlockWrapper;
        }

        public Func<T> AuthenticationWrapper<T>(Func<T> functionToExcute) where T : class
        {
            Func<T> authenticationBlockWrapper = () =>
            {
                if (IsValidLogin())
                {
                    var result = functionToExcute();
                    outHeader = GetServiceResponseForResponseCode(SUCCESS);
                    return result;
                }
                else
                {
                    outHeader = GetServiceResponseForResponseCode(CLIENT_NOTVALID_CREDENTIAL);
                    return null;
                }

            };

            return authenticationBlockWrapper;
        }

        public Func<T> ExecuteServiceRequest<T>(Func<T> functionToExcute) where T:class
        {
            return TryCatchWrapper(() =>
            {
                return AuthenticationWrapper(() =>
                {
                    return functionToExcute();
                });
            })();
        }
    }

AuthenticationWrapper method takes a function as input, returns a function as output, which sourrounds the input function with authentication logic and if authentication succeeds,
it invokes the input method, else it sets soap header with invalid credential error code and returns immediatly.

TryCatchWrapper method, works almost like AuthenticationWrapper, instead of authentication logic, it sourrounds the input function with Try Catch logic.

ExecuteServiceRequest method takes a function as input, and sourrounds it with both AuthenticationWrapper, and TryCatchWrapper.

Let see how we can use the ExecuteServiceRequest method in our service method

C#
 public class MyService
 {
        public SoapHeaderIn header { get; set; }
        public SoapHeaderOut outHeader { get; set; }

        [SoapHeader("header", Direction = SoapHeaderDirection.In)]
        [SoapHeader("outHeader", Direction = SoapHeaderDirection.Out)]
        [WebMethod(Description = "Get MyData")]
        public MyResponse GetMyData(MyRequest request)
        {
            
            return ExecuteServiceRequest(() =>
            {
                //dummy function body ignore it for the purpose of this article
                var data = new ServiceManager()
                    .GetData(request)
                    .Select(m => m.ConvertToDto())
                    .ToList();

                return new MyResponse
                {
                    MyData = data
                };
            })();

        }    
}

Now any changes in non-functional requirement related to authentication, error handling can be fixed in one place, instead of going througout code base; but if we add a new requirement that, all service request should be logged, we have to add one more wrapper method called LogWrapper, which will function similar to AuthenticationWrapper, TryCatchWrapper with its own logging logic. However either we have to modify ExecuteServiceRequest method or add one more method called ExecuteServiceWithLog which will wrap input function with the all three wrapper functions.

Both approach has few problems.

  1. If we modify ExecuteService to include all wrappers, then we lose the ability to selectively to not use logging for certain service methods.
  2. What if we want to change the wrapping order, If we want to execute logging logic before authentication logic, again we need to add one more function like ExecuteServiceWithMyOrder or we have to modify the existing ExecuteServiceRequest function. Again we lose the ability, to selectively change the order of execution for certain service methods.
  3. If we keep adding new functions like ExecuteServiceWithWhateverRequirement foreach requirement, then it will make code base grow very quickly, maintaining the code will become harder problem.

We will try to solve the problem. Lets imagine a extention method called WrapInside exists, which will take variable number of functions as input and will return combined function as output. 
For example:

func1.WrapInside(AuthenticationWrapper,TryCatchWrapper), will return TryCatchWrapper(AuthenticationWrapper(func1)) as output.

The order of execution of the function is left to right, which means

func1.WrapInside(TryCatchWrapper,AuthenticationWrapper), will return AuthenticationWrapper(TryCatchWrapper(func1)) as output.

Now lets implement such a function.

C#
/// <summary>
/// This function will surround funcTobeWrapped function with given wrapper functions.
/// Wrapper functions should always have input and output similar to funcTobeWrapped output.
/// Wrapper functions will be applied from left to right.
/// For example: functTobeWrapped.WrapWith(fun1,fun2), will return fun2(fun1(funcTobeWrapped)).
/// This function will accept n number of wrapperfunction, and only one function to be wrapped.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="funcTobeWrapped"></param>
/// <param name="wrapperFuncs"></param>
/// <returns>Returns a function where funcTobeWrapped nested inside with wrapperFuns</returns>
public static Func<T> WrapInside<T>(this Func<T> funcTobeWrapped, params Func<Func<T>, Func<T>>[] wrapperFuncs) where T : class
{
    var length = wrapperFuncs.Length - 1;

    if (length == 0)
        return wrapperFuncs[0](() => funcTobeWrapped());

    return wrapperFuncs[length](() => funcTobeWrapped.WrapInside(wrapperFuncs.Take(length).ToArray())());
}

WrapInside function apply supplied wrapper function with the use of recursion. The 0th index wrapper is the one which will be invoking original function, all other functions will be invoking the previous wrapper functions.

Let see how we can make use of the function.

C#
 public class MyService
 {
        public SoapHeaderIn header { get; set; }
        public SoapHeaderOut outHeader { get; set; }

        [SoapHeader("header", Direction = SoapHeaderDirection.In)]
        [SoapHeader("outHeader", Direction = SoapHeaderDirection.Out)]
        [WebMethod(Description = "Get MyData")]
        public MyResponse GetMyData(MyRequest request)
        {
            
            //dummy function body ignore it for the purpose of this article
            Func<MyResponse> getData = () =>
            {
                var data = new ServiceManager()
                    .GetData(request)
                    .Select(m => m.ConvertToDto())
                    .ToList();

                return new MyResponse
                {
                    MyData = data
                };
            };

            return getData.WrapInside(base.AuthenticationWrapper, base.TryCatchWrapper)();
        }

 }

Now we solved problems which is mentioned in the previous approach. 

  1. Adding new wrapping logic is easy, you define new wrapping logic function ,and just pass it to the WrapInside. (Change required in codebase is very minimal and maintainable)
  2. We can easily change order of wrapper functions.

However the solution provided here is not generalized solution, I hope this solution will provide others direction for generalized solution.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNice Pin
Marc Clifton16-Sep-18 3:22
mvaMarc Clifton16-Sep-18 3:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.