pavanarya

Let us share the knowledge

Why To Make Use Of Properties Inside our Model Class When we want to display it as a grid using WebGrid() in MVC3

with one comment

Hi,
Previous when i am working on WebGrid() class which is used to render a Model class data to a grid i came across an interesting fact.
Please check my post Displaying data in a grid using WebGrid in cshtml before reading this if you are new to WebGrid class in MVC3

Problem statement:

I am getting below exception when working with grid. Everything seems to be good but there is one minor reason

Column "firstname" does not exist.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: Column "firstname" does not exist.

Source Error: 


Line 14:     
Line 15: }
Line 16: @grid.GetHtml()
Line 17:              
Line 18:     </div>

Source File: f:\My Code\MvcApplication2\MvcApplication2\Views\Shared\myFirstView.cshtml    Line: 16 

First of all let me show my Model class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;

namespace MvcApplication2.Models
{
    public class MyModel
    {
        public string firstname;
        public string lastname;
        public string AddressCity ;
        public string FullName ;
        public string nicknameFirstNmae ;
        public string profession ;
        public string designation ;
        public string experience ;

    }


}

This is the model class that i am using. I am trying to initialize this in my Controller named as FirstController.
Let us see my FirstController Code.

FirstController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using System.Web.Mail;
using System.Dynamic;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Linq.Expressions;
using System.Diagnostics;

namespace MvcApplication2.Controllers
{
    public class FirstController : Controller
    {
       
        public ActionResult Test()
        {
             Models.MyModel mod = new Models.MyModel();
            Models.MyModel mod1 = new Models.MyModel();
            List<Models.MyModel> lst = new List<Models.MyModel>();
            mod.AddressCity = "Vizag";
            mod.firstname = "Pavan";
            mod.lastname = "Kumar";
            mod.nicknameFirstNmae = "PavanArya";
            mod.FullName = "Pavan Kumar Aryasomayajulu";
            mod.experience = "2yrs";
            mod.designation="Associate s/w engineer";
            mod.profession="Information Technology";

            mod1.AddressCity = "Vizag";
            mod1.firstname = "Kiran";
            mod1.lastname = "Kumar";
            mod1.nicknameFirstNmae = "KiranMantri";
            mod1.FullName = "Kiran Kumar Mantri";
            mod1.experience = "3yrs";
            mod1.designation = "s/w engineer";
            mod1.profession = "Information Technology";
            lst.Add(mod);
            lst.Add(mod1);

            return View("myFirstView", lst);
        }
    }
}

So in my controller i am creating an object and initializing it and finally adding them to my List and then sending it to my View.

So inside my view i am trying to create a list of columnnames that i want to display inside my grid and then sending that columnname list as enumerable to WebGrid contructor.

@{
        //Layout = null;
}
@model IEnumerable<MvcApplication2.Models.MyModel>

    <div>
@{
    List<string> columnNames = new List<string>();
    columnNames.Add("firstname");
    columnNames.Add("lastname");
   
    var grid = new WebGrid(@Model,columnNames.AsEnumerable<string>());.
}
@grid.GetHtml()
             
    </div>

Note: Here columnnames list should have values which are fields to our model class.

Column “firstname” doesnt exist error

Column "firstname" does not exist.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: Column "firstname" does not exist.

Source Error: 

Line 14:     
Line 15: }
Line 16: @grid.GetHtml()
Line 17:              
Line 18:     </div>

Source File: f:\My Code\MvcApplication2\MvcApplication2\Views\Shared\myFirstView.cshtml    Line: 16 

So why do we get this exception when we have both firstname as well as lastname in our ModelClass

The reason behind this is we are making use of fields in our model class and WebGrid class is expecting a class that contains properties.

Let us see the function that microsoft is using to get columnnames from Model class.

 public static IEnumerable<string> GetDefaultColumnNames( IEnumerable<object> source, Type elementType)
        {
            IDynamicMetaObjectProvider provider = source.FirstOrDefault<object>() as IDynamicMetaObjectProvider;
            if (provider != null)
            {
                return GetMemberNames(provider);
            }
            return elementType.GetProperties().Where<PropertyInfo>(delegate(PropertyInfo p)
            {
                return (IsBindableType(p.PropertyType) && (p.GetIndexParameters().Length == 0));
            }).Select<PropertyInfo, string>(delegate(PropertyInfo p)
            {
                return p.Name;
            }).OrderBy<string, string>(delegate(string n)
            {
                return n;
            }, StringComparer.OrdinalIgnoreCase).ToArray<string>();
        }

so in the above function we can see

elementType.GetProperties()

This is the main cause for our error. I hope it would be nice if we have something like elementType.GetFields() also.

So what we are supposed to do to get rid of this exception

The answer to this question is simple. Change your fields present inside the model class to properties

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;

namespace MvcApplication2.Models
{
    public class MyModel
    {
        public string firstname { set; get; }
        public string lastname { set; get; }
        public string AddressCity { set; get; }
        public string FullName { set; get; }
        public string nicknameFirstNmae { set; get; }
        public string profession { set; get; }
        public string designation { set; get; }
        public string experience { set; get; }
    }
}

Now everything will be fine.

Complete code that framework uses to get list of properties from an iEnumerable List

Note: I got this code by using reflector software.

I made slight modifications.

 public static IEnumerable<string> GetMemberNames(object obj)
        {
            IDynamicMetaObjectProvider provider = obj as IDynamicMetaObjectProvider;
            Expression parameter = Expression.Parameter(typeof(object));
            return provider.GetMetaObject(parameter).GetDynamicMemberNames();
        }

        internal static Type GetElementType(IEnumerable<dynamic> source)
        {
            Type sourceType = source.GetType();

            if (source.FirstOrDefault() is IDynamicMetaObjectProvider)
            {
                return typeof(IDynamicMetaObjectProvider);
            }
            else if (sourceType.IsArray)
            {
                return sourceType.GetElementType();
            }
            Type elementType = sourceType.GetInterfaces().Select(GetGenericEnumerableType).FirstOrDefault(t => t != null);

            return elementType;
        }

        private static Type GetGenericEnumerableType(Type type)
        {
            Type enumerableType = typeof(IEnumerable<>);
            if (type.IsGenericType && enumerableType.IsAssignableFrom(type.GetGenericTypeDefinition()))
            {
                return type.GetGenericArguments()[0];
            }
            return null;
        }
        public static IEnumerable<string> GetDefaultColumnNames(IEnumerable<object> source)
        {
            return GetDefaultColumnNames(source, GetElementType(source));
        }
        public static IEnumerable<string> GetDefaultColumnNames( IEnumerable<object> source, Type elementType)
        {
            IDynamicMetaObjectProvider provider = source.FirstOrDefault<object>() as IDynamicMetaObjectProvider;
            if (provider != null)
            {
                return GetMemberNames(provider);
            }
            return elementType.GetProperties().Where<PropertyInfo>(delegate(PropertyInfo p)
            {
                return (IsBindableType(p.PropertyType) && (p.GetIndexParameters().Length == 0));
            }).Select<PropertyInfo, string>(delegate(PropertyInfo p)
            {
                return p.Name;
            }).OrderBy<string, string>(delegate(string n)
            {
                return n;
            }, StringComparer.OrdinalIgnoreCase).ToArray<string>();
        }

        private static bool IsBindableType(Type type)
        {
            Type underlyingType = Nullable.GetUnderlyingType(type);
            if (underlyingType != null)
            {
                type = underlyingType;
            }
            if (((!type.IsPrimitive && !type.Equals(typeof(string))) && (!type.Equals(typeof(DateTime)) && !type.Equals(typeof(decimal)))) && (!type.Equals(typeof(Guid)) && !type.Equals(typeof(DateTimeOffset))))
            {
                return type.Equals(typeof(TimeSpan));
            }
            return true;
        }

This is our gateway function . This function accepts a list that is IEnumerable and it returns a list of properties present inside our Model class.

public static IEnumerable<string> GetDefaultColumnNames(IEnumerable<object> source)
        {
            return GetDefaultColumnNames(source, GetElementType(source));
        }

Please let me know if you have any suggestions or questions
Thanks,
Pavan

About these ads

Written by pavanarya

July 7, 2012 at 4:15 pm

Posted in MVC, Reflection

One Response

Subscribe to comments with RSS.

  1. [...] Why to make use of properties in Model class but not fields – pavanarya [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 69 other followers

%d bloggers like this: