Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Design and Develop a website using ASP.NET MVC 4, EF, Knockoutjs and Bootstrap : Part - 2

0.00/5 (No votes)
13 Jan 2013 5K  
Design a website architecture that must be simple, easily understandable by any web designer using asp.net MVC, EF, Knockoutjs and Bootstrap

Background

Design and Develop a website using ASP.NET MVC 4, EF, Knockoutjs and Bootstrap : Part - 1

In part one of this Article, I have discussed about the benefit of using Separation of Concern in any Architecture and created a User Interface application using ASP.NET MVC 4, Knockout and Bootstrap, without knowing how data will flow.

Nothing that was described in part one should be considered difficult by anyone. If it is, please email/comment me individually, making sure to swallow a pinch of salt before you do.

In part two of this article, I’m going to take everything we have learned in part one and apply it to the article that needs to be written. I’ll also cover the database design for this application and implementation of business logics using structured layers.

Please be sure to read Part one for the introduction details.

How to use code

Download both Zip files and unzip in separate folder:

Application.zip : contains all projects under one solution file. To run this application your Visual Studio setting should be enable for “Allow NuGet to download missing packages during build”. Or else refer to below link:

http://docs.nuget.org/docs/workflows/using-nuget-without-committing-packages

Application_DB.Zip : It contains SQL Script to create database and master table entry for table PhoneType and AddressType.

And finally, modify the connection string under Application.Web project.

Part 2: Create Database Design (SQL Server 2008 r2): For DBA

From database design prospective below are the main functionality which need to be achieved:

  • A contact can have First Name, Last Name and Email.
  • A Contact can have multiple addresses.
  • A contact can have multiple phone numbers.

To achieve the contact manager functionality, below database design has been used.

The relationship between tables are as per below database diagram:

Part 3: Design Logical Layers: For Core Developers

As discussed in previous article our final structure is:

Next we will discuss the overall structure for our application, in terms of the logical group of components into separate layers, which communicates with each other with/without any restrictions and each logic has its own goals. Layers are an architectural style, and it resolves the maintenance and enhancement problems in the long run.

So let us proceed with adding a class library in our solution and name it as Application.Common.

Application.Common :

This is a class library application, with some common functionality and can be used by different logical layers. For e.g. Security, Logging, Tracking, Validation etc. The components defined in this layer can not only reuse by other layers in this solution, but can also be utilize by other applications. To make it easier to change in future, we can use Dependency Injection and abstraction, with minimal change in our application.

For example, In this layer we are going to use, a validator component to validate data entry, and custom Logger to log error or warning.

Below is the screen shot for Solution folder after adding common class library :

Next, we will add a class library in our solution and name it as Application.Core.

Application.Core:

The components of this layer implement the system core functionality and encapsulate all the relevant business logic. Basically, this layer usually contains classes which implement the domain logic within their methods. This layer also defined a Unit of Work contract within the Core Layer so it will be able to comply with the PI principle. The primary goal is to differentiate and clearly separate the behavior of the core domain/business and the infrastructure implementation details such as data access and specific repositories linked to a particular technology such as ORM, or simply data access libraries or even cross-cutting aspects of the architecture. Thus, by isolating the application Core functionality, we will drastically increase the maintainability of our system and we could even replace the lower layers (data access, ORM, and databases) with low impact to the rest of the application.

Next, we will add a class library in our solution and name it as Application.DAL.

Application.DAL:

The responsibility of DAL is to and provides data access and persistence operations against the storage database; maintain multiple sessions and connection with multiple database, etc. The Primary goal here is to wrap the EF Context with an Interface/Contract so we can use it from the Manger and Core Layers with no direct dependencies to EF. The data persistence components provide access to the data hosted within the boundaries of our system (e.g., our main database which is within a specific BOUNDED CONTEXT), and also to the data exposed outside the boundaries of our system, such as Web services of external systems. Therefore, it has components like “Repositories” that provide such functionality to access the data hosted within the boundaries of our system, or “Service Agents” that will consume Web Services exposed by other external back-end systems. In addition, this layer will usually have base classes/components with reusable code for all the repository classes.

Next, we will add a class library in our solution and name it as Application. Repository.

Application.Repository:

This is a Class Library and can be accessible only by Application.Manager. For each main root ENTITY in our domain, we need to create one repository. Basically, Repositories are classes/components that encapsulate the logic required to access the application data sources. Therefore, they centralize common data access functionality so the application can have a better maintainability and de-coupling between technology and logic owned by the “Manager” and “Core” layers.

Next, we will add a class library in our solution and name it as Application. DTO.

Application.DTO:

Again this is a class library, which contains different container classes that exposes properties but no methods and communicate between Presentation Layer (Application.Web) and Service Layer (Application.Manager). A Data Transfer Object is an object that is used to encapsulate data, and send it from one subsystem of an application to another. Here we are going to use DTOs by the Manager layer to transfer data between itself and the UI layer. The main benefit here is that it reduces the amount of data that needs to be sent across the wire in distributed applications. They also make great models in the MVC pattern. We can also use DTO’s to encapsulate parameters for method calls. This can be useful if a method takes more than 4 or 5 parameters.

Next, we will add a class library in our solution and name it as Application. Manager.

Application.Manager :

This is a Class Library and can be accessible only by Application.Web. For each module we need to declare one Manager. The primary responsibilities of Manager are to accept request from UI/Web layer, then communicate with required repositories and manipulate data based on condition then return back the response. This layer is intermediate between UI and Repositories.

Application.Web:

In previous article, we have already implemented this layer using Javascript dummy data. This is independent ASP.NET MVC web application, and contains only User Interface components, like html, **.aspx, cshtml, MVC etc. It can also be any windows application. It communicates with some methods from manager layer, then evaluate results and choose whether to show an error or page1 or page2 etc. etc. This layer use javascript to load a model for the presentation, but the data is processed in the server through an ajax request, so the server only manage the business logic and the javascript manages the presentation logic.

To better understand how layers are communicating with each other, let us recall the initial requirement:

Screen 1: Contact List - View all contacts

1.1 This screen should display all the contacts available in Database.
1.2 User should be able to delete any contact.
1.3 User should able to edit any contact details.
1.4 User should be able to create a new contact.

To populate the grid data, on page load, we call GetAllProfiles() method of ContactController. This method returns all Profiles exist in database as an JSON object, then we bind it with JavaScript object self.Profiles. Below is the code to call GetAllProfiles() from contact.js:

var ProfilesViewModel = function () {
    var self = this;
    var url = "/contact/GetAllProfiles";
    var refresh = function () {
        $.getJSON(url, {}, function (data) {
            self.Profiles(data);
        });
    };

On Remove button click we call DeleteProfile () method of ContactController. This method returns removes that profile from database. Below is the code to call DeleteProfile() from contact.js:

self.removeProfile = function (profile) {
    if (confirm("Are you sure you want to delete this profile?")) {
        var id = profile.ProfileId;
        waitingDialog({});
        $.ajax({
            type: 'DELETE', url: 'Contact/DeleteProfile/' + id,
            success: function () { self.Profiles.remove(profile); },
            error: function (err) {
                var error = JSON.parse(err.responseText);
                $("<div></div>").html(error.Message).dialog({ modal: true, 
                  title: "Error", buttons: { "Ok": 
                  function () { $(this).dialog("close"); } } }).show();
            },
            complete: function () { closeWaitingDialog(); }
        });
    }
};

For both Create New button and Edit link, we only redirect to CreateEdit page with id as 0 for Create new and for edit the profile id of selected row. Below is the code for createProfile and editProfile from contact.js:

self.createProfile = function () {
    window.location.href = '/Contact/CreateEdit/0';
};

self.editProfile = function (profile) {
    window.location.href = '/Contact/CreateEdit/' + profile.ProfileId;
};

Below is the diagram representation of communication between three major layers:

Screen 2: Create New Contact

This screen should display one blank screen to provide functionalities as.

2.1 User should be able to Enter his/her First name, Last Name and Email Address.
2.2 User should able to add any number of Phone numbers by clicking on Add numbers.
2.3 User should able to remove any phone number.
2.4 User should able to add any number of Addresses by clicking on Add new address.
2.5 User should able to remove any address.
2.6 Click on save button should save Contact details in Database and user will return back in Contact List page.
2.7 Click on Back to Profile button should return back the user to Contact List page.

Screen 3: Update Existing Contact

This screen should display screen with selected contact information details.

  • 3.1 User should be able to modify his/her First name, Last Name and Email Address.
  • 3.2 User should able to modify /delete/Add any number of Phone numbers by clicking on Add numbers or delete link.
  • 3.3 User should able to modify /delete/Add any number of Addresses by clicking on Add new address or delete link.
  • 3.4 Click on save button should update Contact details in Database and user will return back in Contact List page.
  • 3.5 Click on Back to Profile button should return back the user to Contact List page.

As discussed in previous implementation, for both “Create new” and “Edit existing” requirement we are using single page as CreateEdit.cshtml, by identifying the URL value for profileId i.e. if profileId in URL is 0, then it is request for creating a new profile, and if it is some value, the request is for edit existing profile. Below id the implementation details:

In any case (Create or Edit), on page load we need to initialize data for PhoneType and AddressType. For that we have one method in ContactController as InitializePageData(). Below is the code in CreateEdit.js to initialize both arrays:

var AddressTypeData;
var PhoneTypeData;
 
$.ajax({
    url: urlContact + '/InitializePageData',
    async: false,
    dataType: 'json',
    success: function (json) {
        AddressTypeData = json.lstAddressTypeDTO;
        PhoneTypeData = json.lstPhoneTypeDTO;
    }
});

Next, For Edit profile we need to get the profile data, for that we have GetProfileById() method in our ContactController. We modify our existing CreateEdit.js code as:

$.ajax({
    url: urlContact + '/GetProfileById/' + profileId,
    async: false,
    dataType: 'json',
    success: function (json) {
        self.profile = ko.observable(new Profile(json));
        self.phoneNumbers = ko.observableArray(ko.utils.arrayMap(json.PhoneDTO, function (phone) {
            return phone;
        }));
        self.addresses = ko.observableArray(ko.utils.arrayMap(json.AddressDTO, function (address) {
            return address;
        }));
    }
});

Finally, For Save data we have two methods in database. If it’s Create new the we will call SaveProfileInformtion() method of ContactController else we will call UpdateProfileInformation() method. We modify our existing CreateEdit.js code as:

$.ajax({
    type: (self.profile().ProfileId > 0 ? 'PUT' : 'POST'),
    cache: false,
    dataType: 'json',
    url: urlContact + (self.profile().ProfileId > 0 ? '/UpdateProfileInformation?id=' + 
      self.profile().ProfileId : '/SaveProfileInformation'),
    data: JSON.stringify(ko.toJS(self.profile())), 
    contentType: 'application/json; charset=utf-8',
    async: false,
    success: function (data) {
        window.location.href = '/contact';
    },
    error: function (err) {
        var err = JSON.parse(err.responseText);
        var errors = "";
        for (var key in err) {
            if (err.hasOwnProperty(key)) {
                errors += key.replace("profile.", "") + " : " + err[key];
            }
        }
        $("<div></div>").html(errors).dialog({ modal: true, 
          title: JSON.parse(err.responseText).Message, buttons: { "Ok": 
          function () { $(this).dialog("close"); } } }).show();
    },
    complete: function () {
    }
});

Conclusion

That's it!!! Hope you enjoy this article. I am not an expert, and also I have not followed the entire industry standard at the time of writing this article. And that, in a nutshell, is about all I know to get started designing ASP.NET MVC 4 application. I hope you enjoyed this tutorial and learned something.

All comment/Vote are more than welcome…. Smile | <img src=, If you have any questions feel free to ask; I will be glad to talk more in the comments. Thanks for your time!

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here