Friday, May 4, 2012

MVC3, Entity Framework, Code-First with existing Database and few more..


Introduction
In this whitepaper, I will walk you through developing a simple MVC 3 application architecture using Entity Framework’s code first technology. Later hopefully you can evolve this to be an architecture that you can use for small to mid-size web application development.
Try to read this from start to the end and that way you will be able to gradually build the architecture together with me.
Pre-Requisites
Visual Studio 2010
SQL Server 2005/ 2008
Entity Framework
MVC 3
Create the Database
In real world scenario, the Code-First Technique does not encourage you to create a database at all. When you code the models, the system creates the respective database for you. However it does have its own issues, and out of the issue list, the most difficult to live with is, it re-create the whole database every time the DB schema changes. This made you lose the existing data in the database. So I don't want to deal with that hassle, hence, as a work-around we will be taking a different approach. We use the code-first technique, but with an existing database. So as the first step of this unlike it was with regular code first technique, you have to create the database. What you see below is a simple database I have created for me to do this architecture demonstration.
In general when you create a database, the following few best practices are good to follow
Let the composite table have its own primary key.
Use field ‘IsActive’ to avoid physical deletion of records from the database. If not you will ended up losing vital information when records are deleted.
As a habit use the field ‘Name’ to describe the table record. This can be used as the record descriptor when displaying records to the user.
Create a MVC3 Projects
In this demo I will be using ASP.NET MVC3 Web Application, so let’s create a project and name it as “MVC3Demo”. Let’s also make it a ‘Razor’ Internet Application.

I decided to have a separate project for the business logic code. So let's create class library type project with the name ‘MVC3Demo.Bll’.
Examine the ‘Web.Config’ of the MVC Web Project
The ‘Web.Config’ of our Web project has a connection string already defined and if you examine that closely, you will notice that the connection string with the name ‘ApplicationServices’ is by default using the SQL-EXPRESS database, which is installed with the visual studio 2010 itself. This configuration setting will creates a database inside the ‘App_Data’ folder for you. This is where the system store application services, such as membership service (if you create a new user in the MVC sample project, then it uses the member ship service, to be precise ‘AspNetSqlMembershipProvider’, to create those users in auto-created SQL Express database) related records. However we don’t want to use that database, so let us modify it and also add another connection string for our database that store our system's data objects.
The modified web configuration file will look like this..
Now, with this change, we added another entry with the name ‘DemoDbContext’ and you need to use this very same name to name the database context class that we create to communicate with the database via entity-framework. This context class has to be created by inheriting the .net framework system class named ‘DbContext’. This way the system auto-look for a configuration tag with the name of the class that inherits ‘DbContext’ to find out the connection string details. Additionally in our case we decide to keep the application service setting untouched, so that we have the option of even using a separate database for our role and profile management.
Create ‘MemberShip’ Database
You need to use the command named ‘aspnet_regsql’ to newly create the membership database in our data-base server. In order to do that, let’s go to Star >> ‘All Programs’ >> ‘Microsoft Visual Studio 2010’ >> ‘Visual Studio Tools’ >> ‘Visual Studio Command Prompt (2010)’ and type 'aspnet_regsql'. This will drive you through a wizard and let you create the ‘AccessControlDemo’ database in for your database.  
Run the Project and verifies that new users can be registered with the system and you can login to the system with those newly created user.
Install ‘EntityFramework’ Package for your BLL Project
Since our business logic layer is a class library type project, you do not have required reference for it to be the entity-framework back end. You can use the Library Package Manager, if you have it (it installs automatically with MVC 3.0) to install required 'entity-framework dll' for this project. In order to do that, from within your project in Visual Studio 2010, go to Tools > Library Package Manager > Package Manager Console. In the console, after the "PM>" prompt, type “install-package entityframework” and this will install the package and add the ‘EntityFramework’ reference to your WEB project. Use the Installed location and reference that ‘EntityFramework.Dll’ to your BLL project.

Create ‘DemoDbContext’ Class
As stated few times before, this has to be created by inheriting the ‘DbContext’ Class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
namespace MVC3Demo.Bll
{
    public class DemoDbContext : DbContext
    {
    }
}
Once this is done, you have almost complete the design framework for your project. Remainder is all about building your system on top of this established framework.
Create Model Classes
These model classes are pretty much like the model classes that you have in the MVC web project itself. However in this case we must create them to tally with our database tables. There has to have separate model classes for each table and there has to have properties of same type as fields of the tables. This separation will allow me to share my 'Models' and model access logic across many other projects too.
When you develop by this technique, you need to be extra careful when naming models and their respective properties. One misspell record will break the whole system and to make it worst the system generated errors around this region is not a help at all.
So having that in mind, let me creates a class with the name ‘Student’. Use 'Copy' and 'Paste' as much as possible to avoid typos. I use to copy the table records like this and..
Paste them on to my code like this..  

and then modify it to be like this…

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace MVC3Demo.Bll.Models
{
    [Table("Student", Schema = "dbo")]
    public class Student
    {
        [ScaffoldColumn(false)]
        public int StudentId { get; set; }
        [Required]
        [StringLength(50, ErrorMessage="{0} cannot exceed {1} characters")]
        public string Name { get; set; }
        [Required]
        public int Age { get; set; }
        [Required]
        [StringLength(150)]
        public string Address { get; set; }
        [Required]
        [DisplayName("Registered Date")]
        public DateTime RegisteredDate { get; set; }
        [NotMapped]
        public bool SeniorStudent
        {
            get
            {
                DateTime today = DateTime.Now;
                TimeSpan tSpan = today.Subtract(RegisteredDate);
                return (tSpan.TotalDays > 365);
            }
        }
        [Required]
        [DisplayName("Is Active")]
        public bool IsActive { get; set; }
        public string Description { get; set; }
    }
}
Here is an important point, I have used attributes to include validation logics. Not only that you can have respective error messages define and associate them with the property. In Addition to that, you have the option of having new properties that are not in the database define inside the class with a special attribute (see the 'NotMapped' attribute) as well. The property named, ‘SeniorStudent’ is one such record. In that case you need to mark that with ‘NotMapped’ attribute. To find out more about property attributes, please look at link. msdn.
Just by following the same pattern you can create model classes for the other two tables as well, that is for the table with the name ‘Course’ and ‘StudentCourse’.
Educate the Code to Relate Related Classes.
Now, you may wonder how the relationship in-between tables are been defined here. That is pretty easy, you can educate the model to maintain its relations like this..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MVC3Demo.Bll.Models
{
    public class StudentCourse
    {
        public int StudentCourseId { get; set; }
        public int CourseId { get; set; }
        public Course Course { get; set; }
        public int StudentId { get; set; }
        public Student Student { get; set; }
    }
}
Since the 'StudentCourse; has a many-to-one relationship with both Student and Course class. You need to define 'Student' and 'Course' object inside the 'StudentCourse' class. Tip: Look for Id type properties and as a general practice, add a property with the same name right below it.
Go to 'Student' and 'Course' class and add a property of type ICollection to indicate the many side of the one-to-many relationship.
See how the ‘Course’ class looks ..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MVC3Demo.Bll.Models
{
    public class Course
    {
        public int CourseId { get; set; }
        public string Name { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public int Period { get; set; }
        public bool IsWeekEnd { get; set; }
        public bool IsActive { get; set; }
        public ICollection<StudentCourse> StudentCourses { get; set; }
    }
}
Note: We have used this technique to develop few systems in some of the places I worked for and some of these systems are now run in production environments. In development stage, when naming the properties, we always recommended copy & paste of the name as a small typo often leads us to errors that are very difficult to track. However this technique is proven to be good for architecting small to mid-size systems.
Create Controllers
Just compile the ‘Mvc3Demo.Bll’ and add its reference to our main web project. Now compile the web-project, as when not compiled the add controller wizard does not show the modification done in referenced projects. (I believe Microsoft is aggressively working on some of these minor issues that we see in this version of the release). Then, right click on the controller and fill the respective fields to create the ‘Controller’ and the associated ‘Views’ for our models with names 'Student', ‘Course’ and ‘StudentCourse’.
Compile it and see if everything works. There is one more final adjustment needed. If you run this project as is and try to create a new ‘Course’ then you will get "Invalid object name 'dbo.Courses'" error message. This is because the name of the tables in our database is singular but the context is having plural names for the respective lists that directly match with tables. To avoid this you need to modify the 'DemoDbContext' class as given below.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using MVC3Demo.Bll.Models;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace MVC3Demo.Bll
{
    public class DemoDbContext : DbContext
    {
        public DbSet<Student> Students { get; set; }

        public DbSet<course /> Courses { get; set; }

        public DbSet<studentcourse /> StudentCourses { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<pluralizingtablenameconvention />();
        }
    }
}
Go to the ‘Global.asax’ file and modify the register route method to point the system to 'Student' Index page.
Then run the project and see if everything all right..
Let’s now look at the ‘StudentCourse’ create view and see how the required dropdown box are being automatically created..
Now with this, we have completed developing a basic system. However if you look at the three controller classes, you will see that there are duplicate code being written. To improve the overall quality of the code, I decided to add a abstract controller class. That way I can avoid duplicated code, and have a better architecture in place for the system. This way I can allow newly joining developers to easily adjust to the code and write high-quality codes with very little learning curves.
Create an Abstract Controller Class
In the process of creating this class, I thought of doing few more additional refactoring as noted below..
Added a separate folder called Common, and added few classes that you may want to commonly use across any project you do to this folder. This includes exception logging, handling of cookies, handling of web.config's application settings data and a class to have all commonly used strings.
In addition to this, I decided to add two extensions to the same Common folder. I hope that may come handy at latter part of this code. One extension is for ‘Query’ class and another is added for ‘View’ class.
Note: As you develop multiple projects, the classes you find in this folder can easily be grown to a separate project itself. Such projects can be grouped under your company name. As an example if your company name is XYZ then common project can be named as ‘Xyz.Common’ and let all the project your company develop reference this project. In this way you can set companywide standard for areas of which such standards are applicable.
Added a separate folder called ‘Libraries’. This will keep all third party DLLs that are being referenced by this project. This way you can avoid some of the common DLL reference related errors that you get after deploying project in to the production environment.
Added another class called ‘ConfirmationViewModel’ to Web projects 'Model' folder. This is just to demonstrate how to call a shared view for deletion of records. You will see this being used later..
With these refactoring, I manage to remove almost all the duplicated codes from my controller class and implement them in my generic abstract class. Finally, the fully re-furbished ‘CourseController’ class look here.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVC3Demo.Bll.Models;
using MVC3Demo.Bll;

namespace Mvc3Demo.Controllers
{
    public class CourseController : BaseController<Course>
    {
        public CourseController()
            : this(new DemoDbContext())
        { }

        public CourseController(DemoDbContext db)
            : base(db.Courses, db)
        {
        }

        protected override void LoadEditViewBags(Course entity)
        {
        }

        protected override void LoadAddViewBags()
        {
        }

        protected override Type LogPrefix
        {
            get { return this.GetType(); }
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}
The important base controller is given below. A part of this code is taken from one of our internal company project. Hence that has few additional commonly used methods are being implemented. I have no issues even if you all decide to use this code in your projects too..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVC3Demo.Bll;
using System.Data.Entity;
using System.Data;
using MvcContrib.UI.Grid;
using log4net;
using System.Reflection;
using System.Data.Entity.Infrastructure;
using Mvc3Demo.Common;
using Mvc3Demo.Models;

namespace Mvc3Demo.Controllers
{
    public abstract class BaseController<E> : Controller
        where E : class
    {
        protected DbSet<e /> dbEntitySet;
        protected DemoDbContext db;
        protected readonly ILog logManager;
        public BaseController()
        { }
        public BaseController(DbSet<e /> dbSet, DemoDbContext dbContext)
        {
            dbEntitySet = dbSet;
            db = dbContext;
            logManager = LogManager.GetLogger(LogPrefix);
        }

        public BaseController(DemoDbContext dbContext)
        {
            dbEntitySet = null;
            db = dbContext;
            logManager = LogManager.GetLogger(LogPrefix);
        }

        public virtual ViewResult Index()
        {
            return View(dbEntitySet);
        }

        //
        // GET: /entity/Details/5
        /// <summary />
        /// Get record of the entity from the database
        /// and pass it on to the Details 'PartialView'
        /// </summary />
        /// <param name="id" />Primary Key of the record to be searched</param />
        /// <returns /></returns />
        public virtual PartialViewResult Details(int id)
        {
            return PartialView(GetDetailsData(id));
        }

        //
        // GET: /entity/Create
        /// <summary />
        /// Create the empty View of the entity
        /// </summary />
        /// <returns /></returns />
        public virtual ActionResult Create()
        {
            GetCreateData();
            return View();
        }

        //
        // POST: /entity/Create
        /// <summary />
        /// newly create the entity object in the database. calls to the 'GetCreatePostBackData'
        /// and then call to the 'LoadEditViewBags' and fianlly return a 'View'
        /// </summary />
        /// <param name="entity" /></param />
        /// <param name="DoNotRedirect" /></param />
        /// <returns /></returns />
        [HttpPost]
        public virtual ActionResult Create(E entity, bool? DoNotRedirect)
        {
            try
            {
                int i = GetCreatePostBackData(entity, DoNotRedirect);
                if (i < 0)
                {
                    LoadEditViewBags(entity);
                    return View(entity);
                }
                else
                    return Redirect(PortalCookie.Get(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO));
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        // GET: /entity/Delete/5
        /// <summary />
        /// Delete a record via Ajax delete popup
        /// </summary />
        /// <param name="id" /></param />
        /// <returns /></returns />
        public virtual ActionResult Delete(int id)
        {
            try
            {
                PortalCookie.Set(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO, Request.UrlReferrer.ToString());
                ConfirmationViewModel confirm = new ConfirmationViewModel();
                confirm.Id = id;
                confirm.Action = "Delete";
                confirm.Controller = typeof(E).Name;
                confirm.OperationName = "Delete";
                if (Request.IsAjaxRequest())
                    return PartialView("Confirmation", confirm);
                else
                    return View("Confirmation", confirm);
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        //
        // POST: /entity/Delete/5
        /// <summary />
        /// Once a delete is confirmed via the popup it will do the DB operation
        /// and relavent messages added to the view bag
        /// </summary />
        /// <param name="id" /></param />
        /// <returns /></returns />
        [HttpPost, ActionName("Delete")]
        public virtual ActionResult DeleteConfirmed(int id)
        {
            try
            {
                E entity = dbEntitySet.Find(id);
                dbEntitySet.Remove(entity);
                int i = db.SaveChanges();
                this.AddToMessagePlus(i + " record(s) Deleted Successfully!"
                    , MessageTypes.Success);
                return Redirect(PortalCookie.Get(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO));
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        //
        // GET: /entity/Edit/5
        /// <summary />
        /// Update a particular entity.
        /// </summary />
        /// <param name="id" /></param />
        /// <returns /></returns />
        public virtual ActionResult Edit(int id)
        {
            try
            {
                E entity = GetEditData(id);
                return View(entity);
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        //
        // POST: /entity/Edit/5

        [HttpPost]
        public virtual ActionResult Edit(E entity, bool? DoNotRedirect)
        {
            try
            {
                int i = GetEditPostBackData(entity, DoNotRedirect);

                if (i < 0)
                {
                    LoadEditViewBags(entity);
                    return View(entity);
                }
                else
                    return Redirect(PortalCookie.Get(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO));
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        protected int EditEntity(E entity)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    UpdateTrackingData(entity, false);
                    db.Entry(entity).State = EntityState.Modified;
                    int i = db.SaveChanges();
                    return i;
                }
                return -2;
            }
            catch (Exception e)
            {
                logManager.Error("Error in Controller", e);
                return -1;
            }
        }

        protected int CreateEntity(E entity)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    UpdateTrackingData(entity, true);
                    dbEntitySet.Add(entity);
                    int i = db.SaveChanges();
                    return i;
                }
                return -2;
            }
            catch (Exception e)
            {
                this.logManager.Error("Db Related Error Occured", e);
                return -1;
            }
        }

        protected IEnumerable<e /> GetIndexData()
        {
            try
            {
                return UpdateIncludes(dbEntitySet).AsEnumerable();
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        protected E GetDetailsData(int id)
        {
            try
            {
                return dbEntitySet.Find(id);
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        protected void GetCreateData()
        {
            PortalCookie.Set(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO, (Request.UrlReferrer == null)
                ? Request.RawUrl.ToString() : Request.UrlReferrer.ToString());
            LoadAddViewBags();
        }

        protected E GetEditData(int id)
        {
            PortalCookie.Set(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO, Request.UrlReferrer.ToString());
            E entity = dbEntitySet.Find(id);
            LoadEditViewBags(entity);
            return entity;
        }

        /// <summary />
        /// CreateEntity is called and
        /// then relavent message is add to the view bag
        /// </summary />
        /// <param name="entity" /></param />
        /// <param name="DoNotRedirect" /></param />
        /// <returns /></returns />
        protected int GetCreatePostBackData(E entity, bool? DoNotRedirect)
        {
            int i = CreateEntity(entity);
            return AddCreateSatusMessage(DoNotRedirect, i);
        }

        protected int AddCreateSatusMessage(bool? DoNotRedirect, int i)
        {
            if (i == ConstString.ERROR_INT)
                this.AddToMessage("Error: No record(s) Created!", MessageTypes.Error);
            else if (i == ConstString.ERROR_INVALID_OBJECT_INT)
                this.AddToMessage("Warning: Some record(s) yet to be filled!", MessageTypes.Warning);
            else if ((DoNotRedirect.HasValue) && (DoNotRedirect.Value))
                this.AddToMessage(i + " record(s) Created Successfully!", MessageTypes.Success);
            else
                this.AddToMessagePlus(i + " record(s) Created Successfully!", MessageTypes.Success);
            return i;
        }

        /// <summary />
        /// EditEntity is called and
        /// then relavent message is add to the view bag
        /// </summary />
        /// <param name="entity" /></param />
        /// <param name="DoNotRedirect" /></param />
        /// <returns /></returns />
        protected int GetEditPostBackData(E entity, bool? DoNotRedirect)
        {
            int i = EditEntity(entity);
            return AddEditStatusMessage(DoNotRedirect, i);
        }

        protected int AddEditStatusMessage(bool? DoNotRedirect, int i)
        {
            if (i == ConstString.ERROR_INT)
                this.AddToMessage("Error: No record(s) Updated!", MessageTypes.Error);
            else if (i == ConstString.ERROR_INVALID_OBJECT_INT)
                this.AddToMessage("Warning: Some record(s) yet to be filled!", MessageTypes.Warning);
            else if ((DoNotRedirect.HasValue) && (DoNotRedirect.Value))
                this.AddToMessage(i + " record(s) Updated Successfully!", MessageTypes.Success);
            else
                this.AddToMessagePlus(i + " record(s) Updated Successfully!", MessageTypes.Success);
            return i;
        }

        private PagedViewModel<e /> createPageViewModel(GridSortOptions gridSortOptions
            , int? page, string sortColumn)
        {
            ShowCurrentMessage();
            return new PagedViewModel<e />
            {
                ViewData = ViewData,
                Query = dbEntitySet,
                GridSortOptions = gridSortOptions,
                DefaultSortColumn = sortColumn,
                Page = page,
                PageSize = Common.ConstString.PAGE_SIZE,
            };
        }

        private PagedViewModel<e /> createPageViewModel(GridSortOptions gridSortOptions
            , int? page, string sortColumn, DbQuery<e /> dbQuery)
        {
            ShowCurrentMessage();
            return new PagedViewModel<e />
            {
                ViewData = ViewData,
                Query = dbQuery,
                GridSortOptions = gridSortOptions,
                DefaultSortColumn = sortColumn,
                Page = page,
                PageSize = Common.ConstString.PAGE_SIZE,
            };
        }

        private void UpdateTrackingData(E entity, bool isNew)
        {
            this.UpdateTrackingData<e />(entity, isNew);
        }

        /// <summary />
        ///
        /// </summary />
        /// <param name="name" />Name of the view</param />
        /// <param name="value" />Select list to be assigned to a drop down box</param />
        /// <returns />BaseController itself</returns />
        internal BaseController<e /> AddToViewBag(string name, SelectList value)
        {
            ViewData[name] = value;
            return this;
        }

        /// <summary />
        /// If you have any dropdown boxes please use this method to load their list
        /// to respective view bags.
        /// </summary />
        /// <example />
        /// this.AddToViewBag("EmployeeId", new SelectList(db.Employees, "EmployeeId", "EPFNo"))
        /// .AddToViewBag("DowntimeReasonId", new SelectList(db.DowntimeReasons, "DowntimeReasonId", "Name"))
        /// </example />
        protected abstract void LoadAddViewBags();

        protected abstract void LoadEditViewBags(E entity);

        protected virtual IQueryable<e /> UpdateIncludes(DbSet<e /> dbEntitySet)
        {
            return dbEntitySet.AsQueryable<e />();
        }

        protected void UpdateTrackingData<t />(T entity, bool isNew)
        {
            PropertyInfo pInfoMBy = entity.GetType().GetProperty(ConstString.PROPERTY_MODIFY_BY);
            if (pInfoMBy != null)
                pInfoMBy.SetValue(entity, Convert.ChangeType(User.Identity.Name, pInfoMBy.PropertyType), null);
            PropertyInfo pInfoMDate = entity.GetType().GetProperty(ConstString.PROPERTY_MODIFY_DATE);
            if (pInfoMDate != null)
                pInfoMDate.SetValue(entity, Convert.ChangeType(DateTime.Now, pInfoMDate.PropertyType), null);
            PropertyInfo pInfoABy = entity.GetType().GetProperty(ConstString.PROPERTY_ADDED_DATE);
            if (pInfoABy != null)
                pInfoABy.SetValue(entity, Convert.ChangeType(DateTime.Now, pInfoABy.PropertyType), null);
        }

        /// <summary />
        /// Return this.GetType(); This name is used to trace
        /// error locations of the log message write via log4net library.
        /// </summary />
        protected abstract Type LogPrefix
        {
            get;
        }

        public class CommonMessage
        {
            public CommonMessage()
            {
                this.Message = "";
                this.MessageType = MessageTypes.None;
            }

            public CommonMessage(string message, MessageTypes mType)
            {
                this.Message = message;
                this.MessageType = mType;
            }
            public string Message { get; set; }
            public MessageTypes MessageType { get; set; }
        }

        public CommonMessage GetFromMessagePlus()
        {
            string[] tempArray = TempData.GetMessageSummary();
            if (tempArray == null)
                return null;
            if (tempArray.Length > 0)
            {
                string[] mesgs = tempArray[0].Split('|');
                int t;
                if (int.TryParse(mesgs[1], out t))
                    return new CommonMessage(mesgs[0], (MessageTypes)t);
            }
            return null;
        }

        public void AddToMessagePlus(string message, MessageTypes mtype)
        {
            TempData.AddStatusMessage(message + "|" + (int)mtype);
        }

        public void ShowCurrentMessage()
        {
            CommonMessage temp = GetFromMessagePlus();
            if (temp != null)
                AddToMessage(temp.Message, temp.MessageType);
        }

        public void AddToMessage(string message, MessageTypes mtype)
        {
            switch (mtype)
            {
                case MessageTypes.Success:
                    {
                        ViewBag.successbox = message;
                    } break;
                case MessageTypes.Warning:
                    {
                        ViewBag.warningbox = message;
                    } break;
                case MessageTypes.Error:
                    {
                        ViewBag.errormsgbox = message;
                    } break;
            }
        }
    }

    public enum MessageTypes
    {
        Error,
        Warning,
        Success,
        None
    }
}

Make the Grid Sortable
There are many fancy grid views around that can be used with MVC2 razor views. Yes, as you all know that area need lot more improvements too (over to MSFT). In my view point, out of the lot, the jQuery grid is the best but I thought of going with a more basic simpler ‘MvcContrib.Gird’ for this..
You can find out more about how to integrate ‘MVCContrib’ grid here http://mvccontrib.codeplex.com/wikipage?title=Grid
In the process of integrating the grid, I had to remove the ‘Index’ method (Action) implementation from my abstract class. That creates ambiguity between that and the new method that I wanted in the ‘CourseController’ class. So If I am go with ‘MVCContrib’ grid I have two possible design options,

Remove the Index method implementation from the abstract base controller and have that implemented in the specific controller classes.
Have a new view defined do the action implementation for it in the specific controller while preserving the ‘Index’ method of the abstract controller class.

1 comment: