Best Practices For ASP.NET MVC Application

This article will demonstrate the best practices which should be used while working with ASP.NET MVC application. We always try to build MVC applications, using best practices but we usually don't know where we can implement the best things. So, in this demonstration, you will learn about the best practices which should be implemented when creating MVC application.



Isolate Components

ASP.NET MVC application contains several components, like Model, Controller, View, Data Access Logic, Repository Logic, Service Layer etc. So, the best practice is to always isolate with different solutions. It makes life easy when working with different components.



Use View Model

When working with enterprise level applications in ASP.NET MVC, don't work with Entity [Model] directly. We should create ViewModels which strongly binds with Views. ViewModel is a class just like Model class but it contains some extra properties required only for business process.

Note- ViewModel should always be in different folder.



Always Dispose Context Object

Generally in ASP.NET MVC, we prefer to work with Custom DataContext class which inherits DbContext. But when we perform database operations like Create, Read, Update and Delete through this context object, it is necessary to dispose the context object after process.

To do this, create a class which implements IDisposable interface.

  1. public abstract class DefaultDisposable : IDisposable  
  2. {  
  3.         protected readonly NextProgrammingDataContext context;  
  4.   
  5.         private bool _disposed;  
  6.         private readonly object _disposeLock = new object();  
  7.         protected DefaultDisposable()  
  8.         {  
  9.             context = new NextProgrammingDataContext();  
  10.         }  
  11.         public void Dispose()  
  12.         {  
  13.             Dispose(true);  
  14.         }  
  15.         protected virtual void Dispose(bool disposing)  
  16.         {  
  17.             lock (_disposeLock)  
  18.             {  
  19.                 if (!_disposed)  
  20.                 {  
  21.                     if (disposing)  
  22.                     {  
  23.                         context.Dispose();  
  24.                     }  
  25.   
  26.                     _disposed = true;  
  27.                 }  
  28.             }  
  29.         }  
  30. }  
And, inherit this class with your Repository class where context object will be destroyed after completing the process.
  1. public class CategoryRepository: DefaultDisposable, ICategoryRepository  
  2. {  
  3.         public int AddNewCategory(Category categoryEntity)  
  4.         {  
  5.             int categoryId = 0;  
  6.             if (categoryEntity != null)  
  7.             {  
  8.                 if (CheckCategoryExists(categoryEntity.CategoryName) > 0)  
  9.                 {  
  10.                     categoryId = CheckCategoryExists(categoryEntity.CategoryName);  
  11.                 }  
  12.                 else  
  13.                 {  
  14.                     context.Categories.Add(categoryEntity);  
  15.                     context.SaveChanges();  
  16.                     categoryId = categoryEntity.CategoryId;  
  17.                 }                 
  18.             }  
  19.   
  20.             return categoryId;  
  21.         }  
  22. }  
Use Caching through Web.Config

Caching is an important feature when working with Web development. It reduces the load time when our website uses Caching feature. We can implement Caching in ASP.NET MVC using OutputCache attribute. It is part of Action Filter. OutputCacheAttribute class inherits ActionFilterAttribute class.



To implement the Output Cache, just put the OutputCache attribute on Controller or Action level as following line of code showing.
  1. OutputCache(CacheProfile = "MyCache")]  
  2. public ActionResult Index(int? pageNumber)  
  3. {  
  4.    int page = pageNumber ?? 1;  
  5.    return View();  
  6. }  
The above code shows that we are using OutputCache attribute with action level and passing the CacheProfile.
 
So, what CacheProfile is?
 
Basically, it is best practice to implement the Cache through Web.Config file. We can create n numbers of Cache profiles in Web.Config and use it as per the requirement, as shown in the following code.
  1. <system.web>  
  2.     <caching>  
  3.       <outputCacheSettings>  
  4.         <outputCacheProfiles>  
  5.           <add name="MyCache" duration="100000" varyByParam="none" location="Server"/>  
  6.         </outputCacheProfiles>  
  7.       </outputCacheSettings>  
  8.     </caching>  
  9. </system.web>  
Use BaseController

It is recommended to use Base Controller with our Controller and this Base Controller will inherit Controller class directly. It provides isolation space between our Controller [InterviewController] and Controller. Using Base Controller, we can write common logic which could be shared by all Controllers.
  1. public class BaseController : Controller  
  2. {  
  3.        protected virtual LoginPrincipal LoggedUser  
  4.        {  
  5.            get { return HttpContext.User as LoginPrincipal; }  
  6.        }  
  7.   
  8.        protected int GetUserId()  
  9.        {  
  10.            int userId = -1;  
  11.            if (Request.IsAuthenticated)  
  12.            {  
  13.                userId = LoggedUser.UserId;  
  14.            }  
  15.            return userId;  
  16.        }  
  17. }  
And, use it as following.
  1. public class InterviewController : BaseController  
Use Dependency Injection

It is always recommended to use Dependency Injection with MVC application. And on the runtime, inject class dependencies. For more information, refer to my article How to use Dependency Injection with Asp.Net MVC application.
  1. public class InterviewController : BaseController  
  2. {  
  3.         private const int pageSize = 10;  
  4.         private readonly IInterviewPostRepository _interviewPostRepository;  
  5.         private readonly IInterviewCategoryRepository _interviewCategoryRepository;  
  6.         private readonly ICommentsRepository _commentsRepository;  
  7.         private readonly IUserRepository _userRepository;  
  8.   
  9.         public InterviewController(IInterviewPostRepository interviewPostRepository, IInterviewCategoryRepository interviewCategoryRepository,  ICommentsRepository commentsRepository, IUserRepository userRepository)  
  10.         {  
  11.             _interviewPostRepository = interviewPostRepository;  
  12.             _interviewCategoryRepository = interviewCategoryRepository;  
  13.             _commentsRepository = commentsRepository;  
  14.             _userRepository=userRepository;  
  15.         }  
  16.   
  17.         public ActionResult Index(int? pageNumber)  
  18.         {  
  19.           return View();  
  20.         }  
  21.   
  22. }  
There are different ways to use Dependency Injection. We are using Ninject with this demonstration.
  1. public class NinjectBindings : NinjectModule  
  2. {  
  3.        public override void Load()  
  4.        {  
  5.            Bind<IUserRepository>().To<UserRepository>();             
  6.            Bind<ICommentsRepository>().To<CommentsRepository>();             
  7.            Bind<IInterviewCategoryRepository>().To<InterviewCategoryRepository>();  
  8.            Bind<IInterviewPostRepository>().To<InterviewPostRepository>();          
  9.        }  
  10. }  
Strongly Type View

Best practice, when working with View and passing data from Controller, is using data access logic to use Strongly Typed View and which with map with a ViewModel directly.
  1. @model DotnetTutorial.Data.ViewModel.ContactUsViewModel  
  2. @{  
  3.     ViewBag.Title = "Contact Us - DotNet Tutorial";  
  4.     Layout = "~/Views/Shared/_LayoutBase.cshtml";  
  5. }  
  6.   
  7. <div class="col-md-8">  
  8.     <div>  
  9.         <h1 itemprop="name">CONTACT US</h1>  
  10.     </div>  
  11.     <hr />  
  12.      
  13.     <h3>Send us a Message</h3>  
  14.     <br />  
  15.     @if (ViewBag.ContactUsSuccess != null)  
  16.     {  
  17.         <b style="color: green;">  
  18.             <p class="alert alert-success" style="text-align: center">@ViewBag.ContactUsSuccess</p>  
  19.         </b>  
  20.     }  
  21.     @if (ViewBag.ContactUsFailed != null)  
  22.     {  
  23.         <b style="color: red;">  
  24.             <p class="alert" style="text-align: center">@ViewBag.ContactUsFailed </p>  
  25.         </b>  
  26.     }  
  27.     @using (Html.BeginForm("ContactUs""Home", FormMethod.Post, new { enctype = "multipart/form-data" }))  
  28.     {  
  29.         <div class="control-group form-group">  
  30.             <div class="controls">  
  31.                 @Html.LabelFor(m => m.QueryType)  
  32.                 @Html.DropDownListFor(m => m.SelectValue, new SelectList(Model.QueryTypeList, "value""text"), "Select Query Type"new { @class = "form-control" })  
  33.                 @Html.ValidationMessageFor(m => m.SelectValue, ""new { @style = "color:red" })  
  34.             </div>  
  35.         </div>  
  36.         <div class="control-group form-group">  
  37.             <div class="controls">  
  38.                 @Html.LabelFor(m => m.Name)  
  39.                 @Html.TextBoxFor(m => m.Name, new { @class = "form-control" })  
  40.                 @Html.ValidationMessageFor(m => m.Name, ""new { @style = "color:red" })  
  41.             </div>  
  42.         </div>  
  43.         <div class="control-group form-group">  
  44.             <div class="controls">  
  45.                 @Html.LabelFor(m => m.EmailId)  
  46.                 @Html.TextBoxFor(m => m.EmailId, new { @class = "form-control" })  
  47.                 @Html.ValidationMessageFor(m => m.EmailId, ""new { @class = "", @style = "color:red" })  
  48.             </div>  
  49.         </div>  
  50.         <div class="control-group form-group">  
  51.             <div class="controls">  
  52.                 @Html.LabelFor(m => m.ContactNumber)  
  53.                 @Html.TextBoxFor(m => m.ContactNumber, new { @class = "form-control" })  
  54.             </div>  
  55.         </div>            
  56.         <div class="control-group form-group">  
  57.             <div class="controls">  
  58.                 @Html.LabelFor(m => m.QueryContent)  
  59.                 @Html.TextAreaFor(m => m.QueryContent, new { @class = "form-control", rows = "3" })              
  60.                 @Html.ValidationMessageFor(m => m.QueryContent, ""new { @class = "", @style = "color:red" })  
  61.             </div>  
  62.         </div>  
  63.         <input type="submit" value="Send Message" id="btnSubmitQuery" class="btn btn-primary" />  
  64.              
  65.     }  
  66.   
  67. </div>  
Use HTTP Verbs

Always use HTTP verbs suitable to action method such as HttpPut or HttpGet. If you don't define it, it will work as HttpGet, by default.

But when adding something in database using insert operation, we need to use HttpPut.
  1. [HttpPost]  
  2. public ActionResult AddNewPost(MyViewModel model, string hdnTagList)  
Use Bundling and Minification

As per ASP.NET MVC application performance, we should always use Bundling and Minification. Using Bundling and Minification, we can make fewer requests on server and decrease the file size, which will download faster. Minification is achieved to use .min file. We can do Bundling in BundleConfig class, as shown in the following.
  1. public class BundleConfig  
  2. {  
  3.         // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862  
  4.         public static void RegisterBundles(BundleCollection bundles)  
  5.         {  
  6.             bundles.Add(new ScriptBundle("~/bundles/jquery").Include(  
  7.                         "~/Scripts/jquery-{version}.js"));  
  8.   
  9.             // Use the development version of Modernizr to develop with and learn from. Then, when you're  
  10.             // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.  
  11.             bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(  
  12.                         "~/Scripts/modernizr-*"));  
  13.   
  14.             bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(  
  15.                       "~/Scripts/bootstrap.js",  
  16.                       "~/Scripts/respond.js"));  
  17.   
  18.             bundles.Add(new StyleBundle("~/Content/css").Include(  
  19.                       "~/Content/bootstrap.css",  
  20.                       "~/Content/site.css"));  
  21.         }  
  22. }  
Use Area

If we are interested in creating pluggable applications in MVC, then Area is the best option. Area provides separation of components. Using Area, we can create multiple modules in the same application where every module is working individually without affecting the other functionality.

Mostly, Area is used to create Back-end modules, like Admin part in application. With the following image, we can see that we have created three areas - Blog, InterviewAdmin, TutorialAdmin. Every Area component [blog] will contain its own Controller folder, View folder, and Models folder.



Use Request Validation

Always use Request Validation as false [ValidateInput(false)]. By default, it is true and this can cause security issues because if it is true, then using HTML control, we can pass the HTML content to server. So, best practice is to use Request Validation as false.

Enabling request validation in View pages would cause validation to occur after the input has already been processed by the Controller. By default, MVC performs request validation before a Controller processes the input. To change this behavior, apply the ValidateInputAttribute to a Controller or Action.
  1. [ValidateInput(false)]  
  2. [OutputCache(CacheProfile = "MyCache")]  
  3. public ActionResult Index(string url)  
Use Validation through DataAnnotation

Validation is the important part when working with any application. In ASP.NET MVC, we can implement validation on server side using DataAnnotation which is found in usingSystem.ComponentModel.DataAnnotations namespace.

After using this, we don't need to do extra work. Just pass validation as per requirements, like for checking if the control is not empty use [Required] attribute, for max length validation just use [MaxLength] attribute with properties.
  1. public class PostViewModel  
  2. {  
  3.          
  4.         public int PostId { get; set; }  
  5.   
  6.         [Display(Name="Post Title")]  
  7.         [Required(ErrorMessage="Post Title is required")]  
  8.         [RegularExpression(@"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]  
  9.         [StringLength(500,MinimumLength=3, ErrorMessage="Invalid Title Lenght")]  
  10.         public string PostTitle { get; set; }  
  11.   
  12.   
  13.         [MaxLength(Int32.MaxValue, ErrorMessage = "More than max value")]  
  14.         [AllowHtml]          
  15.         [Display(Name = "Short Content")]  
  16.         [Required(ErrorMessage = "Short Content is required")]          
  17.         public string ShortPostContent { get; set; }  
  18.   
  19.         [AllowHtml]  
  20.         [MaxLength(Int32.MaxValue, ErrorMessage = "More than max value")]  
  21.         [Display(Name = "Full Content")]  
  22.         [Required(ErrorMessage = "Full Content is required")]      
  23.         public string FullPostContent { get; set; }  
  24.   
  25.         [Display(Name = "Meta keywords")]  
  26.         [Required(ErrorMessage = "Meta Keywords is required")]      
  27.         public string MetaKeywords { get; set; }  
  28.   
  29.         [Display(Name = "Meta Description")]          
  30.         public string MetaDescription { get; set; }  
  31. }  
Use Only Single View Engine

In MVC, by default there are two engines available - ASPX engine and Razor engine. But, it prefers to use only one engine for increasing the performance of the application. So, first we need to clear ViewEngines when initializing the application, and then bind our preferable engine with the application.
  1. ViewEngines.Engines.Clear();  
  2. ViewEngines.Engines.Add(new RazorViewEngine());  
Conclusion

So, today we have learned the best practices while working with ASP.NET MVC applications.

I hope this post will help you. Please put your feedback using comment which helps me improve myself for the next post. Please put your doubts and queries in the comment section.