Background
Recently I published here an article on Observer
design pattern. My purpose was to show application of Observer
design pattern in real life scenario. Those who may be interested in this article can read it here. Real Time StockFeed Dashboard (Applying Observer Design Pattern using WPF and C#)
In this article I will be exploring one more popular design pattern Visitor
and demonstrate it's practical application. The application I have developed for this is based on Northwind
database. It is Customer Invoicing Application for a fictitious company ABC LTD
. and it is used to track the list of customers and it's orders. It also helps you to generate the invoice against any order and you can save the Invoice in XML format for future reference.This application is being developed using asp.net MVC. I have used Visual Studio 2013 to develop this application.
Visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is an ability to add new operations to existing object structures without modifying those structures. It is one way to follow the open/closed principle. For more details on this design pattern please refer this link.
Introduction
Purpose of this article is to demonstrate the application of Visitor Design pattern in real life scenario. In this article I have taken an example of Customer Invoicing System where you can track list of customers and their orders.Since this is an invoicing system you can generate the Invoice against any order and send it to the customer. The application also enables you to save the invoice in XML format. I have used two types of visitors in this application as shown below
- Visitor to Generate Invoice
- Visitor to save Invoice in XML format
This article will provide detailed implementation of this pattern using ASP.NET MVC and C#. I will also explore other features such as
- Implement Generic
PagedList<T>
class to implement paging - Features of Bootstrap.CSS
- Entity Framework
- Lambda Expressions
Design Overview
I have prepared two class diagrams to explain the key classes and their roles in this application. One diagram would have looked cluttered so I have logically grouped them based on their responsibilities. Figure 1a shows the first class diagram which contains key classes which are related to Generation of Invoice and saving of Invoice in XML format. There are two key interfaces defined InvoiceElement
and IVisitor
. The detailed explanation is given below.
Figure 1a. Class Diagram of Customer Invoicing System
From the above class diagram you can see that Invoice
is a conceptual class responsible for defining the structure of an invoice. It implements InvoiceElement
interface and also maintains list of all invoice components. Invoice for this example contains three key InvoiceElements namely HeaderElement
, OrderElement
and FooterElement
. Each of these classes implements InvoiceElement
interface. InvoiceElement
interface expose one method accept()
which accepts IVisitor
as its parameter. All the concrete visitor classes must implement this interface. This method enables to extend the functionality of the original class, Invoice
in our case by implementing different types of visitors without doing changes to these classes. Hence Invoice
class clearly simplify its functionality by delegating the implementation of its extended features to different types of visitors.
IVisitor
interface exposes visit()
method and has several overloaded versions of it so that it can accept different types of InvoiceElement
as its paramters. So concrete implementation of visitor can operate based on specific types of InvoiceElement
passed to it and take appropriate action. In this example we have two concrete visitor classes namely InvoiceController
and SaveInvoiceInXML
. I have used InvoiceController
as one of the concrete visitor because I want to generate Invoice and render it in html. It has all the suitable references and infrastructure to interact with View
and Partial views
. I will cover detailed implementation in later section. SaveInvoiceInXML
is a visitor responsible for saving the Invoice
and its components in XML format.
Figure 1b shows another class diagram of customer invoicing system. This contains key controller classes CustomerController
,OrderController
and Business layer classes and other helper classes. These are explained below.
Figure 1b. Class Diagram of Customer Invoicing System
CustomerController
is a controller class and is responsible for the interaction with Customer
view. It also implements paging by using PagedList<Customer>
list using generic custom class PagedList<T>
. This class enables to wrap the list of any reference type entity and exposes key methods and properties required to implement paging functionality. OrderController
class is responsible for interaction with Order
view. It also implements paging by using PagedList<Order>
. BONorthWindFacade
class acts as a facade and provides an unified interface between controller classes and data layer. It exposes two key methods GetCustomers()
and GetOrdersForTheCustomer(string customerID)
. The data layer has been implemented using Entity Framework and NorthWindEntities
is a key class which provides a data context and access to key entitities such as Customer
,Order
,Product
and Order_Details
.
Business Layer
Business layer consists of key domain entities such as Customer
,Order
and Product
. BONorthwindFacade
is a class which act as a facade and provides unified interface for interaction with the data layer and controllers.The next section will explain the data layer and the detailed implementation of Invoice
class, visitor classses and other functionality features are covered in later sections.
Data Layer
Data layer is implemented using Entity Framework. NorthwindEntities
is a key class which acts as a data context and provides access to all key to the entities such as Customer
, Order
,Product
,Order_Details
. The entity diagram is shown in figure 2. All the data layer classes are auto generated using Visual Studio. Please refer the source code for more details.
Figure 2. ER diagram of Customer Invoicing System
Implementation of Visitors
Since this article is to explain the application of the visitor design pattern I will first cover the detailed implementation of the following classes Invoice
, InvoiceController
and SaveInvoiceInXML
.
Implementation of Invoice Class
Invoice
class is a conceptual class and holds the structure of the invoice and all its components. This class implements InvoiceElement
interface and has following key methods.
initInvoiceElements()
accept(IVisitor visitor).
Below code shows the details of this class.
public class Invoice:InvoiceElement
{
private List
<InvoiceElement> _invoiceElements;
public Invoice(Order o){
InitInvoice(o);
}
private HeaderVM InitHeader(){
HeaderVM vm = new HeaderVM();
vm.Name = "ABC PLC";
vm.Address1="121,J.B.Road";
vm.Address2 = "Fleet Street";
vm.City = "London";
vm.Zip = "LS0 5TQ";
vm.Country = "UK";
vm.Email = "sales@abcplcltd.com";
vm.Website = "http://www.abcplc.co.uk";
return vm;
}
private void InitInvoice(Order o)
{
_invoiceElements = new List<InvoiceElementgt;();
HeaderElement header= new HeaderElement(InitHeader());
_invoiceElements.Add(header);
OrderElement orderElemt = new OrderElement(o);
_invoiceElements.Add(orderElemt);
FooterElement footElement = new FooterElement("Thank you for doing Business with us!");
_invoiceElements.Add(footElement);
}
public void accept(IVisitor visitor)
{
_invoiceElements.ForEach(ie => ie.accept(visitor));
}
}
From the above code you can see that Invoice
class maintains list of other InvoiceElement
components. It's constructor accept instance of Order
entity as its parameter and then invokes InitInvoice()
method inside the constructor. This method initialises all Invoice components and adds them to the list. The main method is accept()
which in turn invokes accept()
method of all InvoiceElement
initialised earlier. Please see the use of lambda expression. Please refer source code for more details of the implementation details of HeaderElement
,OrderElement
and FooterElement
.
Implementing InvoiceController visitor
InvoiceController
class is a concrete visitor which is responsible to generate Invoice. Since we are generating the Invoice in HTML
format and as the controller class has all the suitable infrastructure to interact with the views, I have just extended this class by implementing IVisitor
interface. This class maintains a list of HTML
strings. These html is returned from Partial Views
and is rendered by visit()
method by operating on different InvoiceElement
components. Please see the code below for the details.
public class InvoiceController : Controller,IVisitor
{
private NORTHWNDEntities _dbContext = new NORTHWNDEntities();
private List<string> _htmlsectionList = new List<string>();
public ActionResult Index(int id=0)
{
Order od = _dbContext.Orders.Find(id);
if (od == null)
return HttpNotFound();
Invoice newInvoice = new Invoice(od);
ViewBag.CustomerID = od.CustomerID;
newInvoice.accept(this);
ViewBag.OrderID = id;
return View(_htmlsectionList);
}
public ActionResult BackToOrder(string id, int page = 1)
{
return RedirectToAction("Index", "Order", new { id = id, page = page });
}
public ActionResult Save(int id=0)
{
Order od = _dbContext.Orders.Find(id);
if (od == null)
return HttpNotFound();
Invoice newInvoice = new Invoice(od);
SaveInvoiceInXML saveVisitor = new SaveInvoiceInXML();
newInvoice.accept(saveVisitor);
string filePath = string.Format("{0}Order_{1}_{2}.xml", Request.PhysicalApplicationPath, od.CustomerID, od.OrderID);
try
{
if(System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
}
saveVisitor.InvoiceRoot.Save(filePath);
}
catch (Exception ex) {
throw ex;
}
return View(od);
}
public void visit(HeaderElement headerElement)
{
_htmlsectionList.Add(RenderRazorViewToString("_Header", headerElement.Header));
}
public void visit(OrderElement orderElement)
{
_htmlsectionList.Add(RenderRazorViewToString("_Order", orderElement.CurrentOrder));
}
public void visit(FooterElement footerElement)
{
_htmlsectionList.Add(RenderRazorViewToString("_Footer", footerElement));
}
public void visit(InvoiceElement invoiceElement)
{
}
public string RenderRazorViewToString(string viewName, object model)
{
ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
}
From the above code you can see that each version of visit()
method operates on specific InvoiceElement
and returns appropriate Html
markup from partial view and adds to the _htmlSectionList
collection. RenderRazorViewToString()
method is an important one as it converts the PartialViewResult
as html string. The Index()
method is simple but interesting method. It first finds the Order
based on id
passed. It then creates the instance of Invoice
class and then invokes it's accept()
method. It's this method which in turn invokes respective visit()
method to populate the _htmlSectionList
collection. Finally it returns the Invoice
view by passing _htmlSectionList
collection as model to the Index
view. The implementation of this view is very simple as it renders all the partial views
passed to it via _htmlSectionList
collection. The code for the index view is shown below.
@model List<string>
@{
ViewBag.Title = "Invoice";
}
<div class="container">
@foreach (var item in Model) {
@Html.Raw(item);
}
<div id="footer">
<ul class="nav nav-pills">
<li>
@Html.ActionLink("Back To Orders", "BackToOrder", new { id = ViewBag.CustomerID, page = 1 })
</li>
<li>
@Html.ActionLink("Save", "Save", new { id = ViewBag.OrderID })
</li>
</ul>
</div>
Above code is simple. All it does is render the html using Html.Raw()
method. Rest of the code is to show links and handler associated with it. See the use of Bootstrap.CSS
. The sample Invoice
output is shown in Figure 3.
Figure 3. Sample Invoice
Implementation of SaveInvoiceInXML visitor
SaveInvoiceInXML
is another visitor which is used to save the invoice in XML format. It's visit()
methods operate on respective InvoiceElement
and saves it's content in XML format. You need to ensure the read/write permission on the folder where it saves the XML file. Below code shows the details of this class.
public class SaveInvoiceInXML:IVisitor
{
private XElement _root = new XElement("invoice");
public void visit(InvoiceElement invoiceElement)
{
}
public void visit(HeaderElement headerElement)
{
_root.Add(new XElement("header",
new XElement("company",headerElement.Header.Name),
new XElement("address1",headerElement.Header.Address1),
new XElement("address2",headerElement.Header.Address1),
new XElement("city",headerElement.Header.City),
new XElement("country",headerElement.Header.Country),
new XElement("zip",headerElement.Header.Zip),
new XElement("email",headerElement.Header.Email),
new XElement("website",headerElement.Header.Website)
));
}
public void visit(OrderElement orderElement)
{
Decimal orderAmount = orderElement.CurrentOrder.Order_Details.Sum(odet=>(odet.UnitPrice*odet.Quantity));
Decimal taxAmount = 0.20M*orderAmount;
orderAmount +=taxAmount;
_root.Add( new XElement("order",
new XAttribute("orderid",orderElement.CurrentOrder.OrderID),
new XElement("orderdate",string.Format("{0:d}",orderElement.CurrentOrder.OrderDate)),
new XElement("shipping_details",
new XElement("contact_person",orderElement.CurrentOrder.Customer.ContactName),
new XElement("company",orderElement.CurrentOrder.Customer.CompanyName),
new XElement("address",orderElement.CurrentOrder.ShipAddress),
new XElement("city",orderElement.CurrentOrder.ShipCity),
new XElement("zip",orderElement.CurrentOrder.ShipPostalCode),
new XElement("country",orderElement.CurrentOrder.ShipCountry)
),
new XElement("orderdetails",
from odet in orderElement.CurrentOrder.Order_Details
select new XElement("order_detail",
new XElement("productid", odet.ProductID),
new XElement("description",odet.Product.ProductName),
new XElement("unitprice",odet.UnitPrice),
new XElement("quantity",odet.Quantity)
)
),
new XElement("vat",string.Format("{0:0.00}",taxAmount)),
new XElement("totalamount",string.Format("{0:0.00}",orderAmount))
));
}
public void visit(FooterElement footerElement)
{
_root.Add(new XElement("footer",footerElement.Footer));
}
public XElement InvoiceRoot { get { return _root; } }
}
From the above code you can see that the _root
variable is a root of the XML and it of type XElement
. Each visit()
method then adds the content of each type of InvoiceElement
to this root element. Save()
handler method in InvoiceController
class initialises this visitor and passes to accept()
method of Invoice
class which does the trick to populate the XML. Figure 4 shows XML format of the invoice.
Figure 4. Sample Invoice XML
Implementation of Other Controllers and PagedList<T> class
CustomerController
and OrderController
classes are responsible to display the Customer
and Order
views. Both these views implement paging feature and this is achieved thru custom PagedList<T>
class. I decided to built this feature from scratch instead of using any third party control for this purpose. This class and some of the pagination features of Bootstrap.CSS
simplify implementing this feature.
Implementation of PagedList<T>
PagedList<T>
is a generic class which holds an original collection of any reference type and exposes some properties and methods required for paging. It exposes following key methodes and properties.
getListFromPage(int page)
NoOfPages
CurrentPage
Implementation of this class is simple and the code is shown below.
public class PagedList<T> where T:class
{
private List<T> _orgList;
private int _noPages;
private int _pageSize;
private int _minPage = 1;
private int _lastPage = 1;
public PagedList (List<T> tlist,int pageSize){
_orgList =tlist;
_pageSize = pageSize;
if (tlist.Count % _pageSize == 0) {
_noPages = tlist.Count / _pageSize;
}
else
{
_noPages = tlist.Count / _pageSize + 1;
}
}
public int CurrentPage { get { return _lastPage; } set { _lastPage = value; } }
public int NoPages { get { return _noPages; } }
public List<T> GetListFromPage(int pageNo) {
int pageCount = _pageSize;
if (pageNo < _minPage)
{
pageNo = _minPage;
}
if (pageNo >= _noPages)
{
pageNo = _noPages;
pageCount = _orgList.Count - (_pageSize * (pageNo - 1));
}
_lastPage = pageNo;
return _orgList.Skip(_pageSize * (pageNo - 1)).Take(pageCount).ToList<T>();
}
}
From the above code you can see that the constructor of PagedList<T>
class accept any generic list holding collection of reference types and also you can specify the size of the page. Based on this it computes NoOfPages
. GetListFromPage()
method return subset of collection based on page index. Please the use of Skip()
and Take()
extension methods provided by System.Linq.Enumerable
class.
Implementation of CustomerController and OrderController classes
Please refer to source code for detailed implementation of these classes.
Points of Interest
If you are interested to know more about Bootstrap
css then you visit their site here. Also below I am sharing few links which may of interest to you.
Those who are interested in other design patterns then you use below links for more details.
Conclusion
Design patterns provide solution to recurring problems faced in software development. Visitor
is popular design pattern and I hope this article will provide you the insight of how this pattern works and its application in real life scenarios. I hope you will enjoy reading this article. If you have any queries and need more information then you can e-mail me. Thank you.