RESTful Day #6: Request logging and Exception handling/logging in Web APIs using Action Filters, Exception Filters and NLog


Download Source Code from GitHub

Introduction

We have been learning a lot about WebAPI, its uses, implementations, and security aspects since last five articles of the series. This article of the series will explain how we can handle requests and log them for tracking and for the sake of debugging, how we can handle exceptions and log them. We’ll follow a centralized way of handling exceptions in WebAPI and write our custom classes to be mapped to the type of exception that we encounter and log the accordingly. I’ll use NLog to log requests and exceptions as well. We’ll leverage the capabilities of Exception Filters and Action Filters to centralize request logging and exception handling in WebAPI.

Roadmap

The following is the roadmap I have setup to learn WebAPI step by step,
I’ll purposely use Visual Studio 2010 and .NET Framework 4.0 because there are few implementations that are very hard to find in .NET Framework 4.0, but I’ll make it easy by showing how we can do it.

Request Logging

Since we are writing web services, we are exposing our end points. We must know where the requests are coming from and what requests are coming to our server. Logging could be very beneficial and helps us in a lot of ways like, debugging, tracing, monitoring and analytics.

We already have an existing design. If you open the solution, you’ll get to see the structure as mentioned below or one can also implement this approach in their existing solution as well.

Setup NLog in WebAPI

NLog serves various purposes but primarily logging. We’ll use NLog for logging into files and windows event as well. You can read more about NLog at http://NLog-project.org/
One can use the sample application that we used in Day#5 or can have any other application as well. I am using the existing sample application that we were following throughout all the parts of this series. Our application structure looks something like:

Step 1: Download NLog Package

Right click WebAPI project and select manage Nuget Packages from the list. When the Nuget Package Manager appears, search for NLog. You’ll get Nlog like shown in image below, just install it to our project.

After adding this you will find following NLog dll referenced in your application.

Step 2: Configuring NLog

To configure NLog with application add following settings in our existing WebAPI web.config file,

ConfigSection –

Configuration Section – I have added the section to configuration and defined the path and format dynamic target log file name, also added the eventlog source to Api Services.

As mentioned in above target path, I have also created to “APILog” folder in the base directory of application.

Now we have configured the NLog in our application, and it is ready to start work for request logging. Note that in the rules section we have defined rules for logging in files as well as in windows events log as well, you can choose both of them or can opt for one too. Let’s start with logging request in application, with action filters –

NLogger Class

Add a folder “Helpers” in the API, which will segregate the application code for readability, better understanding and maintainability.

To start add our main class “NLogger“, which will responsible for all types of errors and info logging, to same Helper folder. Here NLogger class implements ITraceWriter interface, which provides “Trace” method for the service request.
#region Using namespaces.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Tracing;
using NLog;
using System.Net.Http;
using System.Text;
using WebApi.ErrorHelper;
#endregion

namespace WebApi.Helpers
{
    /// <summary>
    /// Public class to log Error/info messages to the access log file
    /// </summary>
    public sealed class NLogger : ITraceWriter
    {
        #region Private member variables.
        private static readonly Logger ClassLogger = LogManager.GetCurrentClassLogger();

        private static readonly Lazy<Dictionary<TraceLevel, Action<string>>> LoggingMap = new Lazy<Dictionary<TraceLevel, Action<string>>>(() => new Dictionary<TraceLevel, Action<string>> { { TraceLevel.Info, ClassLogger.Info }, { TraceLevel.Debug, ClassLogger.Debug }, { TraceLevel.Error, ClassLogger.Error }, { TraceLevel.Fatal, ClassLogger.Fatal }, { TraceLevel.Warn, ClassLogger.Warn } });
        #endregion

        #region Private properties.
        /// <summary>
        /// Get property for Logger
        /// </summary>
        private Dictionary<TraceLevel, Action<string>> Logger
        {
            get { return LoggingMap.Value; }
        }
        #endregion

        #region Public member methods.
        /// <summary>
        /// Implementation of TraceWriter to trace the logs.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="category"></param>
        /// <param name="level"></param>
        /// <param name="traceAction"></param>
        public void Trace(HttpRequestMessage request, string category, TraceLevel level, Action traceAction)
        {
            if (level != TraceLevel.Off)
            {
                if (traceAction != null && traceAction.Target != null)
                {
                    category = category + Environment.NewLine + "Action Parameters : " + traceAction.Target.ToJSON();
                }
                var record = new TraceRecord(request, category, level);
                if (traceAction != null) traceAction(record);
                Log(record);
            }
        }
        #endregion

        #region Private member methods.
        /// <summary>
        /// Logs info/Error to Log file
        /// </summary>
        /// <param name="record"></param>
        private void Log(TraceRecord record)
        {
            var message = new StringBuilder();

            if (!string.IsNullOrWhiteSpace(record.Message))
                message.Append("").Append(record.Message + Environment.NewLine);

            if (record.Request != null)
            {
                if (record.Request.Method != null)
                    message.Append("Method: " + record.Request.Method + Environment.NewLine);

                if (record.Request.RequestUri != null)
                    message.Append("").Append("URL: " + record.Request.RequestUri + Environment.NewLine);

                if (record.Request.Headers != null && record.Request.Headers.Contains("Token") && record.Request.Headers.GetValues("Token") != null && record.Request.Headers.GetValues("Token").FirstOrDefault() != null)
                    message.Append("").Append("Token: " + record.Request.Headers.GetValues("Token").FirstOrDefault() + Environment.NewLine);
            }

            if (!string.IsNullOrWhiteSpace(record.Category))
                message.Append("").Append(record.Category);

            if (!string.IsNullOrWhiteSpace(record.Operator))
                message.Append(" ").Append(record.Operator).Append(" ").Append(record.Operation);

            
            Logger[record.Level](Convert.ToString(message) + Environment.NewLine);
        }
        #endregion
    }
}

Adding Action Filter

Action filter will be responsible for handling all the incoming requests to our APIs and logging them using NLogger class. We have “OnActionExecuting” method that is implicitly called if we mark our controllers or global application to use that particular filter. So each time any action of any controller will be hit, our “OnActionExecuting” method will execute to log the request.

Step 1: Adding LoggingFilterAttribute class

Create a class LoggingFilterAttribute to “ActionFilters” folder and add following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Filters;
using System.Web.Http.Controllers;
using System.Web.Http.Tracing;
using System.Web.Http;
using WebApi.Helpers;


namespace WebApi.ActionFilters
{
    public class LoggingFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext filterContext)
        {
            GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new NLogger());
            var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter();
            trace.Info(filterContext.Request, "Controller : " + filterContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine + "Action : " + filterContext.ActionDescriptor.ActionName, "JSON", filterContext.ActionArguments);
        }
    }
}
The LoggingFilterAttribute class derived from ActionFilterAttribute, which is underSystem.Web.Http.Filters and overriding the OnActionExecuting method.
Here I have replaced the default “ITraceWriter” service with our NLogger class instance in the controller’s service container. Now GetTraceWriter() method will return our instance (instance NLogger class) and Info() will call trace() method of our NLogger class.
Note that the code below.
GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new NLogger());
is used to resolve dependency between ITaceWriter and NLogger class. Thereafter we use a variable namedtrace to get the instance and trace.Info() is used to log the request and whatever text we want to add along with that request.

Step 2: Registering Action Filter (LoggingFilterAttribute)

In order to register the created action filter to application’s filters, just add a new instance of your action filter toconfig.Filters in WebApiConfig class.
using System.Web.Http;
using WebApi.ActionFilters;

namespace WebApi.App_Start
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Filters.Add(new LoggingFilterAttribute());
        }
    }
}
Now this action filter is applicable to all the controllers and actions in our project. You may not believe but request logging is done. It’s time to run the application and validate our homework.

Running the application

Let’s run the application and try to make a call, using token based authorization, we have already covered authorization in day#5. You first need to authenticate your request using login service and then that service will return a token for making calls to other services. Use that token to make calls to other services. For more details you can read day 5 of this series.
Just run the application, we get
We already have our test client added, but for new readers, just go to Manage Nuget Packages, by right clicking WebAPI project and typing WebAPITestClient in searchbox in online packages
You’ll get “A simple Test Client for ASP.NET Web API”, just add it. You’ll get a help controller in Areas-> HelpPage like shown below:
I have already provided the database scripts and data in my previous article, you can use the same.
Append “/help” in the application url, and you’ll get the test client,
GET:
POST:
PUT:
DELETE:
You can test each service by clicking on it. Once you click on the service link, you’ll be redirected to test the service page of that particular service. On that page there is a button Test API in the right bottom corner, just press that button to test your service:
Service for Get All products:
In the below case, I have already generated the token and now I am using it to make call to fetch all the products from the products table in the database.

Here I have called allproducts API, Add the value for parameter Id and “Token” header with its current value and click to get the result:

Now let’s see what happens to our APILog folder in application. Here you find the API log has been created, with the same name we have configured in NLog configuration in web.config file. The log file contains all the supplied details like Timestamp, Method type, URL , Header information (Token), Controller name, action and action parameters. You can also add more details to this log which you deem important for your application.

Logging Done!

Exception Logging

Our logging setup is completed, now we’ll focus on centralizing exception logging as well, so that none of the exception escapes without logging itself. Logging exception is of very high importance, it keeps track of all the exceptions. No matter business or application or system exceptions, all of them have to be logged.

Implementing Exception logging

Step 1: Exception Filter Attribute

Now we will add an action filter in our application for logging the exceptions. For this create a class,GlobalExceptionAttribute to “ActionFilter” folder and add the code below, the class is derived fromExceptionFilterAttribute, which is under System.Web.Http.Filters.
I override the OnException() method and replace the default “ITraceWriter” service with our NLogger class instance in the controller’s service container, same as we have done in Action logging in above section. Now theGetTraceWriter() method will return our instance (instance NLogger class) and Info() will call trace()method of NLogger class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Filters;
using System.Web.Http;
using System.Web.Http.Tracing;
using WebApi.Helpers;
using System.ComponentModel.DataAnnotations;
using System.Net.Http;
using System.Net;

namespace WebApi.ActionFilters
{
    /// <summary>
    /// Action filter to handle for Global application errors.
    /// </summary>
    public class GlobalExceptionAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new NLogger());
            var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter();
            trace.Error(context.Request, "Controller : " + context.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine + "Action : " + context.ActionContext.ActionDescriptor.ActionName, context.Exception);

            var exceptionType = context.Exception.GetType();

            if (exceptionType == typeof(ValidationException))
            {
                var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(context.Exception.Message), ReasonPhrase = "ValidationException", };
                throw new HttpResponseException(resp);

            }
            else if (exceptionType == typeof(UnauthorizedAccessException))
            {
                throw new HttpResponseException(context.Request.CreateResponse(HttpStatusCode.Unauthorized));
            }
            else
            {
                throw new HttpResponseException(context.Request.CreateResponse(HttpStatusCode.InternalServerError));
            }
        }
    }
}

Step 2: Modify NLogger Class

Our NLogger class is capable to log all info and events, I have done some changes in private method Log() to handle the exceptions
#region Private member methods.
/// <summary>
/// Logs info/Error to Log file
/// </summary>
/// <param name="record"></param>
private void Log(TraceRecord record)
{
var message = new StringBuilder();

if (!string.IsNullOrWhiteSpace(record.Message))
                message.Append("").Append(record.Message + Environment.NewLine);

      if (record.Request != null)
{
       if (record.Request.Method != null)
            message.Append("Method: " + record.Request.Method + Environment.NewLine);

                if (record.Request.RequestUri != null)
                    message.Append("").Append("URL: " + record.Request.RequestUri + Environment.NewLine);

                if (record.Request.Headers != null && record.Request.Headers.Contains("Token") && record.Request.Headers.GetValues("Token") != null && record.Request.Headers.GetValues("Token").FirstOrDefault() != null)
                    message.Append("").Append("Token: " + record.Request.Headers.GetValues("Token").FirstOrDefault() + Environment.NewLine);
            }

            if (!string.IsNullOrWhiteSpace(record.Category))
                message.Append("").Append(record.Category);

            if (!string.IsNullOrWhiteSpace(record.Operator))
                message.Append(" ").Append(record.Operator).Append(" ").Append(record.Operation);

            if (record.Exception != null && !string.IsNullOrWhiteSpace(record.Exception.GetBaseException().Message))
            {
                var exceptionType = record.Exception.GetType();
                message.Append(Environment.NewLine);
                message.Append("").Append("Error: " + record.Exception.GetBaseException().Message + Environment.NewLine);
            }

            Logger[record.Level](Convert.ToString(message) + Environment.NewLine);
        }

Step 3: Modify Controller for Exception

Our application is now ready to run, but there is no exception in our code, so I added a throw exception code inProductController, just the Get(int id) method so that it can throw exception for testing our exception logging mechanism. It will throw an exception if the product is not there in database with the provided id.
  // GET api/product/5
 [GET("productid/{id?}")]
 [GET("particularproduct/{id?}")]
 [GET("myproduct/{id:range(1, 3)}")]
 public HttpResponseMessage Get(int id)
 {
var product = _productServices.GetProductById(id);
      if (product != null)
       return Request.CreateResponse(HttpStatusCode.OK, product);

 throw new Exception("No product found for this id");
      //return Request.CreateErrorResponse(HttpStatusCode.NotFound,   "No product found for this id");
 }

Step 4: Run the application

Run the application and click on Product/all API
Add the parameter id value to 1 and header Token with it’s current value, click on send button to get the result:
Now we can see that the Status is 200/OK, and we also get a product with the provided id in the response body. Let’s see the API log now:
The log has captured the call of Product API, now provide a new product id as parameter, which is not there in database, I am using 12345 as product id and result is:

We can see there is an 500/Internal Server Error now in response status, let’s check the API Log:

Well, now the log has captured both the event and error of same call on the server, you can see call log details and the error with provided error message in the log.

Custom Exception logging

In the above section we have implemented exception logging, but there is default system response and status (i.e. 500/Internal Server Error). It will be always good to have your own custom response and exceptions for your API. That will be easier for client to consume and understand the API responses.

Step 1: Add Custom Exception Classes

Add “Error Helper” folder to application to maintain our custom exception classes separately and add “IApiExceptions” interface to newly created “ErrorHelper” folder –
Add following code the IApiExceptions interface, this will serve as a template for all exception classes, I have added four common properties for our custom classes to maintain Error Code, ErrorDescription, HttpStatus (Contains the values of status codes defined for HTTP) and ReasonPhrase.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace WebApi.ErrorHelper
{
    /// <summary>
    /// IApiExceptions Interface
    /// </summary>
    public interface IApiExceptions
    {
        /// <summary>
        /// ErrorCode
        /// </summary>
        int ErrorCode { get; set; }
        /// <summary>
        /// ErrorDescription
        /// </summary>
        string ErrorDescription { get; set; }
        /// <summary>
        /// HttpStatus
        /// </summary>
        HttpStatusCode HttpStatus { get; set; }
        /// <summary>
        /// ReasonPhrase
        /// </summary>
        string ReasonPhrase { get; set; }
    }
}
Here, I divided our exceptions in three categories:
  1. API Exceptions – for API level exceptions.
  2. Business Exceptions – for exceptions at business logic level.
  3. Data Exceptions – Data related exceptions.
To implement this create a three new classes ApiException.cs, ApiDataException.cs and ApiBusinessExceptionclasses to same folder which implements IApiExceptions interface with following code to the classes.
#region Using namespaces.
using System;
using System.Net;
using System.Runtime.Serialization;
#endregion


namespace WebApi.ErrorHelper
{
    /// <summary>
    /// Api Exception
    /// </summary>
    [Serializable]
    [DataContract]
    public class ApiException : Exception, IApiExceptions
    {
        #region Public Serializable properties.
        [DataMember]
        public int ErrorCode { get; set; }
        [DataMember]
        public string ErrorDescription { get; set; }
        [DataMember]
        public HttpStatusCode HttpStatus { get; set; }
        
        string reasonPhrase = "ApiException";

        [DataMember]
        public string ReasonPhrase
        {
            get { return this.reasonPhrase; }

            set { this.reasonPhrase = value; }
        }
        #endregion
    }
}
I have initialized ReasonPhrase property with different default values in these classes to differentiate the implementation, you can use implement your custom classes as per your application needs.
The directives applied on class as Serializable and DataContract to make sure that the class defines or implements a data contract is serializable and can be serialize by a serializer.
Note: Add reference of “System.Runtime.Serialization.dll” DLL if you facing any assembly issue.
In the same way add “ApiBusinessException” and “ApiDataException” classes into the same folder, with the following code –
#region Using namespaces.
using System;
using System.Net;
using System.Runtime.Serialization; 
#endregion

namespace WebApi.ErrorHelper
{
    /// <summary>
    /// Api Business Exception
    /// </summary>
    [Serializable]
    [DataContract]
    public class ApiBusinessException : Exception, IApiExceptions
    {
        #region Public Serializable properties.
        [DataMember]
        public int ErrorCode { get; set; }
        [DataMember]
        public string ErrorDescription { get; set; }
        [DataMember]
        public HttpStatusCode HttpStatus { get; set; }

        string reasonPhrase = "ApiBusinessException";

        [DataMember]
        public string ReasonPhrase
        {
            get { return this.reasonPhrase; }

            set { this.reasonPhrase = value; }
        }
        #endregion

        #region Public Constructor.
        /// <summary>
        /// Public constructor for Api Business Exception
        /// </summary>
        /// <param name="errorCode"></param>
        /// <param name="errorDescription"></param>
        /// <param name="httpStatus"></param>
        public ApiBusinessException(int errorCode, string errorDescription, HttpStatusCode httpStatus)
        {
            ErrorCode = errorCode;
            ErrorDescription = errorDescription;
            HttpStatus = httpStatus;
        } 
        #endregion

    }
}

#region Using namespaces.
using System;
using System.Net;
using System.Runtime.Serialization;
#endregion

namespace WebApi.ErrorHelper
{
    /// <summary>
    /// Api Data Exception
    /// </summary>
    [Serializable]
    [DataContract]
    public class ApiDataException : Exception, IApiExceptions
    {
        #region Public Serializable properties.
        [DataMember]
        public int ErrorCode { get; set; }
        [DataMember]
        public string ErrorDescription { get; set; }
        [DataMember]
        public HttpStatusCode HttpStatus { get; set; }

        string reasonPhrase = "ApiDataException";

        [DataMember]
        public string ReasonPhrase
        {
            get { return this.reasonPhrase; }

            set { this.reasonPhrase = value; }
        }

        #endregion

        #region Public Constructor.
        /// <summary>
        /// Public constructor for Api Data Exception
        /// </summary>
        /// <param name="errorCode"></param>
        /// <param name="errorDescription"></param>
        /// <param name="httpStatus"></param>
        public ApiDataException(int errorCode, string errorDescription, HttpStatusCode httpStatus)
        {
            ErrorCode = errorCode;
            ErrorDescription = errorDescription;
            HttpStatus = httpStatus;
        }
        #endregion
    }
}

JSon Serializers

There are some objects need to be serialized in JSON, to log and to transfer through the modules, for this I have add some extension methods to Object class.
For that add “System.Web.Extensions.dll” reference to project and add “JSONHelper” class to Helpers folder, with following code:
#region Using namespaces.
using System.Web.Script.Serialization;
using System.Data;
using System.Collections.Generic;
using System;

#endregion

namespace WebApi.Helpers
{
    public static class JSONHelper
    {
         #region Public extension methods.
        /// <summary>
        /// Extened method of object class, Converts an object to a json string.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToJSON(this object obj)
        {
            var serializer = new JavaScriptSerializer();
            try
            {
                return serializer.Serialize(obj);
            }
            catch(Exception ex)
            {
                return "";
            }
        }
         #endregion
    }
}
In the above code “ToJSON()” method is an extension of base Object class, which serializes supplied the object to a JSON string. The method using “JavaScriptSerializer” class which exists in “System.Web.Script.Serialization“.

Modify NLogger Class

For exception handling I have modified the Log() method of NLogger, which will now handle the different API exceptions.
/// <summary>
/// Logs info/Error to Log file
/// </summary>
/// <param name="record"></param>
private void Log(TraceRecord record)
{
var message = new StringBuilder();

      if (!string.IsNullOrWhiteSpace(record.Message))
                message.Append("").Append(record.Message + Environment.NewLine);

            if (record.Request != null)
            {
                if (record.Request.Method != null)
                    message.Append("Method: " + record.Request.Method + Environment.NewLine);

                if (record.Request.RequestUri != null)
                    message.Append("").Append("URL: " + record.Request.RequestUri + Environment.NewLine);

                if (record.Request.Headers != null && record.Request.Headers.Contains("Token") && record.Request.Headers.GetValues("Token") != null && record.Request.Headers.GetValues("Token").FirstOrDefault() != null)
                    message.Append("").Append("Token: " + record.Request.Headers.GetValues("Token").FirstOrDefault() + Environment.NewLine);
            }

            if (!string.IsNullOrWhiteSpace(record.Category))
                message.Append("").Append(record.Category);

            if (!string.IsNullOrWhiteSpace(record.Operator))
                message.Append(" ").Append(record.Operator).Append(" ").Append(record.Operation);

            if (record.Exception != null && !string.IsNullOrWhiteSpace(record.Exception.GetBaseException().Message))
            {
                var exceptionType = record.Exception.GetType();
                message.Append(Environment.NewLine);
                if (exceptionType == typeof(ApiException))
                {
                    var exception = record.Exception as ApiException;
                    if (exception != null)
                    {
                        message.Append("").Append("Error: " + exception.ErrorDescription + Environment.NewLine);
                        message.Append("").Append("Error Code: " + exception.ErrorCode + Environment.NewLine);
                    }
                }
                else if (exceptionType == typeof(ApiBusinessException))
                {
                    var exception = record.Exception as ApiBusinessException;
                    if (exception != null)
                    {
                        message.Append("").Append("Error: " + exception.ErrorDescription + Environment.NewLine);
                        message.Append("").Append("Error Code: " + exception.ErrorCode + Environment.NewLine);
                    }
                }
                else if (exceptionType == typeof(ApiDataException))
                {
                    var exception = record.Exception as ApiDataException;
                    if (exception != null)
                    {
                        message.Append("").Append("Error: " + exception.ErrorDescription + Environment.NewLine);
                        message.Append("").Append("Error Code: " + exception.ErrorCode + Environment.NewLine);
                    }
                }
                else
                    message.Append("").Append("Error: " + record.Exception.GetBaseException().Message + Environment.NewLine);
            }

            Logger[record.Level](Convert.ToString(message) + Environment.NewLine);
        }
The code above checks the exception object of TraceRecord and updates the logger as per the exception type.

Modify GlobalExceptionAttribute

As we have created GlobalExceptionAttribute to handle all exceptions and create response in case of any exception. Now I have added some new code to this in order to enable the GlobalExceptionAttribute class to handle custom exceptions. I am adding only modified method here for your reference .
public override void OnException(HttpActionExecutedContext context)
{
         GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new NLogger());
            var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter();
            trace.Error(context.Request, "Controller : " + context.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine + "Action : " + context.ActionContext.ActionDescriptor.ActionName, context.Exception);

            var exceptionType = context.Exception.GetType();

            if (exceptionType == typeof(ValidationException))
            {
                var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(context.Exception.Message), ReasonPhrase = "ValidationException", };
                throw new HttpResponseException(resp);

            }
            else if (exceptionType == typeof(UnauthorizedAccessException))
            {
                throw new HttpResponseException(context.Request.CreateResponse(HttpStatusCode.Unauthorized, new ServiceStatus() { StatusCode = (int)HttpStatusCode.Unauthorized, StatusMessage = "UnAuthorized", ReasonPhrase = "UnAuthorized Access" }));
            }
            else if (exceptionType == typeof(ApiException))
            {
                var webapiException = context.Exception as ApiException;
                if (webapiException != null)
                    throw new HttpResponseException(context.Request.CreateResponse(webapiException.HttpStatus, new ServiceStatus() { StatusCode = webapiException.ErrorCode, StatusMessage = webapiException.ErrorDescription, ReasonPhrase = webapiException.ReasonPhrase }));
            }
            else if (exceptionType == typeof(ApiBusinessException))
            {
                var businessException = context.Exception as ApiBusinessException;
                if (businessException != null)
                    throw new HttpResponseException(context.Request.CreateResponse(businessException.HttpStatus, new ServiceStatus() { StatusCode = businessException.ErrorCode, StatusMessage = businessException.ErrorDescription, ReasonPhrase = businessException.ReasonPhrase }));
            }
            else if (exceptionType == typeof(ApiDataException))
            {
                var dataException = context.Exception as ApiDataException;
                if (dataException != null)
                    throw new HttpResponseException(context.Request.CreateResponse(dataException.HttpStatus, new ServiceStatus() { StatusCode = dataException.ErrorCode, StatusMessage = dataException.ErrorDescription, ReasonPhrase = dataException.ReasonPhrase }));
            }
            else
            {
                throw new HttpResponseException(context.Request.CreateResponse(HttpStatusCode.InternalServerError));
            }
        }
In the above code I have modified the overrided method OnExeption() and created new Http response exception based on the different exception types.

Modify Product Controller

Now modify the Product controller to throw our custom exception form, please look into the Get method I have modified to throw the APIDataException in case if data is not found and APIException in any other kind of error.
// GET api/product/5
[GET("productid/{id?}")]
[GET("particularproduct/{id?}")]
[GET("myproduct/{id:range(1, 3)}")]
public HttpResponseMessage Get(int id)
{
if (id != null)
      {
       var product = _productServices.GetProductById(id);
            if (product != null)
             return Request.CreateResponse(HttpStatusCode.OK, product);

throw new ApiDataException(1001, "No product found for this id.", HttpStatusCode.NotFound);
      }
      throw new ApiException() { ErrorCode = (int)HttpStatusCode.BadRequest, ErrorDescription = "Bad Request..." };
}

Run the application

Run the application and click on Product/all API:
Add the parameter id value to 1 and header Token with its current value, click on send button to get the result:
Now we can see that the Status is 200/OK, and we also get a product with the provided id in the response body. Lets see the API log now –
The log has captured the call of Product API, now provide a new product id as parameter, which is not there in database, I am using 12345 as product id and result is:
We can see, now there is a custom error status code “1001” and messages “No product found for this id.” And the generic status code “500/Internal Server Error” is now replaced with our supplied code “404/ Not Found”, which is more meaningful for the client or consumer.
Lets see the APILog now:
Well, now the log has captured both the event and error of same call on the server, you can see call log details and the error with provided error message in the log with our custom error code, I have only captured error description and error code, but you can add more details in the log as per your application needs.

Update the controller for new Exception Handling

Following is the code for controllers with implementation of custom exception handling and logging:

Product Controller

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using AttributeRouting;
using AttributeRouting.Web.Http;
using BusinessEntities;
using BusinessServices;
using WebApi.ActionFilters;
using WebApi.Filters;
using System;
using WebApi.ErrorHelper;

namespace WebApi.Controllers
{
    [AuthorizationRequired]
    [RoutePrefix("v1/Products/Product")]
    public class ProductController : ApiController
    {
        #region Private variable.

        private readonly IProductServices _productServices;

        #endregion

        #region Public Constructor

        /// <summary>
        /// Public constructor to initialize product service instance
        /// </summary>
        public ProductController(IProductServices productServices)
        {
            _productServices = productServices;
        }

        #endregion

        // GET api/product
        [GET("allproducts")]
        [GET("all")]
        public HttpResponseMessage Get()
        {
            var products = _productServices.GetAllProducts();
            var productEntities = products as List ?? products.ToList();
            if (productEntities.Any())
                return Request.CreateResponse(HttpStatusCode.OK, productEntities);
            throw new ApiDataException(1000, "Products not found", HttpStatusCode.NotFound);
        }

        // GET api/product/5
        [GET("productid/{id?}")]
        [GET("particularproduct/{id?}")]
        [GET("myproduct/{id:range(1, 3)}")]
        public HttpResponseMessage Get(int id)
        {
            if (id != null)
            {
                var product = _productServices.GetProductById(id);
                if (product != null)
                    return Request.CreateResponse(HttpStatusCode.OK, product);

                throw new ApiDataException(1001, "No product found for this id.", HttpStatusCode.NotFound);
            }
            throw new ApiException() { ErrorCode = (int)HttpStatusCode.BadRequest, ErrorDescription = "Bad Request..." };
        }

        // POST api/product
        [POST("Create")]
        [POST("Register")]
        public int Post([FromBody] ProductEntity productEntity)
        {
            return _productServices.CreateProduct(productEntity);
        }

        // PUT api/product/5
        [PUT("Update/productid/{id}")]
        [PUT("Modify/productid/{id}")]
        public bool Put(int id, [FromBody] ProductEntity productEntity)
        {
            if (id > 0)
            {
                return _productServices.UpdateProduct(id, productEntity);
            }
            return false;
        }

        // DELETE api/product/5
        [DELETE("remove/productid/{id}")]
        [DELETE("clear/productid/{id}")]
        [PUT("delete/productid/{id}")]
        public bool Delete(int id)
        {
            if (id != null && id > 0)
            {
                var isSuccess = _productServices.DeleteProduct(id);
                if (isSuccess)
                {
                    return isSuccess;
                }
                throw new ApiDataException(1002, "Product is already deleted or not exist in system.", HttpStatusCode.NoContent );
            }
            throw new ApiException() {ErrorCode = (int) HttpStatusCode.BadRequest, ErrorDescription = "Bad Request..."};
        }
    }
}
Now you can see, our application is so rich and scalable that none of the exception or transaction can escapelogging.Once setup is inplaced, now you don’t have to worry about writing code each time for logging or requests and exceptions, but you can relax and focus on business logic only.

Conclusion

In this article we learnt about how to perform request logging and exception logging in WebPI. There could be numerous ways in which you can perform these operations but I tried to present this in as simple way as possible. My approach was to take our enterprise level to next level of development, where developers should not always be worried about exception handling and logging. Our solution provides a generic approach of centralizing the operations in one place; all the requests and exceptions are automatically taken care of. In my new articles, I’ll try to enhance the application by explaining unit testing in WebAPI and OData in WebAPI. You can download the complete source code of this article with packages from GitHub. Happy coding Smile | :)

Learning MVC Part 6: Generic Repository Pattern in MVC3 Application with Entity Framework


Introduction

Creating a Generic Repository pattern in an MVC3 application with Entity Framework is the last topic that we are about to cover in our journey of learning MVC.
The article will focus on Unit of Work Pattern and Repository Pattern, and shows how to perform CRUD operations in an MVC application when there could be a possibility of creating more than one repository class. To overcome this possibility and overhead, we make a Generic Repository class for all other repositories and implement a Unit of Work pattern to provide abstraction.

Our roadmap towards Learning MVC

Just to remind our full roadmap towards learning MVC,

Pre-requisites

There are few pre-requisites before we start with the article,
  1. We have running sample application that we created in fifth part of the article series.
  2. We have Entity Framework 4.1 package or DLL on our local file system.
  3. We understand how MVC application is created (follow second part of the series).

Why Generic Repository

We have already discussed what Repository Pattern is and why do we need Repository Pattern in our last article. We created a User Repository for performing CRUD operations, but think of the scenario where we need 10 such repositories.
Are we going to create these classes? Not good, it results in a lot of redundant code. So to overcome this situation we’ll create a Generic Repository class that will be called by a property to create a new repository thus we do not result in lot of classes and also escape redundant code too. Moreover we save a lot of time that could be wasted creating those classes.

Unit of Work Pattern

According to Martin Fowler Unit of Work Pattern “Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.”
From MSDN, The Unit of Work pattern isn’t necessarily something that you will explicitly build yourself, but the pattern shows up in almost every persistence tool. The ITransaction interface in NHibernate, the DataContextclass in LINQ to SQL, and the ObjectContext class in the Entity Framework are all examples of a Unit of Work. For that matter, the venerable DataSet can be used as a Unit of Work.
Other times, you may want to write your own application-specific Unit of Work interface or class that wraps the inner Unit of Work from your persistence tool. You may do this for a number of reasons. You might want to add application-specific logging, tracing, or error handling to transaction management. Perhaps you want to encapsulate the specifics of your persistence tooling from the rest of the application. You might want this extra encapsulation to make it easier to swap out persistence technologies later. Or you might want to promote testability in your system. Many of the built-in Unit of Work implementations from common persistence tools are difficult to deal with in automated unit testing scenarios.”
The Unit of Work class can have methods to mark entities as modified, newly created, or deleted. The Unit of Work will also have methods to commit or roll back all of the changes as well.
The important responsibilities of Unit of Work are,
  • To manage transactions.
  • To order the database inserts, deletes, and updates.
  • To prevent duplicate updates. Inside a single usage of a Unit of Work object, different parts of the code may mark the same Invoice object as changed, but the Unit of Work class will only issue a single UPDATE command to the database.
The value of using a Unit of Work pattern is to free the rest of our code from these concerns so that you can otherwise concentrate on business logic.

Why use Unit of Work?

Again Martin Fowler statements, “When you’re pulling data in and out of a database, it’s important to keep track of what you’ve changed; otherwise, that data won’t be written back into the database. Similarly you have to insert new objects you create and remove any objects you delete.
You can change the database with each change to your object model, but this can lead to lots of very small database calls, which ends up being very slow. Furthermore it requires you to have a transaction open for the whole interaction, which is impractical if you have a business transaction that spans multiple requests. The situation is even worse if you need to keep track of the objects you’ve read so you can avoid inconsistent reads.
A Unit of Work keeps track and takes responsibility of everything you do during a business transaction that can affect the database. When you’re done, it figures out everything that needs to be done to alter the database as a result of your work.”
You see I don’t have to concentrate much on theory, we already have great definitions existing, all we needed is to stack them in a correct format.

Using the Unit of Work

One of the best ways to use the Unit of Work pattern is to allow disparate classes and services to take part in a single logical transaction. The key point here is that you want the disparate classes and services to remain ignorant of each other while being able to enlist in a single transaction. Traditionally, you’ve been able to do this by using transaction coordinators like MTS/COM+ or the newer System.Transactions namespace. Personally, I prefer using the Unit of Work pattern to allow unrelated classes and services to take part in a logical transaction because I think it makes the code more explicit, easier to understand, and simpler to unit test(From MSDN).

Creating a Generic Repository

Cut the Redundancy…
Step 1: Open up our existing MVC3 application created in Part5 in Visual Studio.
Step2: Right click Learning MVC project folder and create a folder named GenericRepository and add a class namedGenericRepository.cs to that folder.
The code of the GenericRepository.cs class is as follows:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace LearningMVC.GenericRepository
{
public class GenericRepository where TEntity : class
{
internal MVCEntities context;
internal DbSet dbSet;

public GenericRepository(MVCEntities context)
{
this.context = context;
this.dbSet = context.Set();
}

public virtual IEnumerable Get()
{
IQueryable query = dbSet;
return query.ToList();
}

public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}

public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}

public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}

public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}

public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
}

We can see, we have created the generic methods and the class as well is generic, when instantiating this class we can pass any model on which the class will work as a repository and serve the purpose.
TEntity is any model/domain/entity class. MVCEntities is our DBContext as discussed in earlier parts.
Step 3: Implementing UnitOfWork: Create a folder named UnitOfWork under LearningMVC project, and add a class UnitOfWork.cs to that folder.
The code of the class is as follows:
using System;
using LearningMVC.GenericRepository;

namespace LearningMVC.UnitOfWork
{
public class UnitOfWork : IDisposable
{
private MVCEntities context = new MVCEntities();
private GenericRepository userRepository;

public GenericRepository UserRepository
{
get
{
if (this.userRepository == null)
this.userRepository = new GenericRepository(context);
return userRepository;
}
}

public void Save()
{
context.SaveChanges();
}

private bool disposed = false;

protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

We see the class implements IDisposable interface for objects of this class to be disposed.
We create object of DBContext in this class, note that earlier it was used to be passed in Repository class from a controller.
Now it’s time to create our User Repository. We see in the code itself that, simply a variable named userRepositoryis declared as private GenericRepository userRepository; of type GenericRepository serving User entity to TEntity template.
Then a property is created for the same userRepository variable in a very simplified manner,
public GenericRepository UserRepository
{
    get
    {
        if (this.userRepository == null)
            this.userRepository = new GenericRepository(context);
        return userRepository;
    }
}
I.e., mere 6-7 lines of code. Guess what? Our UserRepository is created.
(Taken from Google)
You see it was as simple as that, you can create as many repositories you want by just creating simple properties, and no need to create separate classes. And now you can complete the rest of the story by yourself, confused???? Yes it’sDBOperations, let’s do it.
Step 4: In MyController, declare a variable unitOfWork as:
private UnitOfWork.UnitOfWork unitOfWork = new UnitOfWork.UnitOfWork();
Now this unitOfWork instance of UnitOfWork class holds all th repository properties,if we press “.” After it, it will show the repositories.So we can choose any of the repositories created and perform CRUD operations on them.
E.g. our Index action:
public ActionResult Index()
{
    var userList = from user in unitOfWork.UserRepository.Get() select user;
    var users = new List();
    if (userList.Any())
    {
        foreach (var user in userList)
        {
            users.Add(new LearningMVC.Models.UserList() { UserId = user.UserId, 
              Address = user.Address, Company = user.Company, 
              FirstName = user.FirstName, LastName = user.LastName, 
              Designation = user.Designation, EMail = user.EMail, PhoneNo = user.PhoneNo });
        }
    }
    ViewBag.FirstName = "My First Name";
    ViewData["FirstName"] = "My First Name";
    if(TempData.Any())
    {
        var tempData = TempData["TempData Name"];
    }
    return View(users);
}
Here,
  • unitOfWork.UserRepository ­­> Accessing UserRepository.
  • unitOfWork.UserRepository.Get() -> Accessing Generic Get() method to get all users.
Earlier we used to have MyController constructor like:
public MyController()
{
    this.userRepository = new UserRepository(new MVCEntities());
}
Now, no need to write that constructor, in fact you can remove the UserRepository class and Interface we created in part 5 of Learning MVC.
I hope you can write the Actions for rest of the CRUD operations as well.

Details

public ActionResult Details(int id)
{
    var userDetails = unitOfWork.UserRepository.GetByID(id);
    var user = new LearningMVC.Models.UserList();
    if (userDetails != null)
    {
        user.UserId = userDetails.UserId;
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
            user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
    }
    return View(user);
}
 
Create:
[HttpPost]
public ActionResult Create(LearningMVC.Models.UserList userDetails)
{
    try
    {
        var user = new User();
        if (userDetails != null)
        {
            user.UserId = userDetails.UserId;
            user.FirstName = userDetails.FirstName;
            user.LastName = userDetails.LastName;
            user.Address = userDetails.Address;
            user.PhoneNo = userDetails.PhoneNo;
            user.EMail = userDetails.EMail;
            user.Company = userDetails.Company;
            user.Designation = userDetails.Designation;
        }
        unitOfWork.UserRepository.Insert(user);
        unitOfWork.Save();
        return RedirectToAction("Index");
          }
    catch
    {
        return View();
    }
}
 
Edit:
public ActionResult Edit(int id)
{
    var userDetails = unitOfWork.UserRepository.GetByID(id);
    var user = new LearningMVC.Models.UserList();
    if (userDetails != null)
    {
        user.UserId = userDetails.UserId;
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
        user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
      }
    return View(user);
}

[HttpPost]
public ActionResult Edit(int id, User userDetails)
{
TempData[TempData Name”] = Akhil”;

try
{
var user = unitOfWork.UserRepository.GetByID(id);
user.FirstName = userDetails.FirstName;
user.LastName = userDetails.LastName;
user.Address = userDetails.Address;
user.PhoneNo = userDetails.PhoneNo;
user.EMail = userDetails.EMail;
user.Company = userDetails.Company;
user.Designation = userDetails.Designation;
unitOfWork.UserRepository.Update(user);
unitOfWork.Save();
return RedirectToAction(Index”);
}

 
Delete:
public ActionResult Delete(int id)
{
    var user = new LearningMVC.Models.UserList();
    var userDetails = unitOfWork.UserRepository.GetByID(id);

if (userDetails != null)
{
user.FirstName = userDetails.FirstName;
user.LastName = userDetails.LastName;
user.Address = userDetails.Address;
user.PhoneNo = userDetails.PhoneNo;
user.EMail = userDetails.EMail;
user.Company = userDetails.Company;
user.Designation = userDetails.Designation;
}
return View(user);
}

[HttpPost]
public ActionResult Delete(int id, LearningMVC.Models.UserList userDetails)
{
try
{
var user = unitOfWork.UserRepository.GetByID(id);

if (user != null)
{
unitOfWork.UserRepository.Delete(id);
unitOfWork.Save();
}

return RedirectToAction(Index”);
}
catch
{
return View();
}
}

 
Note: Images are taken from Google images.

Conclusion

We now know how to make generic repositories too, and perform CRUD operations using it.

We have also learnt UnitOfWork pattern in detail. Now you are qualified and confident enough to apply these concepts in your enterprise applications. This was the last part of this MVC series, let me know if you feel to discuss any topic in particular or we can also start any other series as well.

Read more:

Other Series

My other series of articles:

For more informative articles visit my Blog.

For more technical articles you can reach out to CodeTeddy.

Learning MVC – Part 5:Repository Pattern in MVC3 Application with Entity Framework


Introduction

In our last four articles, we learnt almost everything about how to create an MVC application and how to communicate with database using the same application.
In the third part of learning MVC, we learnt communication between MVC application and database usingEntityFramework, so I am referring to the same context. In this article, I’ll focus on how to implement a Repository Pattern in the same MVC application, therefore moving ahead a step towards architectural approach of developing an enterprise application.

Our Roadmap

Just to remind you of our full roadmap towards learning MVC:

Pre-requisites

There are a few pre-requisites before we start with the article:
  1. We have running sample application that we created in the third part of the article series.
  2. We have EntityFramework 4.1 package or DLL on our local file system.
  3. We understand how the MVC application is created.

Repository Pattern

Very few authors explain the concept and jump directly over the practical implementation of the pattern. So, first let us understand what is repository pattern? Why should we use it?
In simple terms, a repository basically works as a mediator between our business logic layer and our data access layer of the application. Sometimes, it would be troublesome to expose the data access mechanism directly to business logic layer, it may result in redundant code for accessing data for similar entities or it may result in a code that is hard to test or understand. To overcome these kinds of issues, and to write an Interface driven and test driven code to access data, we use Repository Pattern. The repository makes queries to the data source for the data, thereafter maps the data from the data source to a business entity/domain object, finally and persists the changes in the business entity to the data source. According to MSDN, a repository separates the business logic from the interactions with the underlying data source or Web service. The separation between the data and business tiers has three benefits:
  • It centralizes the data logic or Web service access logic.
  • It provides a substitution point for the unit tests.
  • It provides a flexible architecture that can be adapted as the overall design of the application evolves.
When we use Entity Framework, as we did in our last application created, we were calling the Entity Framework class object in the controller class for accessing the entity classes. Now we can say that that system was somewhat a tightly coupled system. To overcome this situation, as we discussed, we’ll implement Repository Pattern.
In Repository, we write our whole business logic of CRUD operations with the help of Entity Framework classes, that will not only result in meaningful test driven code but will also reduce our controller code of accessing data.

Creating Repository

Creating Repository is not as tough at it sounds to be, once you implement this by your own, you’ll love it.
Step 1: Open up our existing MVC3 application in Visual Studio, that we created in the third part to interact with database with the help of Entity Framework.
Step 2: Create a folder named Repository and add an Interface to that folder named IUserRepository, this interface we derive from IDisposable type of interface.
We’ll declare methods for CRUD operations on User entity class over here, you can choose the names of the method as per your choice, but those should be easy to understand and follow.
Like I used in the below code of my interface:
using System;
using System.Collections.Generic;

namespace LearningMVC.Repository
{
public interface IUserRepository:IDisposable
{
IEnumerable GetUsers();
User GetUserByID(int userId);
void InsertUser(User user);
void DeleteUser(int userId);
void UpdateUser(User user);
void Save();
}
}

We can see each method name signifies particular CRUD operation on User entity.
User Entity is the same entity we generated in Model.tt class in Part3 of learning MVC, remember???????
Step 3: Extract a class from that interface and call it UserRepository. This UserRepository class will implement all the methods of that interface, but with the help of Entity Framework. Now here comes the use of our DBContextclass MVCEntities, we already have this class in our existing solution, so we don’t have to touch this class, simply, write our business logic in the interface methods implemented in UserRepository class:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace LearningMVC.Repository
{
public class UserRepository:IUserRepository
{
private MVCEntities context;

public UserRepository(MVCEntities context)
{
this.context = context;
}

public IEnumerable GetUsers()
{
return context.Users.ToList();
}

public User GetUserByID(int userId)
{
return context.Users.Find(userId);
}

public void InsertUser(User user)
{
context.Users.Add(user);
}

public void DeleteUser(int userId)
{
User user = context.Users.Find(userId);
context.Users.Remove(user);
}

public void UpdateUser(User user)
{
context.Entry(user).State = EntityState.Modified;
}

public void Save()
{
context.SaveChanges();
}

private bool disposed = false;

protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

And inside the solution:

Interface

Class

90% of the job is done now. Now the only thing left is to use this repository in our controller.
This is needless to explain how you’ll call repository inside the controller, as you now know how to treat our controller, but still let’s do it for once.
Step 4: Go to the controller, declare the IUserRepository reference, and in the constructor initialize the object withUserRepository class, passing MVCEntities to the constructor as parameter we defined in UserRepositoryclass:
#region Private member variables...
private IUserRepository userRepository; 
#endregion

#region Public Constructor…
/// <summary>
/// Public Controller to initialize User Repository
/// </summary>
public MyController()
{
this.userRepository = new UserRepository(new MVCEntities());
}
#endregion

In the solution, this will look like:

Step 5: Now for all the actions of the controller in which we were using Entity Framework context directly, we replace the calling logic by the created userRepository object, and call methods defined in repository class.
Like, in Index controller, where we show the list of users, we do:
var userList = from user in userRepository.GetUsers() select user;
            var users = new List();
            if (userList.Any())
            {
                foreach (var user in userList)
                {
                    users.Add(new LearningMVC.Models.UserList() 
                    { UserId = user.UserId, Address = user.Address, 
                    Company = user.Company, FirstName = user.FirstName, 
                    LastName = user.LastName, Designation = user.Designation, 
                    EMail = user.EMail, PhoneNo = user.PhoneNo });
                }
            }

We can see the earlier code used remained the same, only a layer has been introduced between Entity Framework data access layer and business logic, and the controller now only uses that abstracted layer to communicate with database.

Similarly for other Actions of the controller:

Details

Create

Edit

Delete

Step 6: Run the application, and we see the application running as it was earlier:

Now that’s party time.

Conclusion

We now know how to make repositories too, and perform CRUD operations using it.
Now we can visualize how useful the pattern is and how it solved our issues of tight coupling and resulted in an appropriate architecture.
As per MSDN, Use the Repository pattern to achieve one or more of the following objectives:
  • You want to maximize the amount of code that can be tested with automation and to isolate the data layer to support unit testing.
  • You access the data source from many locations and want to apply centrally managed, consistent access rules and logic.
  • You want to implement and centralize a caching strategy for the data source.
  • You want to improve the code’s maintainability and readability by separating business logic from data or service access logic.
  • You want to use business entities that are strongly typed so that you can identify problems at compile time instead of at run time.
  • You want to associate a behavior with the related data. For example, you want to calculate fields or enforce complex relationships or business rules between the data elements within an entity.
  • You want to apply a domain model to simplify complex business logic.
And I fully agree to it, but has our application made use of the pattern appropriately? What if there are 100s of Repositories that need to be created? What if we have 100s of entities? Do we create Repositories for all of them, resulting in a mess and code redundancy? The answer is a big NO. In my next and last article of the series, we’ll learn how to create a Generic Repository to serve the purpose of n number of Entities. The source code of this article and existing article, i.e., Part 3 along with database scripts has been attached. You can download and run the solution, and drop me a question in case you feel like it. I’ll be happy to answer.

Read more:

Other Series

My other series of articles:

For more informative articles visit my Blog.

For more technical articles you can reach out to CodeTeddy.

Learning MVC Part 4 : Creating MVC Application with EntityFramework Code First Approach


Introduction

In our first three articles, we learnt a lot about MVC, starting from definition to use, from creating an application to connecting the MVC application with database using different techniques.
In the very last part of the series, we learnt how to connect our MVC application with existing database using Entity Framework.
This article will focus on connecting our MVC application with database using CodeFirst approach, i.e., one of the features Microsoft’s Entity Framework provides.

Our Roadmap

Just to remind our full roadmap towards learning MVC:
 

Pre-requisites

There are few pre-requisites before we start with the article:
  1. We have the running sample application that we created in the third part of the article series.
  2. We have EntityFramework 4.1 package or DLL on our local file system.
  3. We understand how MVC application is created.

Code-First Approach

To achieve a domain driven design, Entity Framework introduced EF 4.1 Code First. In the Code First approach, we focus on the domain design or entities/POCO classes first and create classes as per our model requirement. We do not have the database of the application, rather we create database automatically from code after defining our domain. The database created perfectly matches with the domain we design, so we have to be very conscious and keen in designing our domain model. It feels exciting to see database created on the fly with the help of our entities and XML configuration, without even opening database server.
No matter, you are not an expert in database, if you are a C# developer, just focus on your model/class creation.EntityFramework will take headache of creating and managing database for you.

Procedure

Step 1: Open the MVC application that we created in Learning MVC-Part3 in your Visual Studio.
We can clearly see and remember what we used to connect our MVC application to database with the help of entity framework, yes it was edmx class and our Model.tt classes generated from edmx classes.
Step 2: We don’t need the existing data-base, so you can delete the already created database for our part 3 application (if created).
Step 3: We don’t need edmx files now, so let’s clean our application, wipe out all these classes. Just deleteEFDataModel.edmxModel1.Context.tt and Model1.tt files. Now please do not run the application. It will give compile time errors, since we were using those classes ;-), Our solution will look like:
Our old solution had UserList class in Models folder, I have only changed the name of the class for differentiating it with previous application, and readability as was in the first part.
Step 4: As simple as that, just add a class to your solution, and name it MVCDBContext.cs as shown in the following image:
Step 5: Just add System.Data.Entity DLL as a reference to the solution if not already added.
Step 6: Use the namespace System.Data.Entity in our DBContext class, and inherit the added class fromDBContext class,
DbContext class: According to MSDN, DbContext class is conceptually similar to ObjectContext. To define, theObjectContext class is the part of the core EF API in the Microsoft .NET Framework 4 and this is our hero class that allows us to perform queries, change tracking and update the database using the strongly typed classes that represent our model (entity class). The DbContext is a wrapper around ObjectContext that exposes the most commonly used features of ObjectContext as well as provides some simpler “shortcuts” to tasks that are frequently used but complicated to code directly with ObjectContext. Simplfied alternative to ObjectContext and is the primary object for interacting with a database using a specific model.
Step 7: Add a DBSet property to the DbContext class that we created:
public DbSet Users { get; set; }
User, defined in angular brackets, is the model that we created in Models folder, so our MVCDBContext class looks like:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using LearningMVC.Models;

namespace LearningMVC
{
public class MVCDBContext : DbContext
{
public DbSet Users { get; set; }
}
}

That’s it, our 90% work is done?
DbSet property: It is a simplified alternative to ObjectSet and is used to perform CRUD operations against a specific type from the model.
By default, the name of the DbContext class will be the name our database that will automatically be created, so be wise to select the name of context class, else it could be handled in web.config as well.
The name of model will be the name of Table in database and properties of model will be the columns of the table.

Our Heroes

Both DbContext and DbSet are our super heroes, in creating and dealing with database operations, and make us far abstracted, providing ease of use to us.
When we are working with DbContext, we are in real working with entity sets. DbSet represents a typed entity set that is used to perform create, read, update, and delete operations. We are not creating DbSet objects and using them indepedently. DbSet can be only used with DbContext.
Step 8: Define a connection string in web.config file, you can remove previously defined connection string, the new connection string will somewhat look like:
The name of the connection string will be the name of the DbContect that we defined, i.e., MVCDbContext.
Step 9: Now, we just have to modify the access method in controllers, earlier, when we created application in third part, we were accessing the context class from the modelcontext class that was generated from edmx file. Edmx file was added having reference to already created database.
But now the case is different, we don’t have a database now, we’ll access the table and columns using ourMVCDBContext class in controllers, so just change the following line of code used in Actions of earlier application:
var dbContext = new MVCEntities() ;
to
var dbContext = new MVCDBContext();
Job done. 
Just Hit F5, and you’ll see:
How does the application run, where is the database??? Dude, go back to your database server, and check for database:
We see our database is created, with the name MVCDB, that’s the magic of EntityFramework. Now we can perform all the CRUD operations on this database, using our application. Just create a new user.
In database we see, user created.
By default, integer property with ID in its name of model will be the primary key in the database, in our caseUserId, or you can define the primary key in the model too.

Conclusion

Now we know how to play with EntityFramework to create database as per our domain model from our code, we have already moved ahead to advanced concepts of MVC and Entity Framework.
When we see the definition of DbContext, it uses the terms Repository Pattern and Unit of Work Pattern. We’ll discuss these more in detail in my next article.

Read more:

Other Series

My other series of articles:

For more informative articles visit my Blog.

For more technical articles you can reach out to CodeTeddy.