Telerik’s HTML5 Kendo UI Grid with Server Side Paging, Sorting & Filtering with MVC3, EF4 & Dynamic LINQ

Update: 06/18/2013 – It is recommended that you follow this post for Kendo UI Grid, Datasource filtering http://blog.longle.net/2013/06/18/mvc-4-web-api-odata-entity-framework-kendo-ui-grid-datasource-with-mvvm/

Update: 05/11/2012 – Added support for querying objects with child properties

Update: 04/24/2012 – Added recursion to the filters processing to support multiple search criterias on the same field while at the same time supporting searches across multiple fields.

I recently did a post Telerik’s HTML5 Kendo UI (Grid, Detail Template, TabStrip) which illustrated how to wire up their HTML5 Grid and handle server side paging. After doing so I quickly found myself needing to wire up the rest of server side bells and whistles e.g. sorting, filtering, etc.. Did some relentless googling and didn’t find any good resources on how to do this with MVC3 and EF4 so hence this blog post for the those of us that are doing just that. Rather than starting from scratch I’ll go ahead and continue where the my last blog left off.

So this first thing we need to do is configure our Kendo UI Grid for to do server side sorting and filtering so that we decompose what the requests pay loads look like coming from the Grid when performing these types of actions on it.

Configuring the Kendo UI Grid:


    $(document).ready(function () {
        var grid = $("#grid").kendoGrid({
            dataSource: {
                type: "json",
                serverPaging: true,
                serverSorting: true,
                serverFiltering: true,
                allowUnsort: true,
                pageSize: 5,
                transport: {
                    read: {
                        url: "Products/GetAll",
                        dataType: "json",
                        type: "POST",
                        contentType: "application/json; charset=utf-8",
                        data: {}
                    },
                    parameterMap: function (options) {
                        return JSON.stringify(options);
                    }
                },
                schema: {
                    model: {
                        fields: {
                            ProductId: { type: "number" },
                            Name: { type: "string" },
                            Status: { type: "string" },
                            Created: { type: "date" }
                        }
                    },
                    data: "Products",
                    total: "TotalCount"
                }
            },
            height: 700,
            sortable: true,
            groupable: true,
            pageable: true,
            filterable: true,
            columns: [
                    { field: "ProductId", title: "ProductId" },
                    { field: "ProductType", title: "ProductType" },
                    { field: "Name", title: "Name" },
                    { field: "Created", title: "Created", format: "{0:MM/dd/yyyy}" }
                ],
            detailTemplate: kendo.template($("#template").html()),
            toolbar: kendo.template($("#toolBarTemplate").html()),
            detailInit: detailInit,
            dataBound: function () {
                this.expandRow(this.tbody.find("tr.k-master-row").first());
            }
        });

        var dropDown = grid.find("#requestType").kendoDropDownList({
            dataTextField: "text",
            dataValueField: "value",
            autoBind: false,
            optionLabel: "All",
            dataSource: [
                    { text: "Electronics", value: "2" },
                    { text: "Machinery", value: "1" }
                ],
            change: function () {
                var value = this.value();
                if (value) {
                    grid.data("kendoGrid").dataSource.filter(
                        { field: "ProductType", operator: "eq", value: parseInt(value) });
                } else {
                    grid.data("kendoGrid").dataSource.filter({});
                }
            }
        });
    });

I’ve highlighted some of the major changes we made to our configuration which include setting up the Grid for server side actions: paging, sorting, filter, unsort and surfacing the filteration capabilities to the UI. Lines 54-72 is for setting up a Grid Toolbar which will contain a Kendo UI DrownDownList so that we can filter the Grid on ProductTypes which we will come back around to later on.

Now that we have the Grid configured for server side processing let’s take a quick look at what’s going down the wire in terms of pay loads for each of these actions so that we can mock up our models for these requests. When loading up IE Developer Tools (hit F12 or Tools > Developer Tools) and clicking on the Network Tab to start capturing network traffic we can see the actual pay load request for each of these actions.

So we can see that the pay load that is coming down the wire when a user performs a filter and sort on the grid is:


{"take":5,"skip":0,"page":1,"pageSize":5,"group":[],"filter":{"filters":[{"field":"ProductType","operator":"eq","value":"3"}],"logic":"and"},"sort":[{"field":"Name","dir":"desc"}]}

From this we can start mocking up our models needed for these types of Grid Actions for our Controller.


namespace MvcApplication3.Models
{
    public class GridFilter
    {
        public string Operator { get; set; }
        public string Field { get; set; }
        public string Value { get; set; }
    }

    public class GridFilters
    {
        public List<GridFilter> Filters { get; set; }
        public string Logic { get; set; }
    }

    public class GridSort
    {
        public string Field { get; set; }
        public string Dir { get; set; }
    }
}

Making changes to our Controller Action

We need to make changes to our existing Action on our Controller to support these new Grid objects that is being posted from our Grid when a user does a server side sort, filter, etc..


    public class ProductsController : Controller
    {
        [HttpPost]
        public JsonResult GetAll(int skip, int take, int page, int pageSize, 
            List<GridSort> sort = null, GridFilters filter = null)
        {
            var myDatabaseContext = new MyDatabaseContext();

            var products = myDatabaseContext.Products.AsQueryable();
            var totalCount = myDatabaseContext.Products.AsQueryable();

            if (filter != null && (filter.Filters != null && filter.Filters.Count > 0))
            {
                string whereClause = null;
                var parameters = new List<object>();
                var filters = filter.Filters;

                for (var i = 0; i < filters.Count; i++)
                {
                    if (i == 0)
                        whereClause += string.Format(" {0}", 
                            BuildWhereClause<Product>(i, filter.Logic, filters[i], 
                            parameters));
                    else
                        whereClause += string.Format(" {0} {1}", 
                            ToLinqOperator(filter.Logic), 
                            BuildWhereClause<Product>(i, filter.Logic, filters[i], 
                            parameters));
                }

                products = products.Where(whereClause, parameters.ToArray());
                totalCount = products.Where(whereClause, parameters.ToArray());
            }

            if (sort != null && sort.Count > 0)
                foreach (var s in sort)
                {
                    s.Field = (s.Field == "ProductType") ? "ProductTypeId" : s.Field;
                    products = products.OrderBy(s.Field + " " + s.Dir);
                }

            products = products.Skip(skip).Take(take);

            List<Product> productList = products.ToList();

            var productViewModels = new List<ProductViewModel.Product>();

            foreach (var p in productList)
            {
                productViewModels.Add(new ProductViewModel.Product
                                            {
                                                Completed = p.Completed.Date,
                                                CompletedBy = p.CompletedBy,
                                                Created = p.Created.Date,
                                                CreatedBy = p.CreatedBy,
                                                Name = p.Name,
                                                ProductId = p.ProductId,
                                                ProductType = p.ProductType.Name,
                                                ProductDetails = p.ProductDetails,
                                                Status = p.Status,
                                                Updated = p.Updated.Date,
                                                UpdatedBy = p.UpdatedBy
                                            });
            }

            return Json(
                new ProductViewModel
                    {
                        Products = productViewModels,
                        TotalCount = totalCount.Count()
                    });
        }

        public static string BuildWhereClause<T>(int index, string logic, 
            GridFilter filter, List<object> parameters)
        {
            var entityType = (typeof(T));
            var property = entityType.GetProperty(filter.Field);

            switch (filter.Operator.ToLower())
            {
                case "eq":
                case "neq":
                case "gte":
                case "gt":
                case "lte":
                case "lt":
                    if (typeof(DateTime).IsAssignableFrom(property.PropertyType))
                    {
                        parameters.Add(DateTime.Parse(filter.Value).Date);
                        return string.Format("EntityFunctions.TruncateTime({0}){1}@{2}", 
                            filter.Field, 
                            ToLinqOperator(filter.Operator), 
                            index);
                    }
                    if (typeof(int).IsAssignableFrom(property.PropertyType))
                    {
                        parameters.Add(int.Parse(filter.Value));
                        return string.Format("{0}{1}@{2}", 
                            filter.Field, 
                            ToLinqOperator(filter.Operator), 
                            index);
                    }
                    parameters.Add(filter.Value);
                    return string.Format("{0}{1}@{2}", 
                        filter.Field, 
                        ToLinqOperator(filter.Operator), 
                        index);
                case "startswith":
                    parameters.Add(filter.Value);
                    return string.Format("{0}.StartsWith(" + "@{1})", 
                        filter.Field, 
                        index);
                case "endswith":
                    parameters.Add(filter.Value);
                    return string.Format("{0}.EndsWith(" + "@{1})", 
                        filter.Field, 
                        index);
                case "contains":
                    parameters.Add(filter.Value);
                    return string.Format("{0}.Contains(" + "@{1})", 
                        filter.Field, 
                        index);
                default:
                    throw new ArgumentException(
                        "This operator is not yet supported for this Grid", 
                        filter.Operator);
            }
        }

        public static string ToLinqOperator(string @operator)
        {
            switch (@operator.ToLower())
            {
                case "eq": return " == ";
                case "neq": return " != ";
                case "gte": return " >= ";
                case "gt": return " > ";
                case "lte": return " <= ";
                case "lt": return " < ";
                case "or": return " || ";
                case "and": return " && ";
                default: return null;
            }
        }

        public JsonResult GetProductDetails(int skip, int take, int page, 
            int pageSize, string group)
        {
            var myDatabaseContext = new MyDatabaseContext();

            var productDetails = myDatabaseContext.ProductDetails
                .OrderBy(p => p.ProducDetailtId);

            return Json(
                new ProductDetailsViewModel
                    {
                        ProductDetails = productDetails.Skip(skip).Take(take),
                        TotalCount = productDetails.Count()
                    },
                JsonRequestBehavior.AllowGet);
        }
    }

Note: Instead of downloading the LINQ Dynamic Query Library, you may want to actually download the sample application for this post because the DynamicQueryable.cs class from the Linq Dynamic Libray has been slightly modified to handle EntityFunctions to support string search actions from our Grid such as Contains, StartsWidth and EndsWith string searches.

A few quick notes in regards to our changes to our Action on our Controller to now support complete server side processing of paging, sorting and filtering.

  • BuildWhereClause<T>(int index, string logic, GridFilter filter, List parameters)
    This helper method will build our our where clauses and predicates so tha we can chain them up and pass them into Dynamic LINQ.

  • ToLinqOperator(string @operator)

    This helper method will convert operators that are sent from our Grid to C# operators that Dynamic LINQ will understand and convert them for us

  • Lines 48-64, here we are iterating through the results to trim off the timestamp off of any properties that are of type datetime, so that when we do any grouping or filtering from the grid the timestamp of these fields are ignored.

                foreach (var p in productList)
                {
                    productViewModels.Add(new ProductViewModel.Product
                                                {
                                                    Completed = p.Completed.Date,
                                                    CompletedBy = p.CompletedBy,
                                                    Created = p.Created.Date,
                                                    CreatedBy = p.CreatedBy,
                                                    Name = p.Name,
                                                    ProductId = p.ProductId,
                                                    ProductType = p.ProductType.Name,
                                                    ProductDetails = p.ProductDetails,
                                                    Status = p.Status,
                                                    Updated = p.Updated.Date,
                                                    UpdatedBy = p.UpdatedBy
                                                });
                }
    
  • Lines 88-103, here we are checking against the type of the column (property) that we are searching against so that we can convert the search criteria to the appropriate type. Currently we are supporting searches against types of string, datetime and int. If you need to add more types simply enhance this section of the implementation.

                        if (typeof(DateTime).IsAssignableFrom(property.PropertyType))
                        {
                            parameters.Add(DateTime.Parse(filter.Value).Date);
                            return string.Format(&quot;EntityFunctions.TruncateTime({0}){1}@{2}&quot;, 
                                filter.Field, 
                                ToLinqOperator(filter.Operator), 
                                index);
                        }
                        if (typeof(int).IsAssignableFrom(property.PropertyType))
                        {
                            parameters.Add(int.Parse(filter.Value));
                            return string.Format(&quot;{0}{1}@{2}&quot;, 
                                filter.Field, 
                                ToLinqOperator(filter.Operator), 
                                index);
                        }
                        parameters.Add(filter.Value);
    
    
  • Lines 109-123, here we are just framing up the different queries for string searches from the Grid. The Grid supports StartsWith, Contains, and EndsWith.

                    case &quot;startswith&quot;:
                        parameters.Add(filter.Value);
                        return string.Format(&quot;{0}.StartsWith(&quot; + &quot;@{1})&quot;, 
                            filter.Field, 
                            index);
                    case &quot;endswith&quot;:
                        parameters.Add(filter.Value);
                        return string.Format(&quot;{0}.EndsWith(&quot; + &quot;@{1})&quot;, 
                            filter.Field, 
                            index);
                    case &quot;contains&quot;:
                        parameters.Add(filter.Value);
                        return string.Format(&quot;{0}.Contains(&quot; + &quot;@{1})&quot;, 
                            filter.Field, 
                            index);
    

    As you can see in the screenshot right below these are the current string search capabilites that the Grid has.

Great, let’s run a few searches from the Grid now.

  • Search on ProductId: 3

  • Search on Created Date: 04/20/2013

  • Search on Created Date >= 04/15/2012 and Name containing “sample product 3”

    Voila! Our controller only returns 3 records which assert our test case and are all of Created date >= 04/20/2012 and all contain the string “sample product 3” in the name.

Update: 04/24/2012

Added recursion to the filters processing to support multiple search criterias on the same field while at the same time supporting searches across multiple fields.

 

        [HttpPost]
        public JsonResult GetAll(int skip, int take, int page, int pageSize, 
            List&lt;GridSort&gt; sort = null, GridFilter filter = null)
        {
            var myDatabaseContext = new MyDatabaseContext();

            var products = myDatabaseContext.Products.AsQueryable();
            var totalCount = myDatabaseContext.Products.AsQueryable();

            if (filter != null &amp;&amp; (filter.Filters != null &amp;&amp; filter.Filters.Count &gt; 0))
            {
                ProcessFilters(filter, ref products);
                totalCount = products;
            }

            if (sort != null &amp;&amp; sort.Count &gt; 0)
                foreach (var s in sort)
                {
                    s.Field = (s.Field == &quot;ProductType&quot;) ? &quot;ProductTypeId&quot; : s.Field;
                    products = products.OrderBy(s.Field + &quot; &quot; + s.Dir);
                }

            products = products.Skip(skip).Take(take);

            List&lt;Product&gt; productList = products.ToList();

            var productViewModels = new List&lt;ProductViewModel.Product&gt;();

            foreach (var p in productList)
            {
                productViewModels.Add(new ProductViewModel.Product
                                            {
                                                Completed = p.Completed.Date,
                                                CompletedBy = p.CompletedBy,
                                                Created = p.Created.Date,
                                                CreatedBy = p.CreatedBy,
                                                Name = p.Name,
                                                ProductId = p.ProductId,
                                                ProductType = p.ProductType.Name,
                                                ProductDetails = p.ProductDetails,
                                                Status = p.Status,
                                                Updated = p.Updated.Date,
                                                UpdatedBy = p.UpdatedBy
                                            });
            }

            return Json(
                new ProductViewModel
                    {
                        Products = productViewModels,
                        TotalCount = totalCount.Count()
                    });
        }

        public static void ProcessFilters(GridFilter filter, ref IQueryable&lt;Product&gt; queryable)
        {
            var whereClause = string.Empty;
            var filters = filter.Filters;
            var parameters = new List&lt;object&gt;();
            for (int i = 0; i &lt; filters.Count; i++)
            {
                var f = filters[i];

                if (f.Filters == null)
                {
                    if (i == 0)
                        whereClause += BuildWhereClause&lt;Product&gt;(f, i, parameters) + &quot; &quot;;
                    if (i != 0)
                        whereClause += ToLinqOperator(filter.Logic) + 
                            BuildWhereClause&lt;Product&gt;(f, i, parameters) + &quot; &quot;;
                    if (i == (filters.Count - 1))
                    {
                        CleanUp(ref whereClause);
                        queryable = queryable.Where(whereClause, parameters.ToArray());
                    }
                }
                else
                    ProcessFilters(f, ref queryable);
            }
        }

Looks like our server side paging, sorting and filteration is golden!

Update: 05/11/2012- Added support for querying objects with child properties


    public static class GridHelper
    {
        public static void ProcessFilters&lt;T&gt;(GridFilter filter, ref IQueryable&lt;T&gt; queryable)
        {
            var whereClause = string.Empty;
            var filters = filter.Filters;
            var parameters = new List&lt;object&gt;();
            for (int i = 0; i &lt; filters.Count; i++)
            {
                var f = filters[i];

                if (f.Filters == null)
                {
                    if (i == 0)
                        whereClause += BuildWherePredicate&lt;T&gt;(f, i, parameters) + &quot; &quot;;
                    if (i != 0)
                        whereClause += ToLinqOperator(filter.Logic) + BuildWherePredicate&lt;T&gt;(f, i, parameters) + &quot; &quot;;
                    if (i == (filters.Count - 1))
                    {
                        TrimWherePredicate(ref whereClause);
                        queryable = queryable.Where(whereClause, parameters.ToArray());
                    }
                }
                else
                    ProcessFilters(f, ref queryable);
            }
        }

        public static string TrimWherePredicate(ref string whereClause)
        {
            switch (whereClause.Trim().Substring(0, 2).ToLower())
            {
                case &quot;&amp;&amp;&quot;:
                    whereClause = whereClause.Trim().Remove(0, 2);
                    break;
                case &quot;||&quot;:
                    whereClause = whereClause.Trim().Remove(0, 2);
                    break;
            }

            return whereClause;
        }

        public static string BuildWherePredicate&lt;T&gt;(GridFilter filter, int index, List&lt;object&gt; parameters)
        {
            var entityType = (typeof(T));
            PropertyInfo property;
            
            if(filter.Field.Contains(&quot;.&quot;))
                property = GetNestedProp&lt;T&gt;(filter.Field);
            else 
                property = entityType.GetProperty(filter.Field);
            
            var parameterIndex = parameters.Count;

            switch (filter.Operator.ToLower())
            {
                case &quot;eq&quot;:
                case &quot;neq&quot;:
                case &quot;gte&quot;:
                case &quot;gt&quot;:
                case &quot;lte&quot;:
                case &quot;lt&quot;:
                    if (typeof(DateTime).IsAssignableFrom(property.PropertyType))
                    {
                        parameters.Add(DateTime.Parse(filter.Value).Date);
                        return string.Format(&quot;EntityFunctions.TruncateTime(&quot; + filter.Field + &quot;)&quot; + ToLinqOperator(filter.Operator) + &quot;@&quot; + parameterIndex);
                    }
                    if (typeof(int).IsAssignableFrom(property.PropertyType))
                    {
                        parameters.Add(int.Parse(filter.Value));
                        return string.Format(filter.Field + ToLinqOperator(filter.Operator) + &quot;@&quot; + parameterIndex);
                    }
                    parameters.Add(filter.Value);
                    return string.Format(filter.Field + ToLinqOperator(filter.Operator) + &quot;@&quot; + parameterIndex);
                case &quot;startswith&quot;:
                    parameters.Add(filter.Value);
                    return filter.Field + &quot;.StartsWith(&quot; + &quot;@&quot; + parameterIndex + &quot;)&quot;;
                case &quot;endswith&quot;:
                    parameters.Add(filter.Value);
                    return filter.Field + &quot;.EndsWith(&quot; + &quot;@&quot; + parameterIndex + &quot;)&quot;;
                case &quot;contains&quot;:
                    parameters.Add(filter.Value);
                    return filter.Field + &quot;.Contains(&quot; + &quot;@&quot; + parameterIndex + &quot;)&quot;;
                default:
                    throw new ArgumentException(&quot;This operator is not yet supported for this Grid&quot;, filter.Operator);
            }
        }

        public static string ToLinqOperator(string @operator)
        {
            switch (@operator.ToLower())
            {
                case &quot;eq&quot;:
                    return &quot; == &quot;;
                case &quot;neq&quot;:
                    return &quot; != &quot;;
                case &quot;gte&quot;:
                    return &quot; &gt;= &quot;;
                case &quot;gt&quot;:
                    return &quot; &gt; &quot;;
                case &quot;lte&quot;:
                    return &quot; &lt;= &quot;;
                case &quot;lt&quot;:
                    return &quot; &lt; &quot;;
                case &quot;or&quot;:
                    return &quot; || &quot;;
                case &quot;and&quot;:
                    return &quot; &amp;&amp; &quot;;
                default:
                    return null;
            }
        }

        public static PropertyInfo GetNestedProp&lt;T&gt;(String name)
        {
            PropertyInfo info = null;
            var type = (typeof(T));
            foreach(var prop in name.Split('.'))
            {
                info = type.GetProperty(prop);
                type = info.PropertyType;
            }
            return info;
        }
    }

Happy Coding…! 🙂

Download sample application: https://skydrive.live.com/redir.aspx?cid=949a1c97c2a17906&resid=949A1C97C2A17906!465&parid=949A1C97C2A17906!361

https://drive.google.com/file/d/0B91gwLeUpEWBU2tnN0dNeVhSTzA/view?usp=sharing

Building a Composite ASP.NET MVC Application with Pluggable Areas from External Projects and Assemblies

Update: 10/25/2012

  1. Resolved rendering issues from a Plugin, when controllers names are not unique
  2. Added support for strongly typed views for Plugin views to support Entity Framework scaffolding and/or model binding
  3. Decommissioned custom Razor View Engine, moving to convention over configuration, approach
  4. Updated (sourcecode) sample application download link with latest code base

I recently did a post on Building a Composite MVC3 Application with Ninject, this post we will achieve the same goals with a much more simplistic approach using MV3 Areas.

This provides separation of concerns and loose coupling, helping you to design and build applications using loosely coupled components that can evolve independently which can be easily and seamlessly integrated into the overall application. These types of applications are known as composite applications.

So the problem is that the MVC3 runtime only scans the current executing assembly for IControllers, classes that implement AreaRegistration (to load and register MVC Areas), etc. The key here is to get the MVC3 runtime to scan other assemblies as well so that we can have external projects/assemblies so that we can scale the application horizontally without creating one large monolithic web project which also serves lends it self well when having multiple teams working in parrallel on the same project.

So let’s start! How do we get the MVC runtime to scan external assemblies as it does witht he current executing assembly of our Web project/assembly?

Open up your AssemblyInfo.cs class and add this attribute at the very end:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Web;
using MvcApplication8.Web;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle(&quot;MvcApplication8.Web&quot;)]
[assembly: AssemblyDescription(&quot;&quot;)]
[assembly: AssemblyConfiguration(&quot;&quot;)]
[assembly: AssemblyCompany(&quot;HP&quot;)]
[assembly: AssemblyProduct(&quot;MvcApplication8.Web&quot;)]
[assembly: AssemblyCopyright(&quot;Copyright © HP 2012&quot;)]
[assembly: AssemblyTrademark(&quot;&quot;)]
[assembly: AssemblyCulture(&quot;&quot;)]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid(&quot;cdb134c0-92a4-4fe4-a7dc-6ea3e4a88bcc&quot;)]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Revision and Build Numbers 
// by using the '*' as shown below:
[assembly: AssemblyVersion(&quot;1.0.0.0&quot;)]
[assembly: AssemblyFileVersion(&quot;1.0.0.0&quot;)]

[assembly: PreApplicationStartMethod(
  typeof(PluginAreaBootstrapper), &quot;Init&quot;)]

Note: This is letting the runtime know that before loading the MVC3 Web project assembly please invoke the “Init” method the class named “PluginAreaBootStrapper” where we will dynamically scan for our MVC3 Plugin assemblies so that the MVC runtime can scan them as well
http://msdn.microsoft.com/en-us/library/system.web.preapplicationstartmethodattribute.aspx.

Let’s take a look at what the PluginAreaBootstrapper.Init() method is doing:

    public class PluginAreaBootstrapper
    {
        public static readonly List&lt;Assembly&gt; PluginAssemblies = new List&lt;Assembly&gt;();

        public static List&lt;string&gt; PluginNames()
        {
            return PluginAssemblies.Select(
                pluginAssembly =&gt; pluginAssembly.GetName().Name)
                .ToList();
        }

        public static void Init()
        {
            var fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, &quot;Areas&quot;);

            foreach (var file in Directory.EnumerateFiles(fullPluginPath, &quot;*Plugin*.dll&quot;))
                PluginAssemblies.Add(Assembly.LoadFile(file));

            PluginAssemblies.ForEach(BuildManager.AddReferencedAssembly);
        }
    }

Note: We are simply scanning the Areas directory in our main MV3 web app for any plugin assemblies that we are dropping the the [Areas] (MvcApplication8.Web/Areas) folder and adding our external Plugin assemblies with BuildManager.AddReferencedAssembly (http://msdn.microsoft.com/en-us/library/system.web.compilation.buildmanager.addreferencedassembly.aspx) before our main(host) MVC3 Web App gets started.

You can setup break points on PluginAreaBootstrapper.Init() and Global.asax.cs.ApplicationStart() and see that the PluginAreaBootstrapper.Init() (where we are loading our external Plugin assemblies) method is invoked before Global.asax.cs.ApplicationStart().

Add a custom Razor View Engine

This is so that our MVC3 application will know where to go (physical location) to retrieve views from our plugins. We are copying each of our plugins to a folder named after the plugin assembly name under the [Areas] folder from our main web app e.g. MvcApplication8.Web/Areas/MvcApplication8.Web.MyPlugin1.


    public class PluginRazorViewEngine : RazorViewEngine
    {
        public PluginRazorViewEngine()
        {
            AreaMasterLocationFormats = new[]
            {
                &quot;~/Areas/{2}/Views/{1}/{0}.cshtml&quot;,
                &quot;~/Areas/{2}/Views/{1}/{0}.vbhtml&quot;,
                &quot;~/Areas/{2}/Views/Shared/{0}.cshtml&quot;,
                &quot;~/Areas/{2}/Views/Shared/{0}.vbhtml&quot;
            };

            AreaPartialViewLocationFormats = new[]
            {
                &quot;~/Areas/{2}/Views/{1}/{0}.cshtml&quot;,
                &quot;~/Areas/{2}/Views/{1}/{0}.vbhtml&quot;,
                &quot;~/Areas/{2}/Views/Shared/{0}.cshtml&quot;,
                &quot;~/Areas/{2}/Views/Shared/{0}.vbhtml&quot;
            };

            var areaViewAndPartialViewLocationFormats = new List&lt;string&gt;
            {
                &quot;~/Areas/{2}/Views/{1}/{0}.cshtml&quot;,
                &quot;~/Areas/{2}/Views/{1}/{0}.vbhtml&quot;,
                &quot;~/Areas/{2}/Views/Shared/{0}.cshtml&quot;,
                &quot;~/Areas/{2}/Views/Shared/{0}.vbhtml&quot;
            };

            var partialViewLocationFormats = new List&lt;string&gt;
            {
                &quot;~/Views/{1}/{0}.cshtml&quot;,
                &quot;~/Views/{1}/{0}.vbhtml&quot;,
                &quot;~/Views/Shared/{0}.cshtml&quot;,
                &quot;~/Views/Shared/{0}.vbhtml&quot;
            };

            var masterLocationFormats = new List&lt;string&gt;
            {
                &quot;~/Views/{1}/{0}.cshtml&quot;,
                &quot;~/Views/{1}/{0}.vbhtml&quot;,
                &quot;~/Views/Shared/{0}.cshtml&quot;,
                &quot;~/Views/Shared/{0}.vbhtml&quot;
            };

            foreach (var plugin in PluginAreaBootstrapper.PluginNames())
            {
                masterLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/{1}/{0}.cshtml&quot;);
                masterLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/{1}/{0}.vbhtml&quot;);
                masterLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/Shared/{1}/{0}.cshtml&quot;);
                masterLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/Shared/{1}/{0}.vbhtml&quot;);

                partialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/{1}/{0}.cshtml&quot;);
                partialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/{1}/{0}.vbhtml&quot;);
                partialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/Shared/{0}.cshtml&quot;);
                partialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/Shared/{0}.vbhtml&quot;);

                areaViewAndPartialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/{1}/{0}.cshtml&quot;);
                areaViewAndPartialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Views/{1}/{0}.vbhtml&quot;);
                areaViewAndPartialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Areas/{2}/Views/{1}/{0}.cshtml&quot;);
                areaViewAndPartialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Areas/{2}/Views/{1}/{0}.vbhtml&quot;);
                areaViewAndPartialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Areas/{2}/Views/Shared/{0}.cshtml&quot;);
                areaViewAndPartialViewLocationFormats.Add(
                    &quot;~/Areas/&quot; + plugin + &quot;/Areas/{2}/Views/Shared/{0}.vbhtml&quot;);
            }

            ViewLocationFormats = partialViewLocationFormats.ToArray();
            MasterLocationFormats = masterLocationFormats.ToArray();
            PartialViewLocationFormats = partialViewLocationFormats.ToArray();
            AreaPartialViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray();
            AreaViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray();
        }
    }

Note: Lines 45-77 is where we iterate through each of our plugin assemblies and register their physical location(s) of their views.

Let’s create our external VS MVC3 Web Project for our Plugin

  1. Add a MVC3 Project to our solution e.g. MVCApplication8.Web.MyPlugin1
  2. Create a controller named MyPlugin1Controller.cs

    #region
    
    using System.Web.Mvc;
    
    #endregion
    
    namespace MvcApplication8.Web.MyPlugin1.Controllers
    {
        public class MyPlugin1Controller : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
        }
    }
  3. Add a View (Index.cshtml)

    
    @{
        ViewBag.Title = &quot;Index&quot;;
        Layout = &quot;~/Views/Shared/_Layout.cshtml&quot;;
    }
    
    &lt;h2&gt;Hello! This is a Razor View from MvcApplication8.Web.MyPlugin1&lt;/h2&gt;
    
    
    
  4. Add some xcopy commands to our Plugin project build post events

    MyPlugin1 post build event

    xcopy “$(ProjectDir)\Views” “$(TargetDir)\MyPlugin1\Views\” /s /i /y
    xcopy “$(ProjectDir)\Areas” “$(TargetDir)\MyPlugin1\Areas\” /s /i /y
    xcopy “$(ProjectDir)\Content” “$(TargetDir)\MyPlugin1\Content\” /s /i /y

    MyPlugin2 post build event

    xcopy “$(ProjectDir)\Views” “$(TargetDir)\MyPlugin2\Views\” /s /i /y
    xcopy “$(ProjectDir)\Areas” “$(TargetDir)\MyPlugin2\Areas\” /s /i /y
    xcopy “$(ProjectDir)\Content” “$(TargetDir)\MyPlugin2\Content\” /s /i /y

    Note: this is to copy our plugin views under the [Areas] folder of our main web app (MvcApplication8.Web), the folder name that the plugins are copied to must match the AreaName being registered by the plugin.

  5. Set the output path for our assemblies to place the compiled assemblies from our plugins under the [Areas] folder in our main host web app e.g. MvcApplication8.Web\Areas.

Now let’s run our application and type in the url prefixed with the controller from our plugin e.g. (http://localhost:48733/MyPlugin1)

Voila.., Our controller and view from an external MVC3 project/assembly loads…!

We’ll go ahead and repeat the steps creating a second plugin just for a sanity check and run the app one more time with the url prefixed with the controller from the second plugin e.g. (http://localhost:48733/MyPlugin2)

Voila.., Our controller and view from our second plugin MVC3 project/assembly loads…!

Quick review on our Solution structure

  1. Our plugin folders that are copied to the [Area] folder of our main MVC3 web app
  2. MyPlugin1 controller from our first plugin
  3. MyPlugin2 controller from our second plugin

Registering Routes from Plugins

In order to Register Routes you have to traditionally do this in the Global.asax.cs RegisterRoutes(RouteCollection routes) method in host web application (MvcApplication.Web). There can only be one Global.asax.cs for any given site. So, how can we register Routes from our Plugins when our host web app and Plugins are now completely decoupled?! Meaning they really have no dependencies and are completely ignorant of eachother (which was our goal to begin with, which was to create a “true” decoupled MVC Pluggable architecture).

This was a great question raised by Basem, so let’s dive into the solution for this. When the MVC runtime starts up our application it scans the current executing assembly as well as our Plugin assemblies thanks to our MvcApplication8.Web.PluginAreaBootstrapper.cs implementation. Meaning it scans for implementations of IController, IDependencyResolver, IControllerFacotry, RazorViewEngines and (whats relevant to our solution at hand) any classes that implement AreaRegistration. If we decompose this implementation you will see that there are two overrides that we must implement:

  • AreaName
  • RegisterArea(AreaRegistration context)

So what we need to do here is add an implementation of AreaRegistration to each of our Plugins.

MvcApplication8.Web.MyPlugin1.MyPlugin1AreaRegistration.cs


namespace MvcApplication8.Web.MyPlugin1
{
    public class MyPlugin1AreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get { return &quot;MyPlugin1&quot;; }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                &quot;MyPlugin1_default&quot;,
                &quot;MyPlugin1/{controller}/{action}/{id}&quot;,
                new {action = &quot;Index&quot;, id = UrlParameter.Optional}
                );
        }
    }
}

MvcApplication8.Web.MyPlugin2.MyPlugin2AreaRegistration.cs


namespace MvcApplication8.Web.MyPlugin2
{
    public class MyPlugin2AreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get { return &quot;MyPlugin2&quot;; }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                &quot;MyPlugin2_default&quot;,
                &quot;MyPlugin2/{controller}/{action}/{id}&quot;,
                new {action = &quot;Index&quot;, id = UrlParameter.Optional}
                );
        }
    }
}

Now if we set up a few break points in our implementations of AreaRegistration we will notice that when the application initially starts up (you may have to close out any Casini instances or restart your site in IIS, to ensure that it is the absolute first time your app is being started) the MVC runtime will scan both of our Plugin assemblies and will read the AreaName and invoke the RegisterArea methods in them. Great, now we can add any Routes from our Plugins into the body of the RegisterArea(AreaRegistrationContext context) methods…!

Happy Coding…! 🙂

Download sample application: https://skydrive.live.com/redir?resid=949A1C97C2A17906!2600&authkey=!ANP30kAEhO6QSfs

Setting Telerik’s HTML5 Kendo UI Grid’s Height to 100% when Binding to Remote Data

Update 06/24/2013 – Telerik, Kendo UI Team (Dimo Dimov) has reached out to me (see block quote below), and has taken the time to provide the best practice on how to handle the 100% height for the Kendo UI Web Grid http://jsfiddle.net/dimodi/SDFsz/, please use this approach vs. the approach in the blog post, thanks for visiting…!

I am Dimo Dimov from the Telerik / Kendo UI team. A client recently provided this page as an example of a 100% high Kendo UI Grid. I reviewed the code and the comments and it seems that a lot of people would benefit from some clarifications how percentage heights work.

First of all, please take a look at the following jsFiddle demo, which is similar to the jsFiddle demo above, but is better and simpler in a couple of ways:

http://jsfiddle.net/dimodi/SDFsz/

The example discussed in this blog post is also similar, but has one drawback – it uses hard-coded “magic numbers” in order to workaround the presumably disobeyed web standard requirement regarding percentage heights. Let me explain this requirement in more detail using the new jsFiddle demo as a starting point.

We have the following DOM tree:

First of all, the rule of thumb is that an element with a percentage height should have a parent with explicit height. The rule applies recursively until an element with a pixel height is reached, or until the HTML element is reached, which should have a height as well. This is because percentage heights are always calculated with regard to the set height of the parent element.

Now let’s get back to our example. The Grid should be 100% high and we set such a style. This implies that the parent element () should have an explicit pixel or percentage height. It has none of those, so we apply a 100% height to it. Now we go up and reach the element. It should also have an explicit height, otherwise the and Grid percentage heights will be ignored. So we apply a 100% height to the element as well. Now we have three nested elements that are 100% high and these heights start working. Since 100% high elements should not have vertical borders, margins or paddings, we remove those, as shown in the jsFiddle demo.

With more complex DOM trees the logic is the same – you start with the given element that needs to expand and go up the tree and apply 100% height styles (or any other values) until an element with a pixel height is reached, or until the element is reached. If you have a sequence of nested 100% high elements, removing just one of those heights will cause all the others to stop working. The most common reason for 100% heights to not work is that there is an element with no height, which breaks the sequence of 100% high nested elements.

One thing more to keep in mind is that 100% high elements cannot have siblings, otherwise they will overflow their parent.

This blog post uses the opposite approach compared to the one I describe – the height is calculated from “the outside to the inside”. The only case, in which such an approach makes sense is when the Grid or some of its parent elements have siblings, so 100% heights cannot be applied that easily. Also, this approach can work with parent elements with missing heights, because it relies on the height of the browser window, but I would not recommend this appoach.

Thank you.

I’ve been somewhat wresting with how to set the KendoUI Grid’s height to 100% so that it could fill a user’s screen as much as possible so that it would only show it’s vertical scrollbar only when absolutely needed e.g. when a user’s actual browser real estate is shorter in height than the grid’s content (rows). Those of us that have wired up the Grid binding to remote data e.g. OAuth, JSON, etc.. have been required to explicitly set the Grid’s height.

I’ve visited the Telerik’s forums and googled, unfortunately no working real solution, hence this blog post.

http://www.kendoui.com/forums/ui/grid/dynamic-grid-height.aspx
http://jsfiddle.net/dimodi/4eNu4/33/

If we don’t set the height, the Grid will render as follows:

Note: No height was set for the Grid configuration and it appears as if the Grid has no records when indeed our service is returning records it’s just the Grid’s content div’s height is 0px because we did not explicity set the height, and if we try to set it to a percent 100%, the Grid does not render at all.


    $(document).ready(function () {
        $(&quot;#grid&quot;).kendoGrid({
            dataSource: {
                type: &quot;json&quot;,
                serverPaging: true,
                pageSize: 5,
                transport: { read: { url: &quot;Products/GetAll&quot;, dataType: &quot;json&quot;} },
                schema: { data: &quot;Products&quot;, total: &quot;TotalCount&quot; }
            },
            pageable: true,
            columns: [
                    { field: &quot;ProductId&quot;, title: &quot;ProductId&quot; },
                    { field: &quot;ProductType&quot;, title: &quot;ProductType&quot; },
                    { field: &quot;Name&quot;, title: &quot;Name&quot; },
                    { field: &quot;Created&quot;, title: &quot;Created&quot; }
                ],
            detailTemplate: kendo.template($(&quot;#template&quot;).html()), detailInit: detailInit,
            dataBound: function () {
                this.expandRow(this.tbody.find(&quot;tr.k-master-row&quot;).first());
            }
        });
    });

If explicity set the height, the Grid will render as follows:

Note: Line 10, where we are explicitly setting the height, however notice that we have plenty of screen real estate under grid, and if we could get the Grid could take advantage of that empty space we wouldn’t need the scrollbar.


    $(document).ready(function () {
        $(&quot;#grid&quot;).kendoGrid({
            dataSource: {
                type: &quot;json&quot;,
                serverPaging: true,
                pageSize: 5,
                transport: { read: { url: &quot;Products/GetAll&quot;, dataType: &quot;json&quot;} },
                schema: { data: &quot;Products&quot;, total: &quot;TotalCount&quot; }
            },
            height: 500,
            pageable: true,
            columns: [
                    { field: &quot;ProductId&quot;, title: &quot;ProductId&quot; },
                    { field: &quot;ProductType&quot;, title: &quot;ProductType&quot; },
                    { field: &quot;Name&quot;, title: &quot;Name&quot; },
                    { field: &quot;Created&quot;, title: &quot;Created&quot; }
                ],
            detailTemplate: kendo.template($(&quot;#template&quot;).html()), detailInit: detailInit,
            dataBound: function () {
                this.expandRow(this.tbody.find(&quot;tr.k-master-row&quot;).first());
            }
        });
    });

So a quick way we can remedy this is to calculate how much vertical space we have and set the maximum height of the Grid ourselves. By taking the Html document height and offsetting it factoring in our layout e.g. header and footer.


    $(document).ready(function () {
        $(&quot;#grid&quot;).kendoGrid({
            dataSource: {
                type: &quot;json&quot;,
                serverPaging: true,
                pageSize: 5,
                transport: { read: { url: &quot;Products/GetAll&quot;, dataType: &quot;json&quot;} },
                schema: { data: &quot;Products&quot;, total: &quot;TotalCount&quot; }
            },
            height: $(document).height() - 350,
            pageable: true,
            columns: [
                    { field: &quot;ProductId&quot;, title: &quot;ProductId&quot; },
                    { field: &quot;ProductType&quot;, title: &quot;ProductType&quot; },
                    { field: &quot;Name&quot;, title: &quot;Name&quot; },
                    { field: &quot;Created&quot;, title: &quot;Created&quot; }
                ],
            detailTemplate: kendo.template($(&quot;#template&quot;).html()), detailInit: detailInit,
            dataBound: function () {
                this.expandRow(this.tbody.find(&quot;tr.k-master-row&quot;).first());
            }
        });
    });

Great! Our Grid now renders and takes up the full vertical real estate within the browser on the intial load

Last Problem, now when we resize the browswer, the Grid is NOT automatically resizing itself

No problem, we will add a method for resizing the grid and wire this up to the browser resize event with jQuery.


  $(document).ready(function () {
        $(&quot;#grid&quot;).kendoGrid({
            dataSource: {
                type: &quot;json&quot;,
                serverPaging: true,
                pageSize: 5,
                transport: { read: { url: &quot;Products/GetAll&quot;, dataType: &quot;json&quot;} },
                schema: { data: &quot;Products&quot;, total: &quot;TotalCount&quot; }
            },
            height: 250,
            pageable: true,
            columns: [
                    { field: &quot;ProductId&quot;, title: &quot;ProductId&quot; },
                    { field: &quot;ProductType&quot;, title: &quot;ProductType&quot; },
                    { field: &quot;Name&quot;, title: &quot;Name&quot; },
                    { field: &quot;Created&quot;, title: &quot;Created&quot; }
                ],
            detailTemplate: kendo.template($(&quot;#template&quot;).html()), detailInit: detailInit,
            dataBound: function () {
                this.expandRow(this.tbody.find(&quot;tr.k-master-row&quot;).first());
            }
        });
    });
    
    function resizeGrid() {
        var gridElement = $(&quot;#grid&quot;);
        var dataArea = gridElement.find(&quot;.k-grid-content&quot;);

        var newGridHeight = $(document).height() - 350;
        var newDataAreaHeight = newGridHeight - 65;

        dataArea.height(newDataAreaHeight);
        gridElement.height(newGridHeight);

        $(&quot;#grid&quot;).data(&quot;kendoGrid&quot;).refresh();
    }
    
    $(window).resize(function () {
        resizeGrid();
    });

Now, when grid first loads

Note: Looks great, Grid occupies all the available vertical real estate it can

When user resizes Grid (maximizes browser vertically)

Note: Still Looks great, Grid automatically resizes itself to occupy the new vertical space within the browser after the resize.

Happy Coding…!

Download sample application:

https://skydrive.live.com/redir.aspx?cid=949a1c97c2a17906&resid=949A1C97C2A17906!375&parid=949A1C97C2A17906!361

Telerik’s HTML5 Kendo UI (Grid, Detail Template, TabStrip) with MVC3 and JSON

Update (4/13/2012): I’ve posted a continuation of this blog which includes server side sorting and filteration complete with solution download – Telerik’s HTML5 Kendo UI Grid with Server Side Paging, Sorting & Filtering with MVC3, EF4 & Dynamic LINQ.

After some relentless googling for for a complete example of using Telerik’s new HTML5 Kendo UI Grid and MVC3 with server-side paging, I realized there isn’t an example anywhere on how to implement this including the Kendo UI documentation on Telerik’s site. Reason being is Telerik is really trying to set a strong message that the Kendo UI suite of controls is not coupled to any backend technology, they are really just pure HTML5 controls, all wired up with jQuery that makes request to your choice of restful like services e.g. oAuth (yes, Kendo UI controls supports oAuth out of the box).

So in short they will run pretty much on any browser with mobile gestures built in. I’ve been working with DevExpress ASP.NET WebForms and MVC controls for the past few years and I must admit that their controls, as well as many other 3rd party server-side control suites including Telerik’s can make your application somewhat bulky.

I think this pure HTML5 and jQuery direction from Telerik is a great idea, giving you the power of nice lean controls without the large footprint and clunkiness that your traditional 3rd party server side controls carry and last but not least let's face it, the timing is perfect, everyone is coding client side apps with jQuery especially when using MVC!

I created an empty MVC3 application and wired everything up in the Home/Index.cshtml view, let’s take a look at what’s all required to get a basic Kendo UI Grid up and running.

Add your Javascript and CSS (optional) references


<link href="@Url.Content("~/Content/KendoUi/kendo.common.css")" rel="stylesheet"/>
<link href="@Url.Content("~/Content/KendoUi/kendo.default.css")" rel="stylesheet"/>
<script src="@Url.Content("~/Scripts/KendoUi/kendo.all.js")" type="text/javascript"></script>

Add your Kendo UI Grid jQuery code to instantiate, configure the Grid


<script language="javascript" type="text/javascript">

    $(document).ready(function () {
        $("#grid").kendoGrid({
            dataSource: {
                type: "json",
                serverPaging: true,
                pageSize: 5,
                transport: { read: { url: "Products/GetAll", dataType: "json"} },
                schema: { data: "Products", total: "TotalCount" }
            },
            height: 400,
            pageable: true,
            columns: [
                    { field: "ProductId", title: "ProductId" },
                    { field: "ProductType", title: "ProductType" },
                    { field: "Name", title: "Name" },
                    { field: "Created", title: "Created" }
                ],
            dataBound: function () {
                this.expandRow(this.tbody.find("tr.k-master-row").first());
            }
        });
    });

Note: the transport property is wired up to our MVC route: controller: ProductsController action: GetAll.

Now, let’s look at what the Grid is sending over the wire when requesting the payload for the Grid to bind by firing up an instance of Fiddler.

So from this we see that the Kendo Grid is making a GET request passing in parameters [take], [skip], [page], [pagesize]. Which now helps us frame up the controller, we now know that we have to provide a controller that accepts a GET request which takes in those parameters in order for us to do nice server side paging, meaning we will only ever return a max of 5 records for the current page you are viewing on the grid instead of returning all rows to the web server or in this case to the client browser and then do our paging processing there, minimizing the payload from our SQL box our web server. With that being said let’s wire up our ProductsController and while we are here, we will go ahead our implement our action for our detail grid which I will go over in just a bit.

Action for the master grid


        public JsonResult GetAll(int skip, int take, int page, int pageSize, string group)
        {
            var myDatabaseContext = new MyDatabaseContext();

            var products = myDatabaseContext.Products
                .OrderBy(p => p.Name)
                .Select(p => new ProductViewModel.Product
                                 {
                                     Completed = p.Completed,
                                     CompletedBy = p.CompletedBy,
                                     CreatedBy = p.CreatedBy,
                                     Name = p.Name,
                                     ProductId = p.ProductId,
                                     ProductType = p.ProductType.Name,
                                     ProductDetails = p.ProductDetails,
                                     Status = p.Status,
                                     Updated = p.Updated,
                                     UpdatedBy = p.UpdatedBy
                                 });

            return Json(
                new ProductViewModel
                    {
                        Products = products.Skip(skip).Take(take),
                        TotalCount = products.Count()
                    },
                JsonRequestBehavior.AllowGet);
        }

Note: I’m using EF 4.3 (which you can get with NuGet, this was chosen because of the super fast prototyping ability you get with EF’s CodeFirst approach, CodeFirst was actually introduced in 4.1, 4.3 has some extra cool Migration features), so if you download the sample app and you’re wondering why there isn’t an *.edmx file, thats why! 🙂

Action for the detail grid


        public JsonResult GetProductDetails(int skip, int take, int page, int pageSize, string group)
        {
            var myDatabaseContext = new MyDatabaseContext();

            var productDetails = myDatabaseContext.ProductDetails
                .OrderBy(p => p.ProducDetailtId);

            return Json(
                new ProductDetailsViewModel
                    {
                        ProductDetails = productDetails.Skip(skip).Take(take),
                        TotalCount = productDetails.Count()
                    },
                JsonRequestBehavior.AllowGet);
        }

Note: Rule of thumb, if you can’t accurately predict how many records will be returned in a service call that returns a collection, always implement pagination! You always want to have the smallest footprint in terms of payload going across the wire, whether it be the payload from your SQL box to your web server, or your web server to your browser or both.

You don’t want:

  • A non-performant app because of large payloads
  • Consumption of all the memory/cpu resources on your web box, for pagination processing on a large collection. Just think if your application is getting thousands of request and your web server is now responsible for pagination processing for all of them.
  • Consumption of all the memory/cpu resources on the client workstation on a large collection for pagination processing

So with that begin said perform pagination processing at the earliest stage, which is on your SQL box, apologize if I’m over emphasizing this, however, I’ve seen this bad practice implemented one to many times.

Notice, the [group] parameter in our action method above (which we are not using at the moment), if you enable server side grouping on the Kendo Grid, it will pass the field (column) that you chose to group on so that you can also perform server side grouping!

Let’s run the application

Great! We have the grid all wired up with server side paging in probably less than 10 minutes, all we had to do was paste in some jQuery configuration code, write an action that returns a list of products with skip and take which is trival with EF4 or pretty much anything that implements IQueryable.

Now let’s revisit wiring up the detail grid which is ProductDetails

Adding a Kendo template for the detail area


<script type="text/x-kendo-template" id="template">
    <br/>    
    <div class="tabstrip">
        <ul>
            <li class="k-state-active">Product Details</li>
        </ul>
    <div>
        <div class="productDetails"></div>
    </div>
    </div>
    <br/>
</script>

This is pretty much the Kendo practice, which is providing a template that the Kendo libraries can interpret and inject into other places, in our case it will read this template and inject it into the Detail area of our grid.

Note: I’m using another Kendo UI control which is the TabStrip control to display a tab,
not neccessary however looks great so I added it to our detail view. 🙂

Add the following (highlighted) to our original jQuery config for our master Grid


    $(document).ready(function () {
        $("#grid").kendoGrid({
            dataSource: {
                type: "json",
                serverPaging: true,
                pageSize: 5,
                transport: { read: { url: "Products/GetAll", dataType: "json"} },
                schema: { data: "Products", total: "TotalCount" }
            },
            height: 400,
            pageable: true,
            columns: [
                    { field: "ProductId", title: "ProductId" },
                    { field: "ProductType", title: "ProductType" },
                    { field: "Name", title: "Name" },
                    { field: "Created", title: "Created" }
                ],
            detailTemplate: kendo.template($("#template").html()), 
            detailInit: detailInit,
            dataBound: function () {
                this.expandRow(this.tbody.find("tr.k-master-row").first());
            }
        });
    });


Note:

  • line 18: specify the Kendo template to use for the detail area
  • line 19: wiring up the detailInit event
  • line 20, 21: wiring up the dataBound event, simply expand the first rown after data is bound for the very first time

Implement the detailInit event


    function detailInit(e) {
        var detailRow = e.detailRow;
        detailRow.find(".tabstrip").kendoTabStrip();

        detailRow.find(".productDetails").kendoGrid({
            dataSource: {
                type: "json",
                serverPaging: true,
                pageSize: 5,
                transport: { read: { url: "Products/GetProductDetails", dataType: "json"} },
                schema: { data: "ProductDetails", total: "TotalCount" }
            },
            height:200,
            columns: [
                { field: "Key", title: "Key" },
                { field: "Value", title: "Value" }
            ]
        });


Note:

  • line 3: inject the Kendo UI TabStrip in the div with Id: tabstrip from our kendo template that we used as our detailTemplate earlier
  • line 10: set service call to controller: ProductsConroller, action: GetProductDetails
  • line 11: map our properties from our entity to properties Kendo Grid, e.g. the Grid expects that your collection is mapped to [data] and that your total record count for that request is mapped to [total]

Notice that in the transport property we are setting the url to the MVC route: controller: Products action: GetProductDetails. Now with that being said we are only getting the precise payload and also retrieving it only on demand or lazy loading if and only if when the user clicks on a particular row to see the details of that row. This is very nice and lean request, not the typical behaivor you get when implementing a traditional ASP.NET WebForms 3rd party control where you have to load and instantiate the entire control tree, rebind almost every parent that leads up to the detail control, so if you haven’t already made the leap to ASP.NET MVC, you better start!

Let’s run the app and see the detail grid in action

There are plenty of resources on the all the configurations you can do on the client side for the Kendo UI Grid, however what I mentioned earier is that there is a lack of any artifacts on how to wire things up on the server side in our case which was MVC3.

Here are a couple of links if your interested in more of the client side configuration including events and API for the Kendo UI Grid.

Happy coding…! 🙂

Building a Composite MVC3 Application with Ninject

Update: 05/04/2012 – Preferred alternative approach without using IoC for Plugins (http://blog.longle.net/2012/03/29/building-a-composite-mvc3-application-with-pluggable-areas/).

So there’s great support in Prism to building composite applications with Silverlight with the notion of Prism modules. They have a nice discovery approach for dynamically discovering modules when loading XAPs and assemblies during runtime for all the different modules your Silverlight app may need. You can load them during start-up or on demand so that you don’t have to download the entire application at one time.

When building an enterprise MVC application, I wanted to borrow some of the ideas and architecture of the Prism extensible plug and play concepts to provide the ability to build modules (areas) of your MVC application outside of your core MVC application.

For example if you had a MVC application and it’s primary role is an E-Commerce site (core site), however you have this new requirement to build a integration point to show customers all their tracking statuses for their orders. Now you have to ramp up a development team as quick as possible so that they can build out this feature, however you now find yourself giving them a full blown training and overview of your core MVC application, full source control of your entire VS Solution set which could have up to 20-30 projects in it.

Now when devs from the team compile it’s taking forever…! Here we can easily see that our MVC app’s footprint is getting to large and somewhat difficult to manage. So here is now the need to break it down into blocks that can be injected into the app during runtime so that development for these different pieces, blocks, or modules can happen in parallel with your core MVC application (loose coupling, modularity and extensibility).

My preference here is to actually use Mef since I have experience with it and the great support for MVC3 and Mef now. However, I’ve been given the opportunity to be engaged on a project that uses Ninject. With this being the situation at hand I wanted to explore and see if we could borrow the some concepts from Prism, and incorporate them using what was out of the box with Ninject. Some of these concepts include separation of concerns and loosely coupled components (modules) that can evolve independently but can be easily and seamlessly integrated into the overall application, which is also known as a composite application.

With a little bit of Googling I stumbled upon this article http://www.thegecko.org/index.php/2010/06/pluggable-mvc-2-0-using-mef-and-strongly-typed-views/ which was pretty much what we were looking for however it just needed to be ported from MVC2 to MVC3, use Ninject instead of Mef and just a tad bit of polishing.

So after a little bit of poking around with Ninject, I quickly found that Ninject also has a notion of Modules, NinjectModules, or classes that implement the INinjectModule interface. You can ask the Ninject Kenerl (container) to scan an assembly and it will scan for all any classes that implement INinjectModule, this can be done directly by implementing the actual interface or inheriting their out of the box NinjectModule abstract class.

So rather than starting from ground zero here, I’m going to continue with the project from my last post http://blog.longle.net/2012/02/15/wrapping-the-ninject-kernel-with-servicelocator/.

Quick breakdown on the VS Solution structure:

  • MvcApplication

    This is our main MVC application which will host all the Plugins

  • MvcApplication.Plugin.Framework

    This is a C# class library where all the plugin infrastructure classes will reside

  • MvcApplicationA.Plugin

    Example PluginA

  • MvcApplicationA.Plugin

    Example PluginB

  • MvcApplicationA.Plugin

    Example PluginB

Let’s take a quick look at some of the classes in the MvcApplication.PluginFramework VS Project.

  • MyController.cs (implements IMyController)

    All controllers from plugins will need to inherit MyController, this provides some meta-data so that we can infer the correct type of a plugin Controller from our IoC that was requested.

  • MyPlugin.cs

    There will be one class in each plugin that must inherit MyPlugin, this class provides meta-data about the plugin, e.g. PluginName, AssemblyName, etc.. It also gives us the overidable Load operation where we can setup our Ninject bindings e.g. Controller and plugin bindings.

  • MyControllerFactory.cs

    We inherit the DefaultControllerFactory and override the GetControllerType method so that when a user is routed to a controller that is in a plugin, we can help the MVC runtime derive what it’s type is by scanning our IoC for registered instances for that a given Controller and return it’s type.

    
        public class MyControllerFactory : DefaultControllerFactory
        {
            protected override Type GetControllerType(RequestContext requestContext, 
                string controllerName)
            {
                var controllerType = base.GetControllerType(requestContext, controllerName);
    
                if (controllerType == null)
                {
                    var controller = ServiceLocator.Current.GetAllInstances<IController>().ToList()
                    .OfType<IMyController>()
                    .SingleOrDefault(c => c.ControllerName == controllerName);
    
                    if (controller != null)
                    {
                        return controller.GetType();
                    }
                }
    
                return controllerType;
            }
        }
    
    
  • MyRazorViewEngine (MyViewEngine.cs)

    Custom RazorViewEngine so that we can properly return Views *.cshtml (Razor) that have been copied to our Plugins directory in our main app (MvcApplication/Plugins).

  • MyWebFormEngine (MyViewEngine.cs)

    Custom WebFormViewEngine so that we can properly return MVC *.aspx (non-Razor) Views that have been copied to our Plugins directory in our main app (MvcApplication/Plugins).

  • MyPluginBootstrapper.cs and yes, I borrowed the name from Prism :p

    • Scans the “MvcApplication/Plugin” directory and loads all of our plugins
    • Register’s any custom routes from our plugins
    • Register’s our MyControllerFactory with the MVC runtime
    • Register’s our MyWebFormViewEngine with the MVC runtime
    • Register’s our MyRazorViewEngine with the MVC runtime
    
        public class MyPluginBootstrapper : NinjectModule
        {
            private const string _pluginPath = "Plugins";
            private readonly string _fullPluginPath;
            private const string _defaultMaster = "Site";
            private const string _defaultRazorMaster = "_Layout";
    
            public MyPluginBootstrapper()
            {
                _fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _pluginPath);
            }
    
            public override void Load()
            {
                var assemblies = new List<Assembly>();
    
                // Discover  Modules in plugin directory (e.g. site/plugins)
                foreach (var file in Directory.EnumerateFiles(_fullPluginPath, "*.Plugin.dll"))
                    assemblies.Add(Assembly.LoadFile(file));
    
                Kernel.Load(assemblies);
    
                //  plugins discovery
                var plugins = new List<IMyPlugin>();
                foreach (IMyPlugin plugin in ServiceLocator.Current.GetAllInstances<IMyPlugin>())
                {
                    plugins.Add(plugin);
                    plugin.RegisterRoutes(RouteTable.Routes);
                }
    
                // Register ControllerFactory with site
                IControllerFactory myControllerFactory = new MyControllerFactory();
                ControllerBuilder.Current.SetControllerFactory(myControllerFactory);
    
                // Setup ViewEngines
                var myWebFormViewEngine = 
                    new MyWebFormViewEngine(_pluginPath, plugins, _defaultMaster);
    
                var myRazorViewEngine = 
                    new MyRazorViewEngine(_pluginPath, plugins, _defaultRazorMaster);
    
                // Register ViewEngines with site
                ViewEngines.Engines.Clear();
                ViewEngines.Engines.Add(myWebFormViewEngine);
                ViewEngines.Engines.Add(myRazorViewEngine);            
            }
        }
    
    

So what’s all required to setup an MVC plugin now?

  1. Create a regular MVC3 app project to be the plugin
  2. Assembly name must match this pattern *.Plugin.dll, yes we are using convention for this.
  3. Must have one class that inherits MyPlugin.cs

    
        public class PluginA : MyPlugin
        {
            public override void Load()
            {
                Bind<IMyPlugin>().To<PluginA>();
                Bind<IController>().To<PluginAController>()
                    .Named(GetControllerName<PluginAController>());
            }
        }
    
    
  4. Setup binding for the plugin e.g. IMyPlugin -> PluginA

    
        public class PluginA : MyPlugin
        {
            public override void Load()
            {
                Bind<IMyPlugin>().To<PluginA>();
                Bind<IController>().To<PluginAController>()
                    .Named(GetControllerName<PluginAController>());
            }
        }
    
    
  5. Setup bindings for any Controllers for the plugin e.g. IController -> PluginAController
    
        public class PluginA : MyPlugin
        {
            public override void Load()
            {
                Bind<IMyPlugin>().To<PluginA>();
                Bind<IController>().To<PluginAController>()
                    .Named(GetControllerName<PluginAController>());
            }
        }
    
    
  6. All plugin Controllers must inherit MyController.cs
    
        public class PluginAController : MyController
        {
            public ActionResult Index()
            {
                return View();
            }
        }
    
    

Wrapping up, we have addressed the following concerns:

  • Loose coupling
  • Separation of concerns
  • Application modularity
  • Building our application from partitioned components
  • IoC & Dependency Injection
  • ServiceLocation with ServiceLocator and/or Ninject’s IKernel
  • Composite pattern

As requirements changed and the project matures, it will be helpful that we can change parts of the application without having these changes cascade throughout the system. Modularizing an application allow you to build application components separately (and loosely coupled) and to change whole parts of your application without affecting the rest of your code base.

Happy coding…! 🙂

Wrapping the Ninject Kernel with ServiceLocator

I was a big fan of using Unity or Mef for IoC in my apps, we used Prism heavily in one of my past projects with Silverlight. Prism offered a IoC with your choice of Unity or Mef for your container as well as very good approach to building a scalable application with the notion of Prism Modules.

I recently came on board to a project extensively using Ninject for our MVC3 application. Ninject has the notion of a Kernel which is pretty much how you access your container of registered or bound instances.

In the event where you would ever change out or switch your IoC implementation or choice of frameworks, a nice wrapper, that wrapped your method of accessing your container would be great. You can in theory switch out your IoC (container) with Mef, Unity, Sprint.NET, AutoFac, Castle Windsor, or any other IoC framework with minimal refactoring (here’s a great list of some popular IoC frameworks from Scott Hanselman). 

With a little research I found that Ninject has a ServiceLocator adapter that you can wire up when you setup your Kernel. With this wired up you can access your Ninject container with the standard Microsoft ServiceLocator, which is also a widely used pattern.

For this demonstration we are just going to use the MVC3 application used from my previous blog: http://blog.longle.net/2012/02/15/inject-ioc-my-mvc3-application-in-less-than-5-minutes/

  1. Right click your references folder and click “Manage NuGet Packages”
  2. Search for ServiceLocator, and install the CommonServiceLocator.NinjectAdapter Package
  3. You should now see the NinjectAdapter reference added
  4. Let’s wire this up now, we will visit the Global.asax.cs file where we setup and initialized our Kernel (container), here’s where we an register the NinjectServiceLocator Adapter with Microsoft.Practices.ServiceLocation.ServiceLocator interface.
    
            protected void Application_Start()
            {
                RegisterMyDependencyResolver();            
                AreaRegistration.RegisterAllAreas();
                RegisterGlobalFilters(GlobalFilters.Filters);
                RegisterRoutes(RouteTable.Routes);
            }
    
            private void RegisterMyDependencyResolver()
            {
                var standardKernel = new StandardKernel();
                standardKernel.Bind<IHelloWorldService>().To<HelloWorldService>();
                ServiceLocator.SetLocatorProvider(() => new NinjectServiceLocator(standardKernel));
                DependencyResolver.SetResolver(new MyDependencyResolver(standardKernel));
            }
    
    
  5. So the first place I wanted to use the ServiceLocator was in our MyDependendencyResolver class, where the MVC runitme routes all request get instances for all your registered or bound interfaces. What happens here is that when the MVC runtime requests for a registered instance of a specific interface and your container does not have one, MVC expects a your IoC container to return a null value and at this point MVC will fallback and attempt to return the default instance.

    For example if the MVC runtime requests for an IControllerFactory and you didn’t setup a binding or registration for this interface the MVC runtime will just return a DefaultControllerFactory in this case. The Ninject Kernel will automatically return null in this case.

    However when using the ServiceLocator interface, there is not a TryGet method like the Ninject Kernel offers when requesting instance, the TryGet method will actually scan the container for any registrations for a given interface and if it doesn’t find one it will return null for you.

    So I have to admit there wasn’t a way to really elegantly handle this. So to mimic this behaivor, which is returning a null when we weren’t able to find any registrations with the ServiceLocator, I had to wrap they request and catch the Microsoft.Practices.ServiceLocation.ActivationException, and if this exception was caught then we would return null so that the MVC runtime could take over and return default instances e.g. when requesting the IControllerFactory, ServiceLocator would return null, and MVC would take over and return the DefaultControllerFactory.

    Here’s the exception before wrapping the request:

    
        public class MyDependencyResolver : IDependencyResolver
        {
            public object GetService(Type serviceType)
            {
                    return ServiceLocator.Current.GetInstance(serviceType);
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                    return ServiceLocator.Current.GetAllInstances(serviceType);
            }
        }
    
    

  6. After wrapping the request to our container and returning null after scanning our container and not finding a registered instance for that interface, in our case the IControllerFactory.
    
        public class MyDependencyResolver : IDependencyResolver
        {
            public object GetService(Type serviceType)
            {
                try
                {
                    return ServiceLocator.Current.GetInstance(serviceType);
                }
                catch (Microsoft.Practices.ServiceLocation.ActivationException ex)
                {
                    return null;
                }
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                try
                {
                    return ServiceLocator.Current.GetAllInstances(serviceType);
                }
                catch (Microsoft.Practices.ServiceLocation.ActivationException ex)
                {
                    return null;
                }
            }
        }
    
    

    Obviously we could create a two extension methods for ServiceLocator maybe TryGet and TryGetAll, but in the interest of time we’ll skip that for now.

    Note, you can always use ServiceLocator everywhere else in your application when requesting for instance(s) for a given interface from your Kernel and leave our former implementation of MyDependencyResolver accessing the Ninject Kenerl directly if you are uncomfortable with wrapping it with the try/catch block to return null, so that the MVC runtime can fallback and return default instances e.g. IControllerFactory -> DefaultControllerFactory.

    Again, not the most elegant way, however when doing a deep dive on the TryGet method from the Ninject Kernel with Reflector I notice that we weren’t to far off from what Ninject was actually doing here.

  7. If you look at the GetValue method that we reflected on (which is what eventually get’s called by the Ninject’s _kernel.TryGet method, notice the return statement ending with the lambda expression SingleOrDefault()
    
    protected virtual object GetValue(Type service, IContext parent)
    {
        Ensure.ArgumentNotNull(service, "service");
        Ensure.ArgumentNotNull(parent, "parent");
        IRequest request = parent.Request.CreateChild(service, parent, this);
        request.IsUnique = true;
        return parent.Kernel.Resolve(request).SingleOrDefault<object>();
    }
    
    

    which returns null if a registration was not found for that interface in our Ninject container.

    Obviously wrapping our request and catching the ActivationException, we are swallowing the exception, which could create some difficulties in debugging true exceptions where things weren’t wired up correctly for Ninject to bind and activate instances to return to us.

  8. Now let’s see do a quick demonstration on using the ServiceLocator pattern other than in our MyDependencyResolver class. Let’s revisit the HomeController where we were orginall injected the IHelloWorldService in the constructor. An alternative way here is we can manually get this injected by requesting it with ServiceLocator.
    Before:
    
        public class HomeController : Controller
        {
            private readonly IHelloWorldService _helloWorldService;
    
            public HomeController(IHelloWorldService helloWorldService)
            {
                _helloWorldService = helloWorldService;
            }
    
            public ActionResult Index()
            {
                ViewBag.Message = _helloWorldService.Hello(@"Welcome to ASP.NET MVC!");
                return View();
            }
    
            public ActionResult About()
            {
                return View();
            }
        }
    
    

    After:

    
        public class HomeController : Controller
        {
            private readonly IHelloWorldService _helloWorldService;
    
            public HomeController()
            {
                _helloWorldService = ServiceLocator.Current.GetInstance<IHelloWorldService>();
            }
    
            public ActionResult Index()
            {
                ViewBag.Message = _helloWorldService.Hello(@"Welcome to ASP.NET MVC!");
                return View();
            }
    
            public ActionResult About()
            {
                return View();
            }
        }
    
    

    Now it’s really preference whether you want the MVC runtime to use our MyDependencyResolver in conjunction with Ninject to automagically figure out what needs to be injected like in our [before] case which was injecting the IHellowWorldService in the constructor of HomeController, or manually requesting it using ServiceLocator.

    Again I don’t see a wrong or right in eithier way, it’s just really preference, I prefer going with our before case where we let MyDependencyResolver and Ninject figure out what, when and where things automagically need to be injected, however I just wanted to demonstrate abstracting Ninject’s Kernel with ServiceLocator if you wanted to have the flexibility of easiliy swapping out our choice of IoC frameworks with mininmal refactoring or code change. 🙂

Download sample application: https://skydrive.live.com/redir.aspx?cid=949a1c97c2a17906&resid=949A1C97C2A17906!371&parid=949A1C97C2A17906!361

Inject (IoC) my MVC3 Application in less than 5 minutes…!

Quickest way to get your MVC3 application up and running with IoC (Inverse of Control) pattern. So let’s start off by first choosing an IoC framework, for this example we will use Ninject, however your more than welcome to choose other IoC frameworks such as Unity, Mef, AutoFac, etc.. I personally prefer Mef, however in this example we will use Ninject so that we can demonstrate how to implement the ServiceLcoator interface to wrap your chose of Ninect container which is accessed by Ninject Kernel, this can come in handy later if you ever decide to switch out your choice of IoC framework.

So let’s start with a fresh new MVC3 application, the first thing on our TODO list is to add Ninject, we are in luck here since we can easily add this with NuGet.

  1. Right click references in your MVC3 project and choose Manage NuGet Packages
  2. Search for “Ninject” in the Search textbox
  3. Your application should be wired up with the Ninject reference added to your project
  4. Add a class named “MyDependencyResolver.cs” anywhere in your MVC3 project, and have it impelment the IDependencyResolver interface. This will allow you to plug pretty much anything into the MVC runtime. The System.Web.Mvc.IDependencyResolver will have two methods that you will need to implement GetService and GetServices.
    
        public class MyDependencyResolver : IDependencyResolver
        {
            public object GetService(Type serviceType)
            {
                throw new NotImplementedException();
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                throw new NotImplementedException();
            }
        }
    
    
  5. Now let’s wire up our Ninject Kernel (container) in this class MyDependencyResolver.cs
    
        public class MyDependencyResolver : IDependencyResolver
        {
            private readonly IKernel _kernel;
    
            public MyDependencyResolver(IKernel kernel)
            {
                _kernel = kernel;
            }
    
            public object GetService(Type serviceType)
            {
                return _kernel.TryGet(serviceType, new IParameter[0]);
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                return _kernel.GetAll(serviceType, new IParameter[0]);
            }
        }
    
    

    We create a constructor that accepts IKernel as a parameter, this is where and how the Ninject Kernel will be injected into this class.

  6. Now what we need to do is register MyDependencyResolver with the MVC runtime so that it knows to use it. So let’s head over to Application_Start() in our Global.asax class to wire this up. Here we add a helper method “RegisterMyDependencyResolver” which we will instantiate a new instance of StandardKernel and register it with the MVC runtime using DependencyResolver.SetResolver method which accepts a parameter of IDependencyResolver which our MyDependencyResolver class implemeneted.
    
            protected void Application_Start()
            {
                RegisterMyDependencyResolver();            
                AreaRegistration.RegisterAllAreas();
                RegisterGlobalFilters(GlobalFilters.Filters);
                RegisterRoutes(RouteTable.Routes);
            }
    
            private void RegisterMyDependencyResolver()
            {
                var standardKernel = new StandardKernel();
                DependencyResolver.SetResolver(new MyDependencyResolver(standardKernel));
            }
    
    
  7. Now if we setup a breakpoint on our GetService method we will see that the MVC runtime will first request an instance of System.Web.Mvc.IControllerFactory, now since we haven’t registered or bound an actual class which implements the IControllerFactory interface, our GetService method will just return null, and the MVC runtime will just default to the default ControllerFactory. Now if we had a custom ControllerFactory and had it registered, this would have returned our own instance.

    As you break on on the GetService method, you will notice that the MVC runtime will also request an instance of IControllerActivator, HomeController, IViewPageActivator, Home_Index_cshtml.

  8. Now let’s demonstrate a simple HelloWold injection example. First we want to program against an interface, IHelloWorld with the a method Hello(string message).

    
        public interface IHelloWorldService
        {
            string Hello(string message);
        }
    
    
    

    Now let’s implement this interface, create a HelloWorldService.cs class and implement this interface.

    
        public class HelloWorldService : IHelloWorldService
        {
            public string Hello(string message)
            {
                return message;
            }
        }
    
    
  9. Now we have to register our concrete implementation HelloWorldService to our IHelloWorldService with our container (Kernel), will do this in our Global.asax where we originally fired up our Kernel.
    
            private void RegisterMyDependencyResolver()
            {
                var standardKernel = new StandardKernel();
                standardKernel.Bind<IHelloWorldService>().To<HelloWorldService>();
                DependencyResolver.SetResolver(new MyDependencyResolver(standardKernel));
            }
    
    
  10. Now if we setup a breakpoint in the constructor of our HomeController we will see that the IHomeController parameter will be hydrated by our GetService method of our MyDependencyResolver.GetService method which we registerd as our DependencyResolver with the MVC runtime, this is DependencyInjection.

Download sample: https://skydrive.live.com/redir.aspx?cid=949a1c97c2a17906&resid=949A1C97C2A17906!370&parid=949A1C97C2A17906!361

Stay tuned for Part 2, where we will wire up the ServiceLocator to wrap how we retrieve registered instances so in the event where we wanted to switch out our IoC framework (e.g. Unity, Mef, AutoFac, StructureMap) we would have very little refactoring and code change.