Singleton Design Pattern Real-Time Example Logging in C#

Singleton Design Pattern Real-Time Example in C# – Exception Logging

In this article, I am going to discuss the Singleton Design Pattern Real-Time Example Exception Logging in C# using ASP.NET MVC Application. Please read our previous article where we discussed the Singleton vs Static Class in C# with Examples. As part of this article, we are going to discuss How to Create a Custom Logger Library using the Singleton Design Pattern which logs the Exceptions to an External File using the ASP.NET MVC application.

Singleton Design Pattern Real-Time Example Exception Logging using ASP.NET MVC Application:

Let us see the step-by-step process of how to implement the Singleton Design Pattern Real-Time Example Exception Logging using C# and ASP.NET MVC Application. 

Step 1: Creating the Database

In this demo, we are going to use the following Employee table.

Singleton Design Pattern Real-Time Examples in C#

So, please use the below SQL script to create the database EmployeeDB and the table Employee with the required test data.

-- Create a database Called EmployeeDB
CREATE DATABASE EmployeeDB
GO

USE EmployeeDB
GO

-- Create a table called Employee
CREATE TABLE Employee (
    [Id]             INT      IDENTITY (1, 1) NOT NULL,
    [Name]           VARCHAR (50) NOT NULL,
    [Gender]		 VARCHAR (50) NOT NULL,
    [Salary]         BIGINT NOT NULL,
    [Department]     VARCHAR (50) NOT NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);
GO

-- Insert some test data to the Employee table
INSERT INTO Employee VALUES('Pranaya', 'Male', 12345, 'IT') 
INSERT INTO Employee VALUES('Anurag', 'Male', 100000, 'Sales') 
INSERT INTO Employee VALUES('Priyanka', 'Female', 20000, 'IT') 
INSERT INTO Employee VALUES('Preety', 'Female', 30000, 'HR') 
INSERT INTO Employee VALUES('James', 'Male', 250000, 'HR') 
INSERT INTO Employee VALUES('Pam', 'Feale', 30000, 'IT') 
INSERT INTO Employee VALUES('Smith', 'Male', 220000, 'Sales') 
GO

-- Fetch the data from the Employee table
SELECT * FROM Employee
GO
Step 2: Create a new ASP.NET MVC Application using Visual Studio

To Create a new ASP.NET MVC Application, open Visual Studio and then select File => New => Project from the context menu as shown in the below image.

Creating new ASP.NET MVC Application in Visual Studio

Once you click on the File => New => Project, the following window will open for selecting the New Project. Now from the New Project window, from the left-hand side select the Web option which is inside the Visual C# which is under the Installed section. From the middle pane, select the ASP.NET Web Application (.NET Framework) and name the project SingletoninMVC and select the location where you want to create or save the project, select the .NET Framework version, and then click on the OK button as shown in the below image.

ASP.NET Web Application (.NET Framework)

Once you click on the OK button, the following New ASP.NET Web Application window will open for selecting the project Template. From this window, select the MVC project template and then we are going to choose the Authentication type and for doing that just click on the Change Authentication button, a new dialog will pop up with the name Change Authentication, here we are going to choose No Authentication and then click on the OK button as shown in the below image.

Selecting MVC Project Template

Once you click on the OK button, It will take some time to create the project for us. So, we have created our ASP.NET MVC Application using Visual Studio. 

Step 3: Adding ADO.NET Entity Data Model

In the next step, we are going to use Entity Framework Database First Approach to Communicate with the EmployeeDB and Employee that we created in Step 1 from our ASP.NET MVC Application.

To do so, add ADO.NET Entity Data Model inside the Models Folder. So, right-click on the Models folder then select Add => New Item from the context menu which will open the following Entity Data Model Wizard. From this wizard, from the left pane, select Data which is inside Visual C# which is under the Installed section. From the middle pane, select ADO.NET Entity Data Model, provide a meaningful name such as EmployeeDataModel, and then click on the ADD button as shown in the below image.

Adding ADO.NET Entity Data Model

Once you click on the Add button, it will open a new window for selecting the Model type. From this window, select Generate From Database option as we are going to work with Entity Framework Database First Approach, and then click on the Next button as shown in the below image.

Singleton Design Pattern Real Time Example in C#

Once you click on the Next button, it will open the Choose Your Data Connection Wizard. From this wizard, click on the New Connection button which will open the Connection Properties window. From this connection properties window, provide the necessary details (Database credentials, Sever Name, Data Provider), select the database (EmployeeDB), and then click on the OK button as shown in the below image.

Singleton Design Pattern Real Time Example in C#

Once you click on the OK button, it will take you back to Choose Your Data Connection Wizard. Here, in this window, you need to provide a meaningful name such as EmployeeDBContext for the Context class and that name will also be the Connection String name that is going to be created in the Web.config file. Finally, click on the Next button as shown below

Singleton Design Pattern Real Time Example in C#

Once you click on the Next button, it will open the Choose Your Version wizard for selecting the Entity Framework version. From this window, select Entity Framework 6.x and click on the Next button as shown in the below image. Sometimes, this window will not open if already the latest version of Entity Framework is installed.

Singleton Design Pattern Real Time Example in C#

Once you select the Entity Framework Version and click on the Next button, it will open the Choose Your Database Objects and settings wizard. From this wizard, select the Employee object, provide a meaningful namespace name and finally click on the Finish button as shown in the image below.

Singleton Design Pattern Real Time Example in C#

Thats it. We have added the Entity Data Model from our ASP.NET MVC Application.

Step 4: Creating the Singleton Class for Logging Functionality

Now, we need to create the Singleton Class for Providing the Exception Logging Functionality. So, add a folder with the name Logger at the root directory of our MVC Application.

ILog.cs

Once you add the Logger Folder, then add a class file with the name ILog.cs and copy and paste the following code into it. This is an interface and this interface provides one method i.e. LogException. The child class is going to provide the implementation for this LogException method. In our example, the Singleton class is going to implement this interface and provide implementations for the LogException method.

namespace SingletoninMVC.Logger
{
    public interface ILog
    {
        void LogException(string message);
    }
}
Log.cs

Now, we need to create the Singleton Class by implementing the ILog interface. So, create a class file with the name Log.cs within the Logger folder and then copy and paste the following code into it. This Log class is nothing but the Singleton class which uses Eager loading which is thread-safe in a multithread environment. Here, we are following the Rules of the Singleton Design Pattern by making the class Sealed (to restrict inheritance), creating the private parameterless constructor (to restrict class instantiation from outside the class), creating a public method to access the only instance of the Log class i.e. (GetInstance method). Once the client gets the Log instance, then the client using the Log instance can call the LogException method by passing the exception message as an argument. The following class code is self-explained, so please go through the comment lines for a better understanding.

using System;
using System.IO;
using System.Text;
namespace SingletoninMVC.Logger
{
    public sealed class Log : ILog
    {
        //Private Constructor to Restrict Class Instantiation from outside the Log class
        private Log()
        {
        }

        //Creating Log Instance using Eager Loading
        private static readonly Log LogInstance = new Log();

        //Returning the Singleton LogInstance
        //This Method is Thread Safe as it uses Eager Loading
        public static Log GetInstance()
        {
            return LogInstance;
        }

        //This Method Log the Exception Details in a Log File
        public void LogException(string message)
        {
            //Create the Dynamic File Name
            string fileName = $"Exception_{DateTime.Now.ToShortDateString()}.log";

            //Create the Path where you want to Create the Log file
            string logFilePath = $"{AppDomain.CurrentDomain.BaseDirectory}\\{fileName}";

            //Build the String Object using StringBuilder for a Better Performance
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("----------------------------------------");
            sb.AppendLine(DateTime.Now.ToString());
            sb.AppendLine(message);

            //Write the StringBuilder Message into the Log File Path using StreamWriter Object
            using (StreamWriter writer = new StreamWriter(logFilePath, true))
            {
                writer.Write(sb.ToString());
                writer.Flush();
            }
        }
    }
}
Step 5: Adding Employee Controller:

Once the Log Singleton class is ready, next we need to use the LogException method in our MVC Application. So, add one controller with the name Employee. Right-click on the Controllers folder and then select Add=> Controller from the context menu which will open the following Add Scaffold window. Here, you need to choose the MVC 5 Controller with views using the Entity Framework template as shown in the below image.

Adding MVC 5 Controller using Visual Studio

Once you click on the Add button, it will open the following Add Controller window. Here, we need to provide the Controller information. From this window, provide the Model class as Employee (SingletoninMVC.Models), Data Context class as EmployeeDBContext (SingletoninMVC.Models), and Controller name as EmployeeController, select all the checkboxes under the Views section and finally, click on the Add button as shown in the below image.

Creating ASP.NET MVC 5 Controller

Once you click on the Add button, it will create an Employee controller with the required action methods and Views which facilitates the CRUD Operations on the employee model. If you are getting some errors, then just build the application and follow the same steps to create the controller.

Step 6: Using Exception Logging inside the EmployeeController

In order to use the Singleton Log class LogException method to the Log the Exception detail, please modify the Employee Controller class follows.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using SingletoninMVC.Logger;
using SingletoninMVC.Models;

namespace SingletoninMVC.Controllers
{
    public class EmployeeController : Controller
    {
        private ILog _ILog;
        private EmployeeDBContext db = new EmployeeDBContext();

        public EmployeeController()
        {
            //Get the Singleton Log Instance
            _ILog = Log.GetInstance();
        }

        //Whenever Any Exception Occurred, the following OnException Method will Execute
        protected override void OnException(ExceptionContext filterContext)
        {
            //First, Log the Exception Details
            _ILog.LogException(filterContext.Exception.ToString());
            //Then set that the Exception is Handled
            filterContext.ExceptionHandled = true;
            //Then Redirect to the Error view
            this.View("Error").ExecuteResult(this.ControllerContext);
        }

        // GET: Employee
        public ActionResult Index()
        {
            return View(db.Employees.ToList());
        }

        // GET: Employee/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Employee employee = db.Employees.Find(id);
            if (employee == null)
            {
                throw new Exception("Employee Not Found");
            }
            return View(employee);
        }

        // GET: Employee/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Employee/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "Id,Name,Gender,Salary,Department")] Employee employee)
        {
            if (ModelState.IsValid)
            {
                db.Employees.Add(employee);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(employee);
        }

        // GET: Employee/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Employee employee = db.Employees.Find(id);
            if (employee == null)
            {
                return HttpNotFound();
            }
            return View(employee);
        }

        // POST: Employee/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "Id,Name,Gender,Salary,Department")] Employee employee)
        {
            if (ModelState.IsValid)
            {
                db.Entry(employee).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(employee);
        }

        // GET: Employee/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Employee employee = db.Employees.Find(id);
            if (employee == null)
            {
                return HttpNotFound();
            }
            return View(employee);
        }

        // POST: Employee/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Employee employee = db.Employees.Find(id);
            db.Employees.Remove(employee);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

By default, the Home Controller Index action method is configured as the landing page. Let us modify the RouteConfig.cs class file which you can find within the App_Start folder as follows. Now, we are making our Employee Controller and Index action method the default route.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace SingletoninMVC
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Employee", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

That’s it. We have done with our implementation.

Testing the Application.

Now run the application, and try to do something which should give some exceptions. And then you will see that all the exceptions are going to be logged under the file created by the logger class. You can find the Exception Log file as shown in the below path.

Singleton Design Pattern in a Real-Time Example of Exception Logging in C# using an ASP.NET MVC Application

This Proves that a singleton design pattern comes in handy in situations where we need to have a single instance of the object. In the next article, I am going to discuss How to Implement Singleton Design Pattern Real-Time Example Caching using C#. Here, in this article, I try to explain the Singleton Design Pattern in a Real-Time Example of Exception Logging in C# using an ASP.NET MVC Application. 

15 thoughts on “Singleton Design Pattern Real-Time Example Logging in C#”

  1. Aymeric Challier

    Thanx for this articles. They are very interesting. It’s very well explained.

    I just have some difficulties, if I do my connection using the easiest way. Why will it be less good? More exactly, what are the advantages of this method?

    I still have difficulties to understand, why to use singleton?

  2. Hi , As you mentioned ,
    In the next article, I will discuss how to implement Caching using the Singleton Design Pattern in an MVC application.

    can you please share the link

    Thank you

  3. Hey Thanx for the article, it’s very good. However i will need more clarification on Static Vs Singleton. If i just make the class static and i don’t use singleton, is good in developing.

  4. Jayprakash Lalwani

    Hi, I want to log messages asynchronously to MSMQ using singleton class. I will really appreciate if you can guide me on this. Thank you.

  5. Hello, I enjoy your tutorials, but this page has the content of EmployeeController.cs file missing, please kindly look into it, thanks for the work you’re doing regardless.

  6. Hello by using dependency injection also we can achieve logger functionality.by using some dependency injection containers we can create single instance and inject object using constructor injection,what is the difference here. is there any specific reason to use singleton here

  7. Hi , As you mentioned ,
    In the next article, I will discuss how to implement Caching using the Singleton Design Pattern in an MVC application.

    These articles are missing

Leave a Reply

Your email address will not be published. Required fields are marked *