Observer Design Pattern in ASP.NET Core

You are currently viewing Observer Design Pattern in ASP.NET Core

Imagine you are building a modern event driven application for a news agency that notifies multiple news channels, online websites, and media houses as soon as they have the latest news about any major incident that happened anywhere in the world. You must need to architect your application in a way that not only you can maintain the list of all interested parties but can also notify them all. This is exactly the use case in which you can use one of the most common and well-known design patterns called Observer. In this tutorial, I will give you an overview of the Observer pattern with an example implementation in ASP.NET Core and C#.

What is Observer Design Pattern?

The Observer is a behavioral design pattern and it allows some objects (known as subjects or publishers or observables) to notify other objects (known as observers or subscribers) about the changes in their state.

According to Wikipedia

The observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependants, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

Wikipedia
Observer Design Pattern in ASP.NET Core

Following are some key points to remember about the Observer pattern:

  1. The subject maintains a list of all observers
  2. The subject can subscribe/unsubscribe observers at runtime.
  3. The subject notifies all observers when its state change or some event occurs
  4. The subject and observers are loosely coupled because they have no explicit knowledge of each other.
  5. New observers can be implemented and used without modifying the subject implementation.

When to use Observer Design Pattern?

The Observer pattern is pretty common in programming languages such as C#, Java, etc. because it provides a neat and well-tested design for applications in which multiple objects are dependent on the state of one object.

Some real-world use cases can be following.

  1. Social media applications that send notifications to multiple followers/subscribers when a new post is published by any user, group, page, etc.
  2. Apps marketplace that notifies all users of an app if there is a new update available
  3. Weather forecasting app that notifies users about weather changes in their area
  4. Sports-related apps which notify users and other systems about the latest match or score updates
  5. Ecommerce application that notifies admins or users if any product goes out of stock or notifies users if the product is shipped.
  6. GUI toolkits and frameworks that allow multiple UI components to respond to events that occurred in other components.

How to Implement Observer Design Pattern?

To show you a real-world implementation of the Observer pattern, I picked a simple use case in which we will perform the following actions.

  1. We will implement multiple observers
  2. We will implement a subject that will act as a publisher
  3. We will attach/detach multiple observers with the subject
  4. We will display a fictitious order on the page
  5. We will allow the user to update/change the order status
  6. We will notify all attached observers as soon as the order status is updated
  7. We will also send the order details from the subject to observers
READ ALSO:  How to Use JQuery DataTables with ASP.NET Core Web API

The following diagram will give you an overview of what we are going to implement.

Observer Design Pattern Implementation Diagram

Setting Up ASP.NET Core Project

Let’s create a new ASP.NET Core MVC web application and create the following Order class in the Models folder.

Order.cs

public class Order
{
    public string OrderNumber { get; set; }
    public DateTime OrderDate { get; set; }
    public decimal TotalAmount { get; set; }

    public OrderStatuses OrderStatus { get; set; }
}

The class has some basic order related properties along with the OrderStatus property which is an enumeration. Create the following OrderStatuses enum in the Models folder.

OrderStatuses.cs

public enum OrderStatuses
{
    PendingPayment = 1,
    OnHold = 2,
    Processing = 3,
    Shipped = 4,
    Cancelled = 5,
    Refunded = 6,
    Failed = 7,
    Completed = 8
}

Create a fictitious order object within the Index action method of the HomeController and return the order object to the razor view page.

HomeController.cs

public IActionResult Index()
{
    var order = new Order()
    {
        OrderNumber = "11283A454B",
        OrderDate = DateTime.Now,
        TotalAmount = 105.99m,
        OrderStatus = OrderStatuses.PendingPayment
    };

    return View(order);
}

Implement the following Index.cshtml Razor view page that will display a simple form to will allow the user to update the order status. The list of statuses is also converted from the enum to a SelectList that we bind with the HTML select element.

Index.cshtml

@model ObserverDesignPatternDemo.Models.Order

@{
    ViewData["Title"] = "Update Order";

    IList<SelectListItem> statuses = Enum.GetValues(typeof(OrderStatuses)).Cast<OrderStatuses>()
        .Select(x => new SelectListItem(){ Text = x.ToString(), Value = ((int)x).ToString()
    }).ToList();

    int status = (int) Model.OrderStatus;

    var list = new SelectList(statuses, "Value", "Text", status);
}

<h1>Update Order</h1>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Index" method="post">
            <div class="form-group">
                <label asp-for="OrderNumber" class="control-label"></label>
                <input asp-for="OrderNumber" class="form-control" readonly="readonly" />
            </div>
            <div class="form-group">
                <label asp-for="OrderDate" class="control-label"></label>
                <input asp-for="OrderDate" class="form-control" readonly="readonly" />
            </div>
            <div class="form-group">
                <label asp-for="TotalAmount" class="control-label"></label>
                <input asp-for="TotalAmount" class="form-control" readonly="readonly" />
            </div>
            <div class="form-group">
                <label asp-for="OrderStatus" class="control-label"></label>
                <select asp-for="OrderStatus" asp-items="statuses" class="form-control"></select>
            </div>
            <div class="form-group">
                <input type="submit" value="Update" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

The form will submit the order information to the following Index method and we will implement this method at the end of this tutorial.

HomeController.cs

[HttpPost]
public IActionResult Index(Order order)
{
    return View(order);
}

Run the project and you should see the following Update Order form in the browser.

Order form example to see Observer pattern in action

Implementing Observers

We are now ready to declare the observer (subscriber) interface. At a bare minimum, it should have a single update method that can be called by the subject (publisher) every time it needs to notify the observer.

Create a Services folder in the project and add the following IOrderObserver interface in the folder. I also passed the Order object as an argument so that the Update method can also receive the order information when it receives the notification.

READ ALSO:  Data Access in ASP.NET Core using EF Core (Database First)

IOrderObserver.cs

public interface IOrderObserver
{
    void Update(Order order);
}

Let’s say you have a business requirement to send an email to the customer every time the order is updated. You can easily add this feature in your application by simply creating an observer class that will implement the above IOrderObserver and will write the code to send emails. Here is an example of such a class.

EmailObserver.cs

public class EmailObserver : IOrderObserver
{
    public void Update(Order order)
    {
        Console.WriteLine("Order No '{0}' status is updated to '{1}'. An email sent to customer.", 
            order.OrderNumber, order.OrderStatus.ToString());
    }
}

I haven’t written the email sending code for brevity but in a real application, you can write the email sending code or call some email service from the Update method.

Now let’s assume, you have a new requirement that you also want to send an SMS every time the order is updated. All you need is another observer class similar to the above class but this time it will have the code of sending SMS to customers. Here is an example of such a class.

SmsObserver.cs

public class SmsObserver : IOrderObserver
{
    public void Update(Order order)
    {
        Console.WriteLine("Order No '{0}' status is updated to '{1}'. SMS message sent to customer.",
            order.OrderNumber, order.OrderStatus.ToString());
    }
}

I am sure you have the idea now that you can create as many observers as you want according to your requirements and each one of these observers will perform their own tasks whenever they will receive the notifications from the subject that the order has been updated.

Implementing the Subject

We are now ready to declare the publisher interface and it should at least have a pair of methods for adding and removing subscribers to and from the list. It is important to note that the publishers must work with subscribers only via the subscriber interface.

Create the following IOrderNotifier interface that will act as the publisher interface in our example.

IOrderNotifier.cs

public interface IOrderNotifier
{
    // Attach an order observer to the subject.
    void Attach(IOrderObserver observer);

    // Detach an order observer from the subject.
    void Detach(IOrderObserver observer);

    // Notify all order observers about an event.
    void Notify(Order order);
}

Now we need to decide who will implement the above IOrderNotifier interface and will maintain the list of subscribers. It is usually not a difficult decision to make because in a real-world application, we may already have an application service that is responsible for updating orders and that service is the most suitable candidate for acting as a publisher. Let’s assume in our application, we have the IOrderService interface with the UpdateOrder method, we can derive it from the publisher interface IOrderNotifier as follows:

READ ALSO:  Full Stack CRUD using Angular 8 and ASP.NET Core 5 Web API

IOrderService.cs

public interface IOrderService : IOrderNotifier
{
   void UpdateOrder(Order order);
}

This means that if we have a class such as OrderService that was implementing the IOrderService before, that class needs to implement the methods declared in the IOrderNotifier interface. The OrderService class also needs to maintain the list of subscribers and will define the functionality of adding/removing subscribers. Add the following OrderService class in the project Services folder.

OrderService.cs

public class OrderService : IOrderService 
{ 
    public List<IOrderObserver> Observers = new List<IOrderObserver>();

    public void UpdateOrder(Order order)
    {
        Notify(order);
    }

    public void Attach(IOrderObserver observer)
    {
        Observers.Add(observer);
    }

    public void Detach(IOrderObserver observer)
    {
        Observers.Remove(observer);
    }

    public void Notify(Order order)
    {
        foreach (var observer in Observers)
        {
            observer.Update(order);
        }
    }
}

In the above implementation, I first declared a List<IOrderObserver> that will maintain the list of subscribers (observers)

public List<IOrderObserver> Observers = new List<IOrderObserver>();

The Attach and Detach methods are self-explanatory as they are simply adding or removing subscribers to and from the list.

public void Attach(IOrderObserver observer)
{
    Observers.Add(observer);
}

public void Detach(IOrderObserver observer)
{
    Observers.Remove(observer);
}

Most subscribers would need some context data about the event. In our example, they need the Order object and that’s why it is passed as an argument of the Notify method. The Notify method iterates over the list of all subscribers and calls their Update method. It also passes the updated order object to all subscribers.

public void Notify(Order order)
{
    foreach (var observer in Observers)
    {
        observer.Update(order);
    }
}

Testing Observer Pattern Implementation

We are now ready to test our implementation. Register the above OrderService in the Startup.cs file as follows.

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddScoped<IOrderService, OrderService>(); 
}

Inject the IOrderService object in the HomeController using ASP.NET Core dependency injection.

HomeController.cs

public class HomeController : Controller
{
    private readonly IOrderService _orderService;

    public HomeController(IOrderService orderService)
    {
        _orderService = orderService;
    }
}

HomeController.cs

[HttpPost]
public IActionResult Index(Order order)
{
    Console.WriteLine("Attaching Observers...");

    var smsObserver = new SmsObserver();
    var emailObserver = new EmailObserver();

    _orderService.Attach(smsObserver);
    _orderService.Attach(emailObserver);

    Console.WriteLine("Updating Order Status...");

    _orderService.UpdateOrder(order);

    return View(order);
}

Run the application in the browser and choose any order status e.g. Shipped and click the Update button. You will notice the following statements in the Output window. You can easily see that both of our subscribers are notified and they printed their statements.

Observer Pattern - Publisher is sending notification

Now let’s say, you don’t want to send an SMS, you just need to unsubscribe the SMS observer object by calling the Detach method as shown below.

Console.WriteLine("Detaching SMS Observer...");
_orderService.Detach(smsObserver);

Console.WriteLine("Updating Order Status...");
_orderService.UpdateOrder(order);

Run the application again and click the Update button by selecting any order status and you will notice that the notification is sent to only email observer.

Observer Pattern - Publisher is sending notification after one observer unsubscribe

Summary

The Observer pattern is useful and can be used in situations where one object has to observe another object and need to receive notifications when the state of another object changes. I hope you have found this post useful. If you have any comments or suggestions, please leave your comments below. Don’t forget to share this tutorial with your friends or community. You can also download the complete source code of this post using the Download Source Code button shown at the start of this post.

This Post Has 4 Comments

  1. Mike

    Thank you!!!

  2. Umair

    Great Explanation

  3. Abdalla Hassan

    Great and Thorough Explanation

Leave a Reply