Click here to Skip to main content
15,867,771 members
Articles / Web Development / HTML

Developing, Architecting and Testing Web Applications with MVC 5, Web API 2, KnockoutJS, Ninject and NUnit

Rate me:
Please Sign up or sign in to vote.
4.96/5 (99 votes)
21 Sep 2013CPOL25 min read 151.1K   5.9K   208   22
The latest technology from the Microsoft Stack

Download source

Introduction 

Being a Microsoft software developer is a lot like ordering breakfast at the International House of Pancakes. You get a stack of pancakes with every dish and you must choose from a variety of pancakes and syrup flavors. For web applications, a solution stack is a set of software subsystems or components needed to deliver a fully functional solution, be it a product or a service. To develop a web application for example, the Microsoft developer needs to use and understand the Microsoft Stack of components including the ever emerging set of open source tools, design patterns and 3rd party products. That's a lot of pancakes.

Overview 

The goal of this article is to walk through a sample Customer Maintenance web application that implements the latest technology from the Microsoft stack, including the latest beta versions of Microsoft .NET 4.5.1, Visual Studio Express 2013, MVC 5, WebAPI 2 and Entity Framework 6.

Throughout this article various design patterns and techniques will be implemented to help foster a loosely coupled design that promotes the Separation of Concerns (SoC) through the various layers of an n-tier web application. Overall the implementation of this sample application is a variation of my previous Code Project article MVC Techniques with jQuery, JSON, Knockout, and C#.

POCO Classes - Plain Old CLR Objects (POCO) Classes 

The Entity Framework comes with a modeling tool that lets you design your domain and database objects - including using either of the following three options: Database First, Model First or the Code First technique.

I chose to use a "Code First" approach to creating my domain objects by creating POCO classes for my domain objects. Creating POCO (Plain Old CLR Objects) means that you can create standard .NET classes (written in any .NET supported language) for defining your domain design - unencumbered by attributes or inheritance required by specific frameworks.

C#
// Customer Domain Entity 
public class Customer
{
    public Guid CustomerID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
    public string Address { get; set; }
    public string City { get; set; } 
    public string Region { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
    public string PhoneNumber { get; set; }
    public string CreditCardNumber { get; set; }
    public Guid PaymentTypeID { get; set; }
    public DateTime? CreditCardExpirationDate { get; set; }
    public string CreditCardSecurityCode { get; set; }
    public Decimal CreditLimit { get; set; }
    public DateTime? DateApproved { get; set; }
    public int ApprovalStatus { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateUpdated { get; set; }
} 

Data Layer Interfaces

One of the design goals of this application is to create a loosely coupled interaction between the different tiers of the application. This Customer Maintenance application will have a business layer that will have a dependency on a data layer to interact with the database.

The below interfaces will be implemented by the data layer. To make the data layer loosely coupled from the business layer, the business layer will not instantiate the data layer. The data layer will be injected into the business layer's constructor and will only know about the data layer's ICustomerDataService interface and not the concrete implementation of the data access layer.

As this application grows it might be determined that we might want to swap out the Entity Framework data access layer with a more traditional approach that incorporates ADO.NET and/or stored procedures. Additionally we could use a mocked data layer for executing Continuous Integration unit tests.

C#
public interface IDataService
{ 
    void CreateSession();
    void BeginTransaction();
    void CommitTransaction(Boolean closeSession);
    void RollbackTransaction(Boolean closeSession);
    void CloseSession();
}

public interface ICustomerDataService : IDataService, IDisposable
{
    void CreateCustomer(Customer customer);
    void UpdateCustomer(Customer customer);
    Customer GetCustomerByCustomerID(Guid customerID);
    List<CustomerInquiry> CustomerInquiry(string firstName, string lastName);
    List<PaymentType> GetPaymentTypes();
    PaymentType GetPaymentType(Guid paymentTypeID);  
} 

Customer Maintenance View 

The default project template that comes with MVC 5 includes Twitter Bootstrap. Bootstrap is a free collection of tools for creating websites and web applications. It contains HTML and CSS-based design templates for typography, forms, buttons, navigation and other interface components, as well as optional JavaScript extensions. The Views for this Customer Maintenance project are formatted to the Bootstrap style sheet.

Image 1

Plain Old HTML 

As I have been working with MVC Views and the Razor View Engine the last few years my design preference has changed a bit when it comes to the View. My approach has changed to making views as clean as possible. The Customer Maintenance View will mainly contain plain old HTML and will not contain any server side scripting Razor code. There also will be minimal or no references to JavaScript in the view. The only link between the view and JavaScript will be through KnockoutJS data-bind tags.

This approach makes the view a self contained piece of easy to read HTML code. Of course as a view becomes more and more complex; some Razor code will eventually be needed. Fortunately, the Razor View Engine is a precise, useful, light language that enables you to create Views for MVC projects in ASP.NET while still keeping a separation of concerns, with the ability to test, and follow pattern based development.  
HTML
<!--- Customer Maintenance View --->
<div class="hero-unit" data-bind="visible: DisplayContent" style="display:no"> 
<table class="table-condensed">
<tr>
 <td class="input-label"><label>First Name:</label></td>
 <td class="input-box"> 
     <div data-bind="visible: EditMode" class="edit-div">        
         <input id="FirstName" type="text" data-bind="value: FirstName" />
         <span class="required-indicator">*</span>
     </div>
     <div class="display-div" data-bind="text: FirstName, visible: DisplayMode "></div>    
 </td>
 <td class="input-label"><label>Last Name:</label></td>
 <td class="input-box">
      <div data-bind="visible: EditMode" class="edit-div">
        <input id="LastName" type="text" data-bind="value: LastName" />
        <span class="required-indicator">*</span>
      </div>
      <div class="display-div" data-bind="text: LastName, visible: DisplayMode "></div>
</td>
</tr>
<tr>
<td class="input-label"><label>Email Address:</label></td>
<td class="input-box">
<div data-bind="visible: EditMode" class="edit-div">
     <input id="EmailAddress" type="text" data-bind="value: EmailAddress" />
     <span class="required-indicator">*</span>
</div>
<div class="display-div" data-bind="text: EmailAddress, visible: DisplayMode "></div>
</td>
<td class="input-label"><label>Phone Number:</label></td>
<td class="input-box">
    <div data-bind="visible: EditMode" class="edit-div">
         <input id="PhoneNumber" type="text" data-bind="value: PhoneNumber " />
    </div>
    <div class="display-div" data-bind="text: PhoneNumber, visible: DisplayMode "></div>
</td>
</tr>
</table>
</div>
   
    
<div style="clear:both"></div>
  <span data-bind="visible: EnableCreateButton">
    <input id="btnCreate" type="button" value="Create" 
         data-bind="click: CreateCustomer" class="btn btn-primary btn-medium" />
  </span>
</div>    

MVVM - Model View View Model KnockoutJS & JavaScript 

The JavaScript for the Customer Maintenance View will interact with the view through KnockoutJS. Knockout is a standalone JavaScript implementation of the Model-View-View Model (MVVM) pattern. Knockout helps provide a clear separation between the presentation HTML markup and the data to be displayed. Knockout helps you easily associate DOM elements with Knockout's view model data using a concise, readable syntax. When your data model's state changes, your UI updates automatically.

The JavaScript in the Customer Maintenance project will make no direct reference to the Document Object Model (DOM) of the view. Using data-bind tags in the HTML, the view will be data bound to the view model created in the JavaScript. Access to the view data and the view's elements will be made by directly accessing the Knockout view model, The MVVM design pattern makes both the view and view's supporting JavaScript cleaner to read and write and test.
JavaScript
//KnockoutJS View Model Setup and Initialization
$(document).ready(function () {
    InitializeCustomerMaintenanceViewModel(); 
    GetCustomerInformation();
  
});

function CustomerMaintenanceViewModel() {
    this.CustomerID = ko.observable("");
    this.FirstName = ko.observable("");
    this.LastName = ko.observable("");
    this.EmailAddress = ko.observable("");
    this.Address = ko.observable("");
    this.City = ko.observable("");
    this.Region = ko.observable("");
    this.PostalCode = ko.observable("");
    this.Country = ko.observable("");
    this.PhoneNumber = ko.observable("");
    this.CreditCardNumber = ko.observable(""); 
    this.PaymentTypes = ko.observableArray();
    this.PaymentTypeID = ko.observable("");
    this.PaymentTypeDescription = ko.observable("");
    this.CreditCardExpirationDate = ko.observable("");
    this.CreditCardSecurityCode = ko.observable("");
    this.CreditLimit = ko.observable("");
    this.MessageBox = ko.observable("");
}

function InitializeCustomerMaintenanceViewModel() {
    customerMaintenanceViewModel = new CustomerMaintenanceViewModel();
    ko.applyBindings(customerMaintenanceViewModel);
} 

ASP.NET Web Forms 

If you are using ASP.NET Web Forms you can implement this same client-side technique described above by including only HTML controls in your ASPX page, excluding ASP.NET server controls from the page and turning off view state. In essence this will lead you to move away from the ASP.NET Web Forms POSTBACK model. Your code-behind file will be empty except for any initial user authentication and security needs in either the Page_Init or Page_Load events of your Web Form. All your server side code interactions will initiate from a web service or RESTful service, or if you prefer, ASP.NET page methods could be used in your ASP.NET Web Form. In the end you'll be simulating MVC behavior using a Web Form.

MVC Controllers 

Since this is an MVC project, the views will be rendered through MVC controllers. To keep things clean and simple, the controllers for this project will have minimal responsibility, which is to simply authenticate the user and render the view to the client. In the case where a customer is being edited, the customerID will be passed into the controller method and the customerID will then be passed to the view through a view model class object.
C#
public class CustomersController : Controller
{     
    /// Customer Maintenance        
    [AuthenicationAction]
    public ActionResult CustomerMaintenance()
    {                
        return View("CustomerMaintenance");
    }
    
    /// Customer Inquiry       
    [AuthenicationAction]
    public ActionResult CustomerInquiry()
    {
        return View("CustomerInquiry");
    }
                        
    /// Edit Customer      
    [AuthenicationAction]
    public ActionResult EditCustomer(string customerID)
    {
        CustomerMaintenanceViewModel viewModel = new CustomerMaintenanceViewModel();
        viewModel.Customer.CustomerID = new Guid(customerID);
        return View("CustomerMaintenance", viewModel);
    }
} 

jQuery AJAX 

When the Customer Maintenance View loads, it will make a jQuery AJAX call to retrieve all the data required to be presented in the view. This approach provides for a greater user experience allowing the view to be partially rendered and allowing the page to appear to be more responsive instead of making the user wait for the entire contents of the page to be rendered all at once.
JavaScript
function GetCustomerInformation()
{ 
    MVC5WebApplication.DisplayAjax();

    var customer = new function () { };
    customer.CustomerID = customerMaintenanceViewModel.CustomerID();
    var jqxhr = $.get(
        "/api/customers/GetCustomerMaintenanceInformation", 
        customer, 
        function (response) {
            GetCustomerCompleted(response);
        },
    "json")
    .fail(function (response) {
        RequestFailed(response);
     }
   );
}

function GetCustomerCompleted(response) {

    customerMaintenanceViewModel.PaymentTypes(response.PaymentTypes); 
    customerMaintenanceViewModel.FirstName(response.Customer.FirstName);
    customerMaintenanceViewModel.LastName(response.Customer.LastName);
    customerMaintenanceViewModel.Address(response.Customer.Address);
    customerMaintenanceViewModel.City(response.Customer.City);
    customerMaintenanceViewModel.Region(response.Customer.Region);
    customerMaintenanceViewModel.PostalCode(response.Customer.PostalCode);
    customerMaintenanceViewModel.Country(response.Customer.Country);
    customerMaintenanceViewModel.PhoneNumber(response.Customer.PhoneNumber);
    customerMaintenanceViewModel.EmailAddress(response.Customer.EmailAddress); 

    MVC5WebApplication.HideAjax();
} 

jQuery AJAX HTTP POST 

To create a new customer, the user will press the Create customer button, and the following JavaScript is executed. The JavaScript below will block the UI (using a jQuery plugin from malsup.com ), display an AJAX message that the server request is running, and populate a customer object with data from the view model. Using the jQuery AJAX POST method, the customer object will automatically be serialized to a JSON object and sent to the server over the HTTP wire to a server route named "/api/customers/create".

JavaScript
function CreateCustomer() {
  
    MVC5WebApplication.DisplayAjax();

    var customer = new PopulateCustomerInformation();
    var jqxhr = $.post("/api/customers/create", customer, 
        function (response) {
            CreateCustomerCompleted(response);
        },
        "json")
        .fail(function (response) {
            RequestFailed(response);
        }
    );
}
function PopulateCustomerInformation() {

    var customer = new function () { };

    customer.FirstName = customerMaintenanceViewModel.FirstName();
    customer.LastName = customerMaintenanceViewModel.LastName();
    customer.Address = customerMaintenanceViewModel.Address();
    customer.City = customerMaintenanceViewModel.City();
    customer.Region = customerMaintenanceViewModel.Region();
    customer.PostalCode = customerMaintenanceViewModel.PostalCode();
    customer.Country = customerMaintenanceViewModel.Country();
    customer.PhoneNumber = customerMaintenanceViewModel.PhoneNumber();
    customer.EmailAddress = customerMaintenanceViewModel.EmailAddress();
    customer.PaymentTypeID = customerMaintenanceViewModel.PaymentTypeID();

    return customer;
}

WebAPI 2 - RESTful Services 

The AJAX requests for the Customer Maintenance project will interact with the server through the latest edition of Microsoft's ASP.NET WebAPI 2.0 component. ASP.NET WebAPI is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices.

ASP.NET WebAPI is an ideal platform for building RESTful applications on the .NET Framework. The framework is full of abstractions. There are controllers, filter providers, model validators, and many other components that form the plumbing of the framework.

You create Web APIs by creating WebAPI Controllers that mimic MVC controllers and borrow from the MVC routing system. The first release of the WebAPI used convention-based routing. When the framework received a request, it matched the URI against the route driven by the HTTP verb (GET,POST,PUT,DELETE, etc).

To create a customer the HTTP verb POST would be used and the PUT verb would be used to update a customer with the GET verb used to retrieve a customer. This was sufficient when publishing simple public RESTful API's. For line of business applications where a single entity (Invoices, Sales Orders, Purchase Orders, etc.) requires several types of updates including updating, approving and voiding the entity, simple convention based routing is not suitable.

Version 2 of the WebAPI introduces a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API. For example, you can easily use the same POST verb that routes to distinct WebAPI method calls that perform different types of functions To enable attribute routing in your MVC web application you need to modify the WebApiConfig class that resides in the App_Start folder and add the line config.MapHttpAttributeRoutes() to the Register method.
C#
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}  

WebAPI Controllers 

To create Web API's, you add (Scaffold) a MVC WebAPI controller to the Controllers folder of your MVC web project. If you have worked with ASP.NET MVC, then you are already familiar with controllers. They work similarly in WebAPI, but controllers in WebAPI derive from the ApiController class instead of Controller class. The major difference you will notice is that actions on WebAPI controllers do not return views, they return data.
C#
[RoutePrefix("api/customers")]
public class CustomersApiController : ApiController
{
    ICustomerDataService customerDataService;       
        
    /// Constructor with Dependency Injection using Ninject
    public CustomersApiController(ICustomerDataService dataService)
    {
        customerDataService = dataService;           
    }

    /// Create Customer   
    [WebApiAuthenication]  
    [HttpPost("create")]
    public HttpResponseMessage CreateCustomer(
    HttpRequestMessage request, [FromBody] CustomerMaintenanceDTO customerDTO)
    {
        TransactionalInformation transaction;

        CustomerMaintenanceViewModel viewModel = new CustomerMaintenanceViewModel();
           
        if (!ModelState.IsValid)
        {
            var errors = ModelState.Errors();

            viewModel.ReturnMessage = ModelStateHelper.ReturnErrorMessages(errors);
            viewModel.ReturnStatus = false;

            var badResponse = Request.CreateResponse<CustomerMaintenanceViewModel>
                              (HttpStatusCode.BadRequest, viewModel);

            return badResponse;
        }

        Customer customer = new Customer();
        ModelStateHelper.UpdateViewModel(customerDTO, customer);
 
        CustomerApplicationService service = new 
               CustomerApplicationService(customerDataService);

        service.CreateCustomer(customer, out transaction);

        viewModel.Customer = customer;
        viewModel.ReturnStatus = transaction.ReturnStatus;
        viewModel.ReturnMessage = transaction.ReturnMessage;  
      
        if (transaction.ReturnStatus == false)
        {
            var badResponse = Request.CreateResponse<CustomerMaintenanceViewModel>
                              (HttpStatusCode.BadRequest, viewModel);
            return badResponse;
        }
        else
        {
            var response = Request.CreateResponse<CustomerMaintenanceViewModel>
                           (HttpStatusCode.Created, viewModel);
            return response;                           
        }
      
    }

    /// Get Customer Maintenance Information
    [WebApiAuthenication]  
    [HttpGet("GetCustomerMaintenanceInformation")]
    public HttpResponseMessage GetCustomerMaintenanceInformation(
    HttpRequestMessage request,Guid customerID)
    {
        TransactionalInformation customerTransaction;
        TransactionalInformation paymentTransaction;
        CustomerMaintenanceViewModel viewModel = new CustomerMaintenanceViewModel();
     
        CustomerApplicationService service = 
             new CustomerApplicationService(customerDataService);

        if (customerID != Guid.Empty)
        {
            Customer customer = service.GetCustomerByCustomerID(
                                   customerID, out customerTransaction);

            viewModel.Customer = customer;
            viewModel.ReturnStatus = customerTransaction.ReturnStatus;
            viewModel.ReturnMessage = customerTransaction.ReturnMessage;

        }
         
        List<PaymentType> paymentTypes = service.GetPaymentTypes(out paymentTransaction);
        viewModel.PaymentTypes = paymentTypes;

        if (paymentTransaction.ReturnStatus == false)
        {              
            viewModel.ReturnStatus = paymentTransaction.ReturnStatus;
            viewModel.ReturnMessage = paymentTransaction.ReturnMessage;
        }
       
        if (viewModel.ReturnStatus == true)
        {
            var response = Request.CreateResponse<CustomerMaintenanceViewModel>
                           (HttpStatusCode.OK, viewModel);

            return response;
        }

        var badResponse = Request.CreateResponse<CustomerMaintenanceViewModel>
                          (HttpStatusCode.BadRequest, viewModel);

        return badResponse;
    }
} 

Route Attributes 

The [HttpGet] attribute defines an HTTP GET method. The string "GetCustomerInformation" is the URI template for the "api/customers/GetCustomerInformation" route. The [HttPost] attribute defines an HTTP POST method with a route of "create" which maps to "api/customers/create". Instead of specifying the entire route path on each method, you can add a route prefix at the class level using the attribute such as [RoutePrefix("api/customers")]. Route attributes such as HttpGet and HttpPost also restrict the client application to calling those routes by the specified HTTP verb.    

C#
[RoutePrefix("api/customers")]
public class CustomersApiController : ApiController
{
    [WebApiAuthenication] 
    [HttpGet("GetCustomerInformation")]
    public HttpResponseMessage GetCustomerMaintenanceInformation()
    {
    
    }  
    [WebApiAuthenication] 
    [HttpPost("create")]
    public HttpResponseMessage CreateCustomer()
    {
    }
}   

Custom Action Filters 

The [WebApiAuthenication] attribute is a custom WebAPI Action Filter that will authenticate the user using FORMS Authentication and executes the following code before executing the method. This action filter is similar to the action filter [AuthenicationAction] used to authenticate the user in the MVC controller methods when rendering views. The [WebApiAuthenication] action filter will override the OnActionExecuting event of the WebAPI controller action.

Since the WebAPI controller methods will only be called from the Customer Maintenance application, the filter will validate that the method call is being executed from an AJAX request by checking the request header for a property called X-Requested-With to determine if the request is coming from an AJAX request. Additionally this custom filter will check to see if the user has been authenticated. If either of these conditions is not met, then the filter will return a bad request response HTTP 400 status code back to the calling client and the WebAPI action will not be performed.

C#
public class WebApiAuthenicationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
    public override void OnActionExecuting(Http.Controllers.HttpActionContext actionContext)
    {
        var request = actionContext.Request;
        var headers = request.Headers;

        if (!headers.Contains("X-Requested-With") || 
            headers.GetValues("X-Requested-With").FirstOrDefault() != "XMLHttpRequest")
        {
            TransactionalInformation tran = new TransactionalInformation();
            
            tran.ReturnMessage.Add("Access has been denied.");
            tran.ReturnStatus = false;

            actionContext.Response = request.CreateResponse<TransactionalInformation>
                                     (HttpStatusCode.BadRequest, tran);
        }
        else
        {
            HttpContext ctx = default(HttpContext);
            ctx = HttpContext.Current;
            if (ctx.User.Identity.IsAuthenticated == false)
            {
                TransactionalInformation tran = new TransactionalInformation();

                tran.ReturnMessage.Add("Your session has expired.");
                tran.ReturnStatus = false;

                actionContext.Response = request.CreateResponse<TransactionalInformation>
                                         (HttpStatusCode.BadRequest, tran);
            }
        }
    }
} 

Model Binding 

When the WebAPI receives an HTTP request, it converts the request into .NET types based on the signature of the action. Dynamic model binding is used to automatically populate the parameters of your action. In the Create Customer action, I created a Customer Data Transformation object that will get instantiated and populated through the WebAPI model binder.

Since the Create Customer request is being sent via a POST request, the values of the request will reside in the body of the request. To read the values from the request body, a [FromBody] attribute is needed to tell the model binder how to populate the action signature. Since the WebAPI only allows one parameter to be tagged with the [FromBody] attribute, a customer data transformation object is required.

Customer Data Transformation Object 

The customer data transformation object (DTO) is similar to the customer domain entity object. The customer domain entity object could have been used here, but if you intend to build an application that will grow in the future, you should consider using a DTO object because domain entities are not optimal for data transformation. Domain entities always have more or less properties then you need on the client side which can cause over-binding to occur and make model binding inefficient.
C#
public class CustomerMaintenanceDTO
{

    public Guid CustomerID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string Region { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
    public string PhoneNumber { get; set; }
    public string CreditCardNumber { get; set; }
    public Guid? PaymentTypeID { get; set; }
    public DateTime? CreditCardExpirationDate { get; set; }
    public string CreditCardSecurityCode { get; set; }
    public Decimal? CreditLimit { get; set; }

} 

Model Binding Validation 

When a client sends data to your web API, the model binder will validate the data before doing any processing. Any parameters that cannot be converted to a .NET type will set the state of the model to invalid. Additionally you could have annotations on your DTO object for data validation (such a marking certain parameters required, etc). The WebAPI does not automatically return an error to the client when validation fails. It is up to the WebAPI controller action to check the model state and respond appropriately. In the code snippet below, the status of the model state is checked and the controller action returns an HTTP status 400 Bad Request code back to the client if the model state is invalid.
C#
if (!ModelState.IsValid)
{ 

    var errors = ModelState.Errors();

    viewModel.ReturnMessage = ModelStateHelper.ReturnErrorMessages(errors);
    viewModel.ReturnStatus = false;
    
    var badResponse = Request.CreateResponse<CustomerMaintenanceViewModel>
                      (HttpStatusCode.BadRequest, viewModel);

    return badResponse;
}     

HTTP Status Codes 

When the Create Customer action successfully executes, it returns an HTTP 201 status code specifying that the requested data has been created. RESTful services are traditionally built over HTTP. The protocol communicates the status of the request by means of HTTP status codes. It is difficult to implement all of the status codes in an application, but as a requirement and a best practice, all your WebAPI actions should return an HTTP status code. Perhaps the best approach is to limit the number of status codes exposed to a handful. Your clients can take action based on these HTTP codes and implement them fairly easily.

The HTTP Request Message 

Finally, the WebAPI also gives you the ability to see the entire HTTP request message sent to the action. You can access this information by supplying an HttpRequestMessage parameter in your action signature.

Dependency Injection - Ninject 

One of the goals of this Customer Maintenance application is to create loosely coupled layers. The Customer business layer that is being consumed by the WebAPI methods, is dependent on a data access layer. Instead of instantiating the data access layer directly within the business layer, the data access layer will be passed into the business layer through its constructor.

The data access layer will be injected into the WebAPI controller's constructor using Ninject. This will allow us to swap out the data access layer without having to make any changes to the business layer. For unit testing purposes we might want to use a mocked version of the data access layer that doesn't actually hit the database.

Ninject is a fast, lightweight dependency injector for .NET applications. It helps you split your application into a collection of loosely-coupled, highly-cohesive pieces. By using Ninject to support your software’s architecture, your code will become easier to write, reuse, test, and modify.

Installing Ninject with NuGet 

After creating a new MVC application, run Install-Package Ninject.MVC3 from the package manager console in Visual Studio. This will download and install all the required assemblies and add some source code to integrate it into the application. After the installation you will find a NinjectWebCommon.cs file in the App_Start folder. This class file will contain the code needed to configure Ninject.

NuGet Package Manager 

NInject.MVC3 can also be found and installed from the Nuget Package Gallery. NuGet is a Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects that use the .NET Framework. When you add a library or tool, NuGet copies files to your solution and automatically makes whatever changes that are needed in your project, such as adding references and changing your app.config or web.config file. When you remove a library, NuGet removes files and reverses whatever changes it made in your project so that no clutter is left. The NinjectWebCommon.cs class file below, registers your dependent objects.
C#
public static class NinjectWebCommon
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();
   
    /// Starts the application    
    public static void Start()
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));

        bootstrapper.Initialize(CreateKernel);
    }
    /// Stops the application.
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }
   
    /// Creates the kernel that will manage your application.
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);

        // Install our Ninject-based IDependencyResolver into the Web API config
        GlobalConfiguration.Configuration.DependencyResolver = 
                new NinjectDependencyResolver(kernel);

        return kernel;
    }
   
    /// Load your modules or register your services here!
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<MVC5DataServiceInterface.ICustomerDataService>().To
               <MVC5EntityFrameworkDataAccess.EFCustomerService>();     
    }
} 

Registering Objects with Ninject 

In the RegisterServices method, you use the Ninject kernel.Bind command to register your dependent objects. By registering your dependent objects, Ninject will instantiate and inject your registered objects into your class constructors based on the signature of the constructor. This is where implementing your classes against a class interface allows you to inject different objects into your constructor that implement the same interface. In the example above, I'm registering with Ninject, the Entity Framework version of the Customer Data Service component that implements the ICustomerDataService interface.

Dependency Injection (DI) implements the Inversion of Control (IoC) programming technique. The Inversion of Control and Dependency Injection patterns are all about removing dependencies from your code. In the below WebAPI class constructor, object coupling is bound at run time and the actual object is not known at compile time. With a single line of code in the NinjectWebCommon Ninject class, you are able to tell the Ninject kernal which object to load at run-time. For example, you can provide a data access layer that uses an Entity Framework implementation or you could switch to an ADO.NET based data access layer. For unit testing purposes you could also inject a mocked object of your data access layer. The sample Customer Maintenance application contains code to implement all three of these different data access layers.
C#
ICustomerDataService customerDataService;       
         
/// Constructor with Dependency Injection using Ninject
public CustomersApiController(ICustomerDataService dataService)
{
    customerDataService = dataService;           
} 

Dependency Resolution Issues and the ApiController Class 

The difficulty presented with the WebAPI is that Ninject bindings don’t apply to ApiController instances. When you try to hit one of your API endpoints you will receive an error stating that your controller does not have a default constructor. To work around this problem you need to set up a dependency resolver. To handle the dependency resolutions, you need to create two custom classes that will be used by the NinjectWebCommon class. The below dependency resolver code was added to the NinjectWebCommon class file and can be found online from various technical blogs. Check out Phil Haack's blog on dependency resolvers.
C#
public class NinjectDependencyScope : IDependencyScope
{
    IResolutionRoot resolver;
    public NinjectDependencyScope(IResolutionRoot resolver)
    {
        this.resolver = resolver;
    }
    public object GetService(Type serviceType)
    {
        if (resolver == null)
            throw new ObjectDisposedException("this", "This scope has been disposed");
        return resolver.TryGet(serviceType);
    }
    public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
    {
        if (resolver == null)
            throw new ObjectDisposedException("this", "This scope has been disposed");
        return resolver.GetAll(serviceType);
    }
    public void Dispose()
    {
        IDisposable disposable = resolver as IDisposable;
        if (disposable != null)
            disposable.Dispose();
        resolver = null;
    }
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
    IKernel kernel;
    public NinjectDependencyResolver(IKernel kernel)
        : base(kernel)
    {
        this.kernel = kernel;
    }
    public IDependencyScope BeginScope()
    {
        return new NinjectDependencyScope(kernel.BeginBlock());
    }
} 

The Business Layer 

The business layer for the Customer Maintenance project contains the key methods for creating, updating, validating and retrieving customer information. Passing in the ICustomerDataService interface into the business layer makes it easy to keep the business logic layer independent of the data access layer.

The underlying concrete methods of the data access layer are hidden from the business layer. Customer objects and generic lists of customer objects are passed from and to the data access layer regardless of what data access technology is hitting the database. This makes it easy to switch out the data access layer without changes to the business layer. As you can see below, there is no explicit data access code within the business layer. For example, the CreateSession method could either initialize an Entity Framework data context object or create a database connection using standard ADO.NET depending on which data access layer is being injected into the business layer.

C#
public class CustomerApplicationService
{
    ICustomerDataService _customerDataService;
    private ICustomerDataService CustomerDataService
    {
        get { return _customerDataService; }
    }
 
    /// Constructor
    public CustomerApplicationService(ICustomerDataService dataService)
    {
        _customerDataService = dataService;
    }
    
    /// Create Customer    
    public void CreateCustomer(Customer customer, out TransactionalInformation transaction)
    {
        transaction = new TransactionalInformation();
        CustomerBusinessRules customerBusinessRules = new CustomerBusinessRules();

        try
        {
            CustomerDataService.CreateSession();
            
            customerBusinessRules.ValidateCustomer(customer, CustomerDataService);
            if (customerBusinessRules.ValidationStatus == true)
            {
                CustomerDataService.BeginTransaction();
                CustomerDataService.CreateCustomer(customer);
                CustomerDataService.CommitTransaction(true);
                transaction.ReturnStatus = true;
                transaction.ReturnMessage.Add("Customer successfully created.");
            }
            else
            {
                transaction.ReturnStatus = customerBusinessRules.ValidationStatus;
                transaction.ReturnMessage = customerBusinessRules.ValidationMessage;
                transaction.ValidationErrors = customerBusinessRules.ValidationErrors;
            }
        }
        catch (Exception ex)
        {            
            CustomerDataService.RollbackTransaction(true);
            transaction.ReturnMessage = new List<string>();
            string errorMessage = ex.Message;
            transaction.ReturnStatus = false;
            transaction.ReturnMessage.Add(errorMessage);
        }
        finally
        {
            CustomerDataService.CloseSession();
        }          
    }
} 

Unit Testing - NUnit 

While developing the sample application for this article, I decided to install the NUnit unit testing tool through the NuGet package management tool so I could write some sample unit tests against the business layer of the application.

After writing a couple unit tests it occurred to me that unit testing seems to be much more popular in theory than in practice. Why do you think this is the case? Well, there’s a huge barrier to entry because setting up realistic and useful tests is hard. While writing my unit tests it also occurred to me that I was actually writing integration tests and not unit tests at all.

A couple key things I have learned about writing good unit tests is that writing good unit tests includes writing the test first, then the code using a Test Driven Development (TDD) approach. This ensures that you are writing testable code. You will also need to develop and program your classes to implement interfaces. This will allow you to inject mocked objects of external dependencies such as databases and other data stores and file systems. Additionally, you should create a process where you can refresh your test data before each run of your suite of unit tests.

This is helpful if you want to implement a Continuous Integration process where all your suite of unit tests run after each check-in of code and after a software build runs. This sample application contains test data that can refresh the database tables in SQL-Server Express. You can also use this test data to build mocked data in your mocked data access layer that doesn't actually hit the database. To refresh the data, I included a Seed class library that can be run from the application menu or from the mocked data access object.

When the below NUnit test starts up, it executes the InitializeDependencies method because it is tagged with an [SetUp] attribute. This is the attribute that marks a class that contains the one-time setup for all the test fixtures under a given namespace. The class may contain at most one method marked with the [SetUp] attribute and one method marked with the [TearDown] attribute.

The InitializeDependencies method below will conditionally create the particular ICustomerDataService object that you want to use for testing purposes. In a Continuous Integration environment you will want to create mocked objects for certain objects that required external resources such as databases, data stores etc. You will want to do this because you might have hundreds of tests that need to execute and you will want your tests to run in a timely fashion when developers are checking in code that initiate automatic builds and tests.

Additionally, you will probably also run a nightly build of your application that uses a different configuration for executing your dependencies. In the examples below, I have three test methods. NUnit will execute methods that have the [Test] attribute. You can use an NUnit Assert method in your test methods to evaluate the condition of your test. It is a best practice to limit each of your unit tests to just one assertion per unit test.

C#
[TestFixture]
public partial class Testing
{
    ICustomerDataService customerDataService;
    /// Initialize Dependencies
    [SetUp]
    public void InitializeDependencies()
    {
        string integrationType = ConfigurationManager.AppSettings["IntegrationType"];

        if (integrationType == "EntityFramework")
            customerDataService = new EFCustomerService();
        else if (integrationType == "Ado")
            customerDataService = new AdoCustomerService();
        else
            customerDataService = new MockedCustomerService();

    }
    /// Create Customer Integration Test
    [Test]
    public void CreateCustomerIntegrationTest()
    {
        string returnMessage;

        TransactionalInformation transaction;

        CustomerApplicationService service = 
                new CustomerApplicationService(customerDataService);

        List<PaymentType> paymentTypes = service.GetPaymentTypes(out transaction);
        var paymentType = (from p in paymentTypes where p.Description == "Visa" 
                           select p).First();

        Customer customer = new Customer();

        customer.FirstName = "William";
        customer.LastName = "Gates";
        customer.EmailAddress = "bgates@microsoft.com";
        customer.PhoneNumber = "(425)882-8080";
        customer.Address = "One Microsoft Way";
        customer.City = "Redmond";
        customer.Region = "WA";
        customer.PostalCode = "98052-7329";
        customer.PaymentTypeID = paymentType.PaymentTypeID;
        customer.CreditCardExpirationDate = Convert.ToDateTime("12/31/2014");
        customer.CreditCardSecurityCode = "111";
        customer.CreditCardNumber = "123111234";

        service.CreateCustomer(customer, out transaction);

        returnMessage = Utilities.GetReturnMessage(transaction.ReturnMessage);

        Assert.AreEqual(true, transaction.ReturnStatus, returnMessage);

    }
    /// Test Valid Email Address
    [Test]
    public void TestValidEmailAddress()
    {
         
        Customer customer = new Customer();

        customer.EmailAddress = "bgates@microsoft.com";
            
        ValidationRules validationRules = new ValidationRules();
        validationRules.InitializeValidationRules(customer);

        Boolean returnStatus = validationRules.ValidateEmailAddress("EmailAddress");

        Assert.AreEqual(true, returnStatus);
    }
   
    /// Test InValid Email Address    
    [Test]
    public void TestInValidEmailAddress()
    {
        Customer customer = new Customer();

        customer.EmailAddress = "bgates@microsoft";

        ValidationRules validationRules = new ValidationRules();
        validationRules.InitializeValidationRules(customer);

        Boolean returnStatus = validationRules.ValidateEmailAddress("EmailAddress");

        Assert.AreEqual(false, returnStatus);
    }
} 

NUnit GUI Test Runner 

The nunit.exe NUnit Test Runner program (that was downloaded with the NUnit NuGet package) is a graphical runner. It shows the tests in an explorer-like browser window and provides a visual indication of the success or failure of the tests. It allows you to selectively run single tests or suites and reloads automatically as you modify and re-compile your code. You can also run your tests through a command line, which can be included in a Continuous Integration build and test environment.

Image 2

Data Binding and Data Grids 

Finally, no web application is complete without implementing your favorite data grid control. Following the design principles of this sample application, I wanted all UI presentation functionality to reside and execute on the client. This meant moving the data grid data-binding functions to happen client side in JavaScript.

Traditionally data-binding has been performed server-side but I just wanted the server side to simply serialize JSON collections to the client. All this led me to search for a good client side data grid. My research led me to find about twenty different jQuery based data grids and several data grid controls from 3rd party vendors. This all was a bit daunting and overwhelming so I decided to simply code a plain old HMTL table and use KnockoutJS to bind a JSON collection serialized from the server to the table with only a few lines of JavaScript code.

Image 3

Plain Old HTML Data Grid 

The HTML for the Customer Inquiry grid is below. It's basically an HTML table with a header row defined with a thead tag and a tbody tag for presenting the data rows for the Customer Inquiry grid. Adding KnockoutJS data-bind tags to the HTML table will allow KnockoutJS to automatically data bind customer data to the grid for each customer we wish to display. As you can see, you only need to define one row of HTML  with a data-bind tag that includes a foreach attribute. With the foreach attribute, KnockoutJS will automatically generate the HTML for each customer row.
HTML
<table id="CustomerInquiryTable" border="0" class="table" style="width: 100%;">
        <thead>         
            <tr>
                <th style="width: 15%; height: 25px">Last Name</th>
                <th style="width: 15%">First Name</th>
                <th style="width: 35%">Email Address</th>
                <th style="width: 15%">City</th>
                <th style="width: 20%">Payment Type</th>
            </tr>   
        </thead>
        <tbody data-bind="foreach: Customers">
            <tr>
                <td><div data-bind="text: LastName "></div></td>
                <td><div data-bind="text: FirstName "></div></td>
                <td><div data-bind="text: EmailAddress"></div></td>
                <td><div data-bind="text: City"></div></td>
                <td><div data-bind="text: PaymentTypeDescription"></div></td>
            </tr>
        </tbody>
    </table> 

KnockoutJS View Model 

To interact with the Customer Inquiry Data Grid with KnockoutJS, some JavaScript is needed to create and initialized the KnockoutJS view model. To data-bind customer data to the HTML table, a KnockoutJS observable array of customers is defined in the view model. If you want to detect and respond to changes on one object, you’d use KnockoutJS observables. If you want to detect and respond to changes of a collection of things, use an observableArray. This is useful in many scenarios like data grids where you’re displaying or editing multiple values and need repeated sections of UI to appear and disappear as items are added and removed.

JavaScript
$(document).ready(function () {
    InitializeCustomerInquiryViewModel();   
});

function InitializeCustomerInquiryViewModel() {

    customerInquiryViewModel = new CustomerInquiryViewModel();
    ko.applyBindings(customerInquiryViewModel);

}

function CustomerInquiryViewModel() {

    this.Customers = ko.observableArray("");
    this.TotalCustomers = ko.observable();
    this.TotalPages = ko.observable();
    this.PageSize = ko.observable(pageSize);
   
}  

Client Side Data Binding with KnockoutJS, AJAX and JSON 

Traditional ASP.NET data binding has always occurred server side in code behind, especially with ASP.NET Web Forms. To follow the separation of concerns design goals for this project, the data binding for the Customer Inquiry data grid will occur on the client in JavaScript.

The first step is to make an AJAX call to the server by accessing the WebAPI route that will return an HTTP response that contains a JSON collection of customer data. The server will automatically serialize a .NET generic list of customer data to a JSON collection that will be transmitted over HTTP. Binding the customer data to the Customer Inquiry HTML table is as simple as looping through the JSON collection in JavaScript and populating ("pushing") rows into the customer observable array in the KnockoutJS view model.

Upon completion of the for loop, KnockoutJS will do the rest, by automatically rendering the HTML needed to display the customer rows through the data-bind tags in the HTML. In the end, all our data binding is performed client side. I believe client-side data binding in JavaScript promotes the development of cleaner looking code. Following the Separation of Concerns principle, data binding is a presentation layer function and should be performed on the client.
JavaScript
function CustomerInquiry() {
    var jqxhr = $.get("/api/customers/GetCustomers", "", function (response) {
            CustomerInquiryCompleted(response);
        }, "json")
        .fail(function (response) {
            RequestFailed(response);
        }
    );
  
}

function CustomerInquiryCompleted(response)
{     
    customerInquiryViewModel.TotalPages(response.TotalPages);
    customerInquiryViewModel.TotalCustomers(response.TotalRows);

    customerInquiryViewModel.Customers.removeAll();

    for (i = 0; i < response.Customers.length; i++) {
        var customer = response.Customers[i];
        customerInquiryViewModel.Customers.push(customer);
    }
 
}  

Filtering, Paging and Sorting 

To make the Customer Inquiry data grid more robust I added functionality for filtering, paging and sorting the grid. The client will call the GetCustomers WebAPI route and pass in filtering, sorting and paging information. The WebAPI controller action will then call the business layer while also injecting the data access layer into the business layer's constructor.

Data Access Layer Paging 

The filtering and paging and sorting of data is implemented in the customer data access layer. Below is the code that uses LINQ to query customer data against an Entity Framework context object. The Entity Framework paging example uses Dynamic LINQ. To get this to work, I had to download the Dynamic LINQ Library source code from Microsoft and save it in a C# class file called DynamicLibrary.cs and then reference the namespace System.Linq.Dynamic.

The Dynamic LINQ library implements the IQueryable interface to perform it's operations. This was needed because I needed to be able to pass literal string values into LINQ's Lambda expression syntax. The LINQ query below joins the Customer Table with the Payment Types table because we want to display the credit card payment type for each customer. The data layer returns generic lists of an object, so a CustomerInquiry class was needed that contains properties from both the customer table and the Payment Type table. Using the LINQ Skip method allows the query to return a single page of customer data based on a page size and current page number that we want to return for the grid.                  

C#
/// Customer Inquiry     
public List<CustomerInquiry> CustomerInquiry(
string firstName, string lastName, DataGridPagingInformation paging)        
{
    string sortExpression = paging.SortExpression;

    if (paging.SortDirection != string.Empty)
        sortExpression = sortExpression + " " + paging.SortDirection;

    var customerQuery = dbConnection.Customers.AsQueryable();

    if (firstName != null && firstName.Trim().Length > 0)
    {
        customerQuery = customerQuery.Where(c => c.FirstName.StartsWith(firstName));
    }

    if (lastName != null && lastName.Trim().Length > 0)
    {
        customerQuery = customerQuery.Where(c => c.LastName.StartsWith(lastName));
    }

    var customers = 
    from c in customerQuery
    join p in dbConnection.PaymentTypes on c.PaymentTypeID equals p.PaymentTypeID
    select new { c.CustomerID,c.FirstName,c.LastName,c.EmailAddress,c.City,p.Description };

    int numberOfRows = customers.Count();

    customers = customers.OrderBy(sortExpression);

    var customerList = customers.Skip((paging.CurrentPageNumber - 1) 
                     * paging.PageSize).Take(paging.PageSize);

    paging.TotalRows = numberOfRows;
    paging.TotalPages = Utilities.CalculateTotalPages(numberOfRows, paging.PageSize);

    List<CustomerInquiry> customerInquiry = new List<CustomerInquiry>();

    foreach (var customer in customerList)
    {
        CustomerInquiry customerData = new CustomerInquiry();
        customerData.CustomerID = customer.CustomerID;
        customerData.FirstName = customer.FirstName;
        customerData.LastName = customer.LastName;
        customerData.EmailAddress = customer.EmailAddress;
        customerData.City = customer.City;
        customerData.PaymentTypeDescription = customer.Description;                      
        customerInquiry.Add(customerData);
    }

    return customerInquiry;
            
}     

Paging with ADO.NET and Inline T-SQL  

For those who prefer to execute traditional SQL-Server T-SQL statements, the sample Customer Maintenance application contains a data access layer object based on ADO.NET and T-SQL. The below sample code performs the same paging functionality as the LINQ/Entity Framework version. Since both of these data access layers implement the ICustomerDataService interface, you can switch between the two by simply changing the Ninject configuration file without any additional changes to the sample Customer Maintenance application. 

/// Customer Inquiry        
public List<CustomerInquiry> CustomerInquiry(
string firstName, string lastName, DataGridPagingInformation paging)
{
    List<Customer> customers = new List<Customer>();
    string sortExpression = paging.SortExpression;

    int maxRowNumber;
    int minRowNumber;

    minRowNumber = (paging.PageSize * (paging.CurrentPageNumber - 1)) + 1;
    maxRowNumber = paging.PageSize * paging.CurrentPageNumber;

    StringBuilder sqlBuilder = new StringBuilder();
    StringBuilder sqlWhereBuilder = new StringBuilder();

    string sqlWhere = string.Empty;

    if (firstName != null && firstName.Trim().Length > 0)
        sqlWhereBuilder.Append(" c.FirstName LIKE @FirstName AND ");

    if (lastName != null && lastName.Trim().Length > 0)
        sqlWhereBuilder.Append(" c.LastName LIKE @LastName AND ");

    if (sqlWhereBuilder.Length > 0)
        sqlWhere = " WHERE " + 
        sqlWhereBuilder.ToString().Substring(0, sqlWhereBuilder.Length - 4);

    sqlBuilder.Append(" SELECT COUNT(*) as total_records FROM Customers c ");
    sqlBuilder.Append(sqlWhere);
    sqlBuilder.Append(";");
    sqlBuilder.Append(" SELECT * FROM ( ");
    sqlBuilder.Append(" SELECT (ROW_NUMBER() OVER (ORDER BY " + paging.SortExpression + " " +
                        paging.SortDirection + ")) as record_number, ");
    sqlBuilder.Append(" c.*, p.Description as PaymentTypeDescription ");
    sqlBuilder.Append(" FROM Customers c ");
    sqlBuilder.Append(" INNER JOIN PaymentTypes p ON p.PaymentTypeID = c.PaymentTypeID ");
    sqlBuilder.Append(sqlWhere);
    sqlBuilder.Append(" ) Rows ");
    sqlBuilder.Append(" where record_number between " + minRowNumber + " and " + maxRowNumber);

    string sql = sqlBuilder.ToString();

    SqlCommand sqlCommand = new SqlCommand();
    sqlCommand.CommandText = sql;
    sqlCommand.Connection = dbConnection;

    if (firstName != null && firstName.Trim().Length > 0)
    {
        sqlCommand.Parameters.Add("@FirstName", System.Data.SqlDbType.VarChar);
        sqlCommand.Parameters["@FirstName"].Value = firstName + "%";
    }

    if (lastName != null && lastName.Trim().Length > 0)
    {
        sqlCommand.Parameters.Add("@LastName", System.Data.SqlDbType.VarChar);
        sqlCommand.Parameters["@LastName"].Value = lastName + "%";
    }

    SqlDataReader reader = sqlCommand.ExecuteReader();
    reader.Read();

    paging.TotalRows = Convert.ToInt32(reader["Total_Records"]);
    paging.TotalPages = Utilities.CalculateTotalPages(paging.TotalRows, paging.PageSize);

    reader.NextResult();

    List<CustomerInquiry> customerList = new List<CustomerInquiry>();

    while (reader.Read())
    {
        CustomerInquiry customer = new CustomerInquiry();

        DataReader dataReader = new DataReader(reader);
        customer.CustomerID = dataReader.GetGuid("CustomerID");
        customer.FirstName = dataReader.GetString("FirstName");
        customer.LastName = dataReader.GetString("LastName");
        customer.EmailAddress = dataReader.GetString("EmailAddress");
        customer.City = dataReader.GetString("City");
        customer.PaymentTypeDescription = dataReader.GetString("PaymentTypeDescription");

        customerList.Add(customer);

    }

    reader.Close();
    return customerList;

}        

Rolling your own data grid or use a 3rd Party control   

This was all fun and I enjoyed having complete control over the data grid. If you prefer to go with a data grid from a 3rd party vendor such as Infragistics or Telerik, or if you prefer using one of the open-source jQuery grids, make sure the data grid control is not tied to or dependent on any server side references. This will help you maintain your Separation of Concerns (SoC) design pattern.

Conclusion 

Since the first software systems were implemented, it was understood that it was important for them to be modular. One of the most important principles in Software Engineering is the Separation of Concerns (SoC): The idea that a software system must be decomposed into parts that overlap in functionality as little as possible. This article demonstrated various techniques for achieving this principle for web application development. At the end of the day you end up with an architecture that will allow different people to work on individual pieces of the system in isolation, an architecture that facilitates reusability and maintainability of a system and allows new features to be easily added. And perhaps even more importantly, it enables everyone to better understand the system.

Technologies included in this article 

Visual Studio 2013 Express Preview for Web
Microsoft SQL-Server Express 2012
Microsoft .NET Framework 4.5.1
Microsoft Entity Framework 6.0 Beta
Microsoft ASP.NET MVC 5
Twitter Bootstrap
Microsoft WebAPI 2
KnockoutJS
Ninject
NUnit
Block UI
ToastrJS

License

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


Written By
Software Developer Joey Software Solutions
United States United States
Mark Caplin has specialized in Information Technology solutions for the past 30 years. Specializing in full life-cycle development projects for both enterprise-wide systems and Internet/Intranet based solutions.

For the past fifteen years, Mark has specialized in the Microsoft .NET framework using C# as his tool of choice. For the past four years Mark has been implementing Single Page Applications using the Angular platform.

When not coding, Mark enjoys playing tennis, listening to U2 music, watching Miami Dolphins football and watching movies in Blu-Ray technology.

In between all this, his wife of over 25 years, feeds him well with some great home cooked meals.

You can contact Mark at mark.caplin@gmail.com

...

Comments and Discussions

 
Questionawesome Artical how to localize ? Pin
Member 1293849814-Jan-17 2:50
Member 1293849814-Jan-17 2:50 
QuestionMy vote of 5 Pin
Ragavamsi21-Aug-15 2:08
Ragavamsi21-Aug-15 2:08 
GeneralMy Vote of 5 Pin
Chairman15-Apr-15 4:08
Chairman15-Apr-15 4:08 
QuestionI am not able to run this solution Pin
sanjiv pandey7-Jan-15 23:47
sanjiv pandey7-Jan-15 23:47 
GeneralMy vote of 3 Pin
Member 19611497-Jul-14 1:19
Member 19611497-Jul-14 1:19 
GeneralMy vote of 5 Pin
M.Azam Shaikh16-Jun-14 20:35
professionalM.Azam Shaikh16-Jun-14 20:35 
QuestionNice but too Simple Pin
g5temp12-May-14 9:36
g5temp12-May-14 9:36 
QuestionProject Breaks After Update Pin
Member 1068700721-Mar-14 7:37
Member 1068700721-Mar-14 7:37 
QuestionNice Work! Pin
Mr. irshad4-Mar-14 20:32
Mr. irshad4-Mar-14 20:32 
QuestionExcellent Article - Thank you Pin
Walter Lockhart25-Feb-14 10:33
Walter Lockhart25-Feb-14 10:33 
QuestionExcellent job 5+ Stars Pin
Member 165621426-Dec-13 6:36
Member 165621426-Dec-13 6:36 
GeneralVery nice article Pin
AbdullaMohammad25-Dec-13 15:03
AbdullaMohammad25-Dec-13 15:03 
QuestionWow ... another Hard coded database connection Pin
crystal915414-Nov-13 7:32
crystal915414-Nov-13 7:32 
QuestionVery Good Pin
Abhishek-Gupta-13-Nov-13 1:10
professionalAbhishek-Gupta-13-Nov-13 1:10 
GeneralMy vote of 5 Pin
Renju Vinod6-Oct-13 20:16
professionalRenju Vinod6-Oct-13 20:16 
QuestionNice article Pin
Sacha Barber30-Sep-13 6:17
Sacha Barber30-Sep-13 6:17 
GeneralMy Vote of 5 Pin
Shakeel Iqbal26-Sep-13 2:52
Shakeel Iqbal26-Sep-13 2:52 
GeneralMy vote of 5 Pin
Hemant__Sharma24-Sep-13 8:14
Hemant__Sharma24-Sep-13 8:14 
GeneralMy vote of 5 Pin
Oshtri Deka24-Sep-13 1:57
professionalOshtri Deka24-Sep-13 1:57 
GeneralMy vote of 5 Pin
WiseLearner22-Sep-13 5:56
WiseLearner22-Sep-13 5:56 
GeneralReally Nice Work Pin
Joz Filip21-Sep-13 11:06
Joz Filip21-Sep-13 11:06 
GeneralMy vote of 5 Pin
TinTinTiTin19-Sep-13 19:38
TinTinTiTin19-Sep-13 19:38 
Thanks for sharing!!

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

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