Showing posts with label ASP.NET MVC. Show all posts
Showing posts with label ASP.NET MVC. Show all posts

Sunday, 22 January 2012

ASP.NET MVC-3.0/ JqGrid/ Unit Of Work/ Repository/ EF 4.1 CRUD Application (N Layered)


Introduction:

 In this article, I will demonstrate the design of multilayered web-based application operations using ASP.NET MVC 3.0/ JqGrid/ Unit of Work/ Repository/ EF 4.1 and later give code demonstration of how to perform CRUD operations using it.
I am assuming that you have basic understanding of ASP.NET MVC/JqGrid/Jquery and EF 4.1.


Design:

DAL: Data Access Layer contains repositories, and EF 4.1 generated code, which handles interaction with underlying data store, and Entities.
BLL: Business Process Layer contains classes that incorporate application business rules. Furthermore, unit of work pattern is used in this layer to track all changes that can affect the database.
Web Application: It is an ASP.NET MVC based web application, through which users can interact with the application.


 Project Structure:


Implementation Details:

EF 4.1 Database First Model:


In the MVCJqGridCrud solution, add DAL, BLL and Web project.
Right click on DAL project, select add from the context menu and from the sub-menu select New Item.
Select ADO.NET Entity Data Model.
Click Add.


In follow up wizard, select generate from database.
Click Next.


Note: I used database named as “ProgrammingEFDB1” for this demo which can be found in code folder (downloadable). It has two table contact and address.
The next step of wizard, will give you the options to configure your database connection.
Click on New Connection and select your desired database from connection property pop up. In this case, it is ProgrammingEFDB1.
Check the check box in bottom of the wizard which states “Save entity connection settings in App.Config as”.

Click Next.
In the next step of the wizard select only tables for this demo, and check the checkboxes at the bottom and click finish.

Model1.edmx will be added in DAL project.
Double click on Model1.edmx to open the designer view of the file.
In the designer view right click and from context menu select “Add code generation items”.
Select the ADO.NET DbContext Generator from the wizard to use simplified DbContext API.
There will be two items added to DAL project.
·         Model1.tt
   It contains POCO classes for each entity in your model.
·         Model1.Context.tt
   It derived from DBContext to use for querying and persisting data.



Repository:

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Repository isolates domain objects from details of the database access code.

I used specialized repositories, which derives from generic repository (base) with overridden or additional method. By this approach I can benefit the use of common methods from generic repository and adding/overriding specialized method in specialized repository.
In this demo, I have kept possible common methods like GetById(), Add() in Base Repository. Meanwhile, a more specialized method named as GetContactsPaged() relevant to contact repository, is in Contact Repository.


RepositoryBase.cs (Generic Repository Class):

public abstract class RepositoryBase<T> where T : class
    {
        internal ProgrammingEFDB1Entities _dataContext;
        internal DbSet<T> _dbset;
        public RepositoryBase(ProgrammingEFDB1Entities context)
        {
            this._dataContext = context;
            this._dbset = context.Set<T>();
        }
        public virtual List<T> Get(
            Expression<Func<T, bool>> filter = null,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<T> query = _dbset;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }


        public virtual void Add(T entity)
        {
            _dbset.Add(entity);
        }
        public virtual void Delete(T entity)
        {
            _dbset.Remove(entity);
        }
        public virtual void Delete(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = _dbset.Where<T>(where).AsEnumerable();
            foreach (T obj in objects)
                _dbset.Remove(obj);
        }
        public virtual T GetById(long id)
        {
            return _dbset.Find(id);
        }
        public virtual T GetById(string id)
        {
            return _dbset.Find(id);
        }
        public virtual IEnumerable<T> GetAll()
        {
            return _dbset.ToList();
        }
        public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
        {
            return _dbset.Where(where).ToList();
        }
        public T Get(Expression<Func<T, bool>> where)
        {
            return _dbset.Where(where).FirstOrDefault<T>();
        }
    }

ContactRepository.cs

public class ContactRepository : RepositoryBase<Contact>
    {

        public ContactRepository(ProgrammingEFDB1Entities context)
            : base(context)
        {

        }

        public List<Contact> GetContactPaged(int page, int rows, out int totalCount)
        {

            totalCount = _dataContext.Contacts.Count();
            return _dataContext.Contacts.OrderBy(c => c.ContactID).Skip(page * rows).Take(rows).ToList();
        }

    }

Unit of Work:

A Unit of Work keeps track of everything you do during a business transaction that can affect the database. In my opinion, Unit of Work is somewhat similar to a transaction. It just encapsulates single business operation. In this demo, I make sure that each business operation has a new DBContext. When unitofwork.save() method is called it will commit the changes in current DBContext.


UnitofWork.cs

public class UnitOfWork : IDisposable
    {
        private ProgrammingEFDB1Entities _context;
        public UnitOfWork()
        {
            _context = new ProgrammingEFDB1Entities();
        }
        private ContactRepository _contactRepository;
        public ContactRepository ContactRepository
        {
            get
            {

                if (this._contactRepository == null)
                {
                    this._contactRepository = new ContactRepository(_context);
                }
                return _contactRepository;
            }
        }
        public void Save()
        {
            _context.SaveChanges();
        }

        private bool _disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            this._disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }


Business Logic Layer:

It encapsulates the business logic of the application. In code demo, I have used only a single class in named as ManageContacts.cs for simplicity. It uses Unit of Work pattern for application business operations.


ManageContacts.cs

public class ManageContacts
    {
       
        public IEnumerable<Contact> GetContacts()
        {
            IEnumerable<Contact> contacts;
            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                contacts = unitOfWork.ContactRepository.GetAll();
            }
            return contacts;
        }
      
        public bool AddContact(Contact contact)
        {
            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                unitOfWork.ContactRepository.Add(contact);
                unitOfWork.Save();
            }
            return true;
        }
      
        public bool UpdateContact(Contact contact, int id)
        {
            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                var contactEntity = unitOfWork.ContactRepository.GetById(id);
                contactEntity.Title = contact.Title;
                contactEntity.FirstName = contact.FirstName;
                contactEntity.LastName = contact.LastName;
                contactEntity.ModifiedDate = DateTime.Now;
                unitOfWork.Save();
            }
            return true;
        }
       
        public bool DeleteContact(int id)
        {
            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                Contact contact = unitOfWork.ContactRepository.GetById(id);
                unitOfWork.ContactRepository.Delete(contact);
                unitOfWork.Save();
            }
            return true;
        }

       
        public List<Contact> GetContactPaged(int page, int rows, out int totalCount)
        {
            List<Contact> contacts;
            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                contacts = unitOfWork.ContactRepository.GetContactPaged(page, rows, out totalCount);
            }
            return contacts;
        }
        public Contact GetById(int id)
        {
            Contact contact;
            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                contact = unitOfWork.ContactRepository.GetById(id);
            }
            return contact;
        }

    }


Controller:

ASP.NET MVC controller coordinates between UI and model (data model).  
Following action methods are added in our application.
GridDemoData(): it will return contacts data in json format to UI layer for JqGrid binding, handle user input for paging.
PerformCRUDOperation(): it handles CRUD inputs from user through JqGrid UI.


HomeController.cs

public ActionResult GridDemoData(int page, int rows, string search, string sidx, string sord)
        {

            var manageContacts = new ManageContacts();
            int currentPage = Convert.ToInt32(page) - 1;
            int totalRecords = 0;

            var data = manageContacts.GetContactPaged(currentPage, rows, out totalRecords);

            var totalPages = (int)Math.Ceiling(totalRecords / (float)rows);
            var jsonData = new
            {
                total = totalPages,
                page,
                records = totalRecords,

                rows = (
                           from m in data
                           select new
                           {
                               id = m.ContactID,
                               cell = new object[]
                                                                            {
                                                                                m.Title,
                                                                                m.FirstName,
                                                                                m.LastName
                                                                               
                                                                            }
                           }).ToArray()
            };
            return Json(jsonData, JsonRequestBehavior.AllowGet);

        }
        public ActionResult PerformCRUDAction(Contact contact)
        {
            var manageContacts = new ManageContacts();

            bool result = false;
            switch (Request.Form["oper"])
            {
                case "add":
                    contact.AddDate = DateTime.Now.Date;
                    contact.ModifiedDate = DateTime.Now;
                    result = manageContacts.AddContact(contact);
                    break;
                case "edit":
                    int id = Int32.Parse(Request.Form["id"]);
                    result = manageContacts.UpdateContact(contact,id);
                    break;
                case "del":
                    id = Int32.Parse(Request.Form["id"]);
                    result = manageContacts.DeleteContact(id);
                    break;
                default:
                    break;
            }
            return Json(result);
        }


View ( UI Layer ):

JqGrid Setup Requirements:
Following code is used for JqGrid setup in site.master.

<link href="<%= ResolveUrl("~/Content/themes/flick/ui.jqgrid.css") %>" rel="stylesheet"
        type="text/css" />
    <link href="<%= ResolveUrl("~/Content/Site.css") %>" rel="stylesheet" type="text/css" />
    <link href="<%= ResolveUrl("~/Content/themes/flick/jquery-ui-1.8.17.custom.css") %>"
        rel="stylesheet" type="text/css" />
    <script src="<%: Url.Content("~/Scripts/jquery-1.5.1.min.js") %>" type="text/javascript">
    </script>
    <script src="../../Scripts/jquery-ui-1.8.17.custom.min.js" type="text/javascript"></script>
    <script src="../../Scripts/grid.locale-en.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>

Following HTML code need to be added in index.aspx for JqGrid

<div id="myDiv" style="width: 100%">
        <table id="grid" cellpadding="0" cellspacing="0">
        </table>
        <div id="pager" name="pager" style="text-align: center;">
        </div>
      
    </div>

Below is Javascript code for JqGrid in JqGridCRUD.js.

var lastsel;
$(function () {

    $("#grid").jqGrid({
        colNames: ['Title', 'First Name', 'Last Name'],
        colModel: [
                        { name: 'Title', index: 'Title', sortable: false, align: 'left', width: '200',
                            editable: true, edittype: 'text'

                        },
                        { name: 'FirstName', index: 'FirstName', sortable: false, align: 'left', width: '200',
                            editable: true, edittype: 'text'

                        },
                        { name: 'LastName', index: 'LastName', sortable: false, align: 'left', width: '200',
                            editable: true, edittype: 'text'

                        }

                      ],
        pager: jQuery('#pager'),
        sortname: 'FirstName',
        rowNum: 10,
        rowList: [10, 20, 25],
        sortorder: "",
        height: 225,
        viewrecords: true,
        rownumbers: true,
        caption: 'Contacts',
        imgpath: '/Content/jqGridCss/smoothness/images',
        width: 750,
        url: "/Home/GridDemoData",
        editurl: "/Home/PerformCRUDAction",
        datatype: 'json',
        mtype: 'GET',
        onCellSelect: function (rowid, iCol, aData) {


            if (rowid && rowid !== lastsel) {

                if (lastsel)
                    jQuery('#grid').jqGrid('restoreRow', lastsel);
                jQuery('#grid').jqGrid('editRow', rowid, true);
                lastsel = rowid;
            }
        }
    })
    jQuery("#grid").jqGrid('navGrid', '#pager', { edit: false, add: true, del: true, search: false, refresh: true },
            { closeOnEscape: true, reloadAfterSubmit: true, closeAfterEdit: true, left: 400, top: 300 },
            { closeOnEscape: true, reloadAfterSubmit: true, closeAfterAdd: true, left: 450, top: 300, width: 520 },
            { closeOnEscape: true, reloadAfterSubmit: true, left: 450, top: 300 });

});

UI Screen Shot:


Conclusion:

Find the code in attached folder, and please don’t forget to rate or vote for this article. Furthermore, do add your comments to make it better.

Friday, 21 October 2011

Deploying ASP.NET MVC 2.0 web application on IIS 7.0 in Production Environment



In this article, I am going to explain how we can deploy asp.net mvc 2.0 application on IIS 7.0 in a production environment. Furthermore, I will highlight some configuration which we normally ignore, but they can greatly improve website performance and help us for smooth deployment.

·         System.Web.Mvc
·         System.Web.Routing
·         System.Web.Abstractions
One possible problem could arise if hosting server have both asp.net mvc 2.0 and asp.net mvc 3.0 installed, and you tried to access asp.net mvc 2.0 website. Error details are as below:

Parser Error Message: The type 'System.Web.Mvc.ViewMasterPage' is ambiguous: it could come from assembly 'C:\Windows\assembly\GAC_MSIL\System.Web.Mvc\2.0.0.0__31bf3856ad364e35\System.Web.Mvc.dll' or from assembly 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.Mvc\v4.0_3.0.0.0__31bf3856ad364e35\System.Web.Mvc.dll'. Please specify the assembly explicitly in the type name.

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />

        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="2.0.0.0" />
   
  </dependentAssembly>
    </assemblyBinding>
  </runtime>

The first step in a deployment process is to publish the website on a local drive. Right click on the website in a visual studio solution pane and select publish, then in publish dialog select publish method as File System and target location to any local drive location.








While publishing website don’t leave <compilation debug=”true”/>. The biggest disadvantage of it is that scripts and images downloaded from webresource.axd handler are not cached.
As a result, it will slow the down the web page loading, because with every request from browser, fresh copy of JavaScript files and static resources need to fetch from a server. For more details visit this link (http://weblogs.asp.net/scottgu/archive/2006/04/11/442448.aspx)
Next step is to Copy and move published folder to hosting server. Click on start menu type ‘inetmgr’ in run command and press enter it will open up IIS interface.

Add Application Pool in IIS 7.0

Create the application pool for your application by right clicking in application pool and click on add application pool from context menu



In add application pool dialog select framework version .NET 4.0 name it such as ‘newApplicationPool’









You can change the identity property to either built in account or a custom account according to your requirement.


By default one application pool has one worker process within which asp.net application runs, it is recommended to create a separate application pool for each web application, which has many advantages, for example.

1)      App pool can run with different identities so you can apply restriction this way
2)      You can restart/recycle an app pool without affecting other applications
3)      If one app pool crashes it does not affect other applications
4)      Each app pool has its own address space in memory
On other hands, one application pool can have many applications with similar configuration. In this case, each web application has its own appDomain under which application runs. So if one of the applications, sharing application pool, fails it does not impact the other application sharing this application pool because of appDomain. However, if a worker process crashed, then it will affect all the applications sharing the application pool as stated earlier.


Usually, one application pool has one worker process. Nevertheless, you can create multiple worker process for single application pool, which called a web garden. However, this configuration has its own requirement like you cannot use session state ‘In Proc’ as it going to be shared by processes. You have to change mode to ‘Out proc’ such as state server or ms sql server.

How To create a web garden in IIS 7.0

Select the application pool for which you want to have multiple worker process select the advance settings and enter the ‘maximum number of worker processes’

 

Add New Web Site in IIS 7.0

Now right click on sites folder and select add web site

Next, in add web site dialog enter site name like ‘NewWebSite’ and select the application pool we have created earlier for website, which is ‘NewApplicationPool’. Next enter the physical path of published code in hosting server. Now in binding section, make following entries. Select http for type, left IP address and port fields as default and in a host name, field enters a web site url like ‘www.NewWebApplication.com’


References: