theKindOfMe

July 7, 2010

Ad-Hoc Build – Zip – Rename – Deploy ‘Automation’

Filed under: Uncategorized — Tags: , , — yasi8h @ 8:15 am

At work i wanted to automate the build releasing process (of mostly c# desktop/mobile applications which are developed using VS and distributed as installers). This is basically giving the QA a fresh versioned build of whatever the software that is being developed. Although it sound pretty easy and straightforward. It is not. And i have come to hate doing this manually, because it takes a lot of time and its repetitive work, so its very… boring!. The following CLI program that i wrote just automates:

  • Update the working folder from the SVN repo
  • Clean
  • Build (using Release config).
  • Zip (only the Installer(s) resulting from the final build).
  • Extract the version of the resulting executables by reading the AssemblyInfo.cs of the main project.
  • Name the zip file according to all the conventions (boring…).
  • Copy it over to the QA’s SMB share, and put it in a folder structure (again some more naming conventions come in to play).

I know this is not the nicest way of doing this. I should probably be using MSBuild/nAnt/a continues integration server…etc to do it. But, it felt a bit more complicated, and it required me to spend a little bit of time learning stuff. So… i ended up writing this.

#region

using System;
using System.Diagnostics;
using System.IO;

#endregion

namespace auto_build
{
    internal class Program
    {
        private const string Title = "auto_build v0.01";

        private static void Main(string[] args)
        {
            Console.Title = Title;
            var settings = Settings.Default;

            PSeperator();
            P(Title);

            PSeperator();
            P("Settings");

            PSeperator();
            P("Updating Source from SVN...");
            Run(settings.SvnPath, string.Format(settings.SvnUpdateCommand, settings.SolutionRoot));

            PBlankLine();
            P("Cleaning Solution...");
            Run(settings.DevenvPath, string.Format(settings.DevenvClean, settings.SolutionPath, settings.SolutionName));

            PBlankLine();
            P("Building (Release Config) Solution...");
            Run(settings.DevenvPath,
                string.Format(settings.DevenvBuildRelease, settings.SolutionPath, settings.SolutionName));

            PBlankLine();
            P("Zipping the Setup...");
            //ex: "NAME-SG v1.0.0.3106 Setup 06-Jul-2010 10-40"
            var zipFilePath = string.Format("{0}\\Release", settings.SetupProjectPath);
            var zipFileName = string.Format(settings.ZipFileNameFormat, settings.ZipFileApplicationName,
                                            GetProjectVersion(),
                                            DateTime.Now.ToString(settings.ZipFileNameDateTimeFormat));
            Run(settings.WinrarPath,
                string.Format(settings.WinrarPathArgsForCreatingAnArchive, zipFileName, zipFilePath));

            PBlankLine();
            P("Coping the zipped archive to the destanation...");
            var directory =
                Directory.CreateDirectory(string.Format("{0}//v{1}", settings.DestPath, GetProjectVersion()));
            File.Move(string.Format("{0}//{1}", zipFilePath, zipFileName),
                      string.Format("{0}//{1}", directory.FullName, zipFileName));

            PBlankLine();
            P("Done!");
            P("Press any key to exit...");
            Console.ReadLine();
        }

        private static string GetProjectVersion()
        {
            using (
                var reader =
                    new StreamReader(
                        new FileStream(string.Format(Settings.Default.AssemblyInfoPath, Settings.Default.SolutionPath),
                                       FileMode.Open, FileAccess.Read)))
            {
                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine();
                    if (!line.Contains("// [assembly: AssemblyVersion(\"") &&
                        line.Contains("[assembly: AssemblyVersion(\""))
                    {
                        var start = line.IndexOf("[assembly: AssemblyVersion(\"") +
                                    "[assembly: AssemblyVersion(\"".Length;
                        var end = line.IndexOf("\")]");
                        var length = end - start;

                        return line.Substring(start, length);
                    }
                }
            }

            return "x.x.x.x"; //couldn't find the assebmly version
        }

        private static string Run(string command, string paras)
        {
            var pProcess = new Process
                               {
                                   StartInfo =
                                       {
                                           FileName = command,
                                           Arguments = paras,
                                           UseShellExecute = false,
                                           RedirectStandardOutput = true
                                       }
                               };
            pProcess.Start();

            //Get program output
            var strOutput = pProcess.StandardOutput.ReadToEnd();
            pProcess.WaitForExit();
            return strOutput;
        }

        #region Console Text Output Helpers

        private static void P(string text)
        {
            Console.WriteLine(text);
        }

        private static void PBlankLine()
        {
            P(string.Empty);
        }

        private static void PSeperator()
        {
            PBlankLine();
            P("------------------------------------------------------");
            PBlankLine();
        }

        #endregion
    }
}

The program above uses a settings file to get most of the information it requires. you can get the settings file and the above posted file here.

April 1, 2010

Getting a PropertyInfo from a PropertyDescriptor in .NET

Filed under: Uncategorized — Tags: , , — yasi8h @ 5:57 am
PropertyDescriptor pd;
PropertyInfo pi;

//when pd is actually pointing to some real Property Descriptor object
pi = pd.ComponentType.GetProperty(pd.Name);

January 4, 2010

Validation Frameworks for .net Compact Framework

Filed under: Uncategorized — Tags: , — yasi8h @ 6:52 am

If you google the title of this post you would not find any satisfying results. I tried it and i didn’t (.net validation framework intends to support the .net compact framework. But this is still in the todo list) came across any validation framework that officially supports .net CF. But most of .net validation frameworks out there or at least certain parts of the frameworks can be used in a .net cf project. I had to do some research on this topic recently so i am writing down my finding here. Please note that this post is by no means perfect. I might have missed some validation frameworks intentionally or accidentally. I was interested in a framework that would provide a basic validation framework and some ‘bindings’/helpers for windows forms and asp.net MVC. I needed a framework that can be used in a web/desktop and a mobile application. Any framework that was heavily coupled with any one of the platforms was ignored (as it will be difficult to use them in the other platforms).

Validation Frameworks ‘Assessed’

.net Validation Framework

  • The main ValidationFramework.dll assembly works with .netcf 3.5
  • validation rules can be specified as attributes, runtime code and xml configuration. so for whatever validation you need to do, you can use a exsisting rule or you can write a new one.
    • Attributes
      • As you would expect you can specify attributes that specify one or more rules to be applied on a given field, property or (method) parameter.
    • Runtime Code
      • You could write some code to specify what rules needs to be applied to what members. This code can be invoked at the runtime. This code will use types given in ValidationFramework.Reflection namespace to get the relavant members of a given type through reflection. After which you can add validation rules to those members.
    • XML
      • Validation definitions can be defined using XML configuration. The mapping document is designed to be readable and hand-editable.
        • <?xml version=”1.0″ encoding=”utf-8″ ?> <validationMapping xmlns=”urn:validationFramework-validationDefinition-1.5″> <class typeName=”ValidationSample.Person, ValidationSample”><property name=”Name”> <rule typeName=”RequiredStringRule” initialValue=”aaaa”/> </property> <property name=”Age”> <rule typeName=”RangeRule” minimum=”18″ maximum=”80″errorMessage=”Age is invalid” /> </property> <method name=”SetTheAge”> <overloadType typeName=”System.Int32″/> <parameter name=”age”> <rule typeName=”RangeRule” minimum=”18″ maximum=”80″ errorMessage=”Age is invalid” /> </parameter> </method> </class> </validationMapping>
  • Actual validating of a object is done one of the two ways noted bellow.
    • Inside validation
      • This is when the class is aware of validation and does the validation her self. this can be done by inheriting this class from one of the provided base classes and using the inherited methods (such as NotifyValidateChange method, which validate and then do a NotifyChange as in INotifyPropertyChange interface implementations).
      • Another way of doing this is by using a FieldValidationManager or a PropertyValidationManager internally within the class.
    • Outside validation
      • When outside validation is done the class whose objects are validated doesn’t know about the validation process and does not invoke it. someone can externally use a FieldValidationManager or a PropertyValidationManager to do the validation.
  • Validation Rules can be grouped using RuleSets
  • All the Validate methods in *ValidationManagers and Validation related base classes return a ValidationResult.
  • If you decide to write your biznuess classes inheriting from a provided base class that implements IDataErrorInfoProvider interface you can use windows forms controls like ErrorProvider to display errors in windows forms projects.
  • The framework also provides types to help you implement validation in WPF and Web projects.
  • Although the framework have some documentation including the on site wiki, QuickStart projects, FAQ and a help file with documantaion for all the types and there members in the framework. There is a lack of a getting started guide.

Validation Application Block

  • Has better documentation when compared to .net Validation Framework.
  • Just as the .net Validation Framework, Validation Application Block (from here onwards reffered to as VAB) has three ways to specify validation rules or validators.
    • Attributes
    • Runtime Code
    • XML
  • “can be integrated with ASP.NET, Windows Forms, and Windows Communications Foundation (WCF).”
  • Depends on
    • Core library functionality: Microsoft.Practices.EnterpriseLibrary.Common.dll
    • The ObjectBuilder subsystem: Microsoft.Practices.ObjectBuilder.dll
  • When editing the XML configuration for the validation block you can use the provided enterprice library configuration editor(s). This configuration file can contain validation details for types.
  • Validation Rules can be grouped using RuleSets.
  • Validation is done in a manner similler to .net validation framework (or the other way around). ValidationResults class is used to send out validation results…etc
  • Has “Self Validation”. Which basically lets a class mark it self as self validatable and then when the class is validated a specified (by the class it self) method (inside the class) will be run. This method would receive a ValidatinResults type instance which should be modified with the appopriate results.
  • Self Validation and ‘normal’ validation (other validators provided by the framework) can be used together.
  • Validation of a object is done like:
    • Customer customer = new Customer(); ValidationResults results = Validation.Validate<Customer>(customer, customerRuleSetCombo.Text);
    • Or
    • Customer customer = new Customer(); Validator<Customer> validator = ValidationFactory.CreateValidator<Customer>(customerRuleSetCombo.Text); ValidationResults results = validator.Validate(customer);
  • Provides integration support for Win Forms. The main point of integration is ValidationProvider class. It is an Extender (http://msdn.microsoft.com/en-us/library/system.componentmodel.iextenderprovider.aspx) control that adds properties (at design time) to controls. So if you have a form for adding a new customer you can have a ValidationProvider component in it. And set each control on the form to use the ValidationProvider (using the extended properties on them). You can also set a property (via the SourcePropertyName extented property) name to which the value in the respective control should set to.
    • ValidationProvider class effectively lets you validate values for a given business class’s properties without actually having to have an instance of it at the time of validation.However ValidationProvider is not supported on netcf, due to the unavailability of IExtenderProvider interface.

EViL

  • Works with netcf
  • Validation rules can be specified with attributes
  • Business objects can inherit from a provided base class to get the IsValid method (which is used to validate an instance) or use EvilValidator class to do the validation.
  • Don’t have any integration helpers

Fluent Validation

  • Works with netcf
  • Can write validation rules using the builder pattern
    • RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage(“Please specify a valid postcode”);
  • Provide ASP.net MVC framework integration (although as of the time of writing the provided integration is minimal).

NHibernate Validator

  • NHibernate.Validator.dll Works with .netcf
  • Validation rules can be declared using attributes, the builder pattern (ex:- Define(x => x.Name).NotNullableAndNotEmpty();) and XML.
  • Written validators can support applying constrains to the NHibernate meta model. Hence you can use your validation rules to generate an database schema with relevant constrains.
  • “NHibernate Validator is not limited to only be used with NHibernate”.
  • “NHibernate Validator integrates with NHibernate in a native way”
  • Provides integration with
    • WinForms – through NHibernate.Validator.Binding
    • NHibernate
  • Validation is done in a similar way to validation application block.
  • framework can be configured using xml or programaticaly (fluent/builder pattern).

Spring.NET Validation Framework

  • Validation rules can be specified by XML and programmatically.
  • Have WinForms support with Spring.NET RichClient
  • RichClient provides some custom controls, which are not usable in netcf (ie: the custom controls are based on .net system.windows.forms controls).

Personally i think the Validation Application Block is the best bet. It has good documentation, features and ‘support’. But i would never know for sure. Got to use it and experience it for my self.

July 10, 2009

Ever got a exception like “_COMPlusExceptionCode = x” in .net?

Filed under: Uncategorized — Tags: , , — yasi8h @ 6:05 am

In Short:

if you ever get a exception like “_COMPlusExceptionCode = x” in a .net application while you are debugging it in visual studio, look at the following screenshots.

Long Story:

While working on a asp mvc project i got a exception when the EF was trying to save some changes to the db. so i examined the exception object via visual studio’s visual aids. it said _COMPlusExceptionCode = -532459699 ! now what is that?

complusexception while debugging in visual studio

complusexception while debugging in visual studio

after some googling… i felt a bit lost. but by accident i came across the following error message in the same application, in the same line no, for the same exception. a usual ‘exception icon’ or whatever you call it.

a exception icon displayed in visual studio

a exception icon displayed in visual studio

now when i click on that…

visual studio exception infomation dialog been displayed for a update exception

visual studio exception infomation dialog been displayed for a update exception

viola! there is my exception. it seems that visual studio decodes these COM Plus Exception Codes to meaningful exceptions. but at times its easy to miss this way of viewing the exception.

July 1, 2009

Using Custom T4 templates To Generate Better Controllers in ASP MVC

Filed under: Uncategorized — Tags: , , , , — yasi8h @ 7:52 am

While working on this asp.net mvc project at work i couldn’t help notice that there are lots of code that is getting copy-pasted from one controller to the other, when i am adding a new controller. now as you know mvc fm comes with a set of T4 templates that lets you add views and controllers in such a way that some code in these views and controllers are generated for you. this is grate. but you can customize the original templates to your needs and make them generate more of the code for you. (please note that i am talking about controllers that are responsible for mostly CURD on a given single model.)

First of all you will need to identify what code gets repeated from controller to controller in your asp.net mvc project. and then you can get these stuff out in to a template and start working on it.

Locating the T4 Templates

you can find your original templates in [Visual Studio Install Directory]\Common7\IDE\ItemTemplates\[CSharp | VisualBasic]\Web\MVC\CodeTemplates\ and the you can copy them to your project folder. you can start editing these copies and they will be used when you invoke your templates(ex:- by clicking on the Add Controller sub menu within the IDE). for more info please refer to T4 Templates: A Quick-Start Guide for ASP.NET MVC Developers )

Customizing

In my case i wanted my controllers to have all the basic actions like List, Details, Create, Edit and my custom actions like Grid, GridData(take a look at A Grid with Ajax/Pagination/Sorting/Filtering on ASP.net MVC with ExtJS and Enitiy Framework).

i needed to get the type of the model that a given controller is associated with. and then i have to traverse it’s properties(through reflection…etc) and do some string manipulation to generate custom method signatures and custom anonymous types. i also wanted to filter the list of properties that will be used in these parameters and anon types.

Action method signatures

action method signatures can be different from controller to controller. as in…

//Different Properties, in different models
//
//Create action of a Product Controller
public ActionResult Create(string ID, string Name, string Description, string Price)
//
//Create action of a User Controller
public ActionResult Create(string ID, string Name, string Age)

//Different Primary Keys in different models
//
public ActionResult Details(int UID)
//
public ActionResult Details(int UID, int SiteUID)

Method calls to models

//Different Primary Keys
//
public ActionResult Details(int UID)
        {
            return View(Category.GetModelByPrimaryKey(new {UID}));
        }
//
public ActionResult Details(int UID, int SiteUID)
        {
            return View(Category.GetModelByPrimaryKey(new {UID, SiteUID}));
        }

//Different Properties for different models
//
public ActionResult Create(string ID, string Name)
        {
            Category.Create(new {ID,Name}, User.Identity.Name)
            return View();
        }
//
public ActionResult Create(string ID, string Name, int PropOne, string PropTwo)
        {
            Category.Create(new {ID,Name, PropOne, PropTwo}, User.Identity.Name)
            return View();
        }

But unlike in the view here in the controller you can’t just access the model’s type through MvcTextTemplateHost.ViewDataType. when you are generating a controller this property is null. so i thought of using reflection get the model for the current controller. you can use the ControllerRootName property of MvcTextTemplateHost(available through mvcHost variable in the default template) to get the model name(ex: ProductController would give Product).

<#
Type controllerType = Assembly.LoadFile("D:\\path\\to\\your\\model\\assemblys\\dll\\file.dll").GetType("Example.Models." + mvcHost.ControllerRootName);
#>

Following is the full source code of a generated controller and the modified template that was used to generate it. I have reused some methods like IsBindableType and GetEntityKeyProperties(i have modified this a bit) that can be found in some View templates in mvc framework. The generated controller can be used to do CURD operations on the model and provide a backend(providing data to the grid through json) for a ExtJS grid! 🙂 (A Grid with Ajax/Pagination/Sorting/Filtering on ASP.net MVC with ExtJS and Enitiy Framework)

Generated Controller

//
//A Generated Controller
//
using System;
using System.Collections;
using System.Web.Mvc;
using ExampleInc.Filters;
using ExampleInc.Lib.Helpers;
using ExampleInc.Models;
using ExampleInc.Models.UoW;

namespace ExampleInc.Controllers
{
	[Authorize]
    [NavigationFilter]
    public class ExampleModelController : Controller
    {
        //
        // GET: /ExampleModel/
        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

        //
        // GET: /ExampleModel/Details/5
        public ActionResult Details(Int32 SiteUID,Int32 UID)
        {
            return View(ExampleModel.GetModelByPrimaryKey(new {SiteUID,UID}));
        }

        //
        // GET: /ExampleModel/Create
        public ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /ExampleModel/Create
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(Int32 SiteUID ,Int32 UID ,String ID ,String Name ,Decimal Turnover ,String StreetAddress ,String City ,String PostalCode ,String Telephone1 ,String Telephone2 ,String Email ,String Fax ,String Notes ,String Capacity ,Int16 NumberOfMealsPerDay ,Decimal SellingPricePerMeal ,Decimal BudgetPricePerMeal ,String BestTimeToCall ,Byte TradingDaysPerWeek ,Byte TradingWeeksPerYear ,Boolean AccountActive ,Int32 PrimaryContact_SiteUID ,Int32 PrimaryContactUID ,DateTime LastCallStartTime ,Boolean IsProfileCall )
        {
            if (ViewData.ModelState.IsValid)
            {
                // Attempt to add the user

                if (ExampleModel.Create(new {SiteUID ,UID ,ID ,Name ,Turnover ,StreetAddress ,City ,PostalCode ,Telephone1 ,Telephone2 ,Email ,Fax ,Notes ,Capacity ,NumberOfMealsPerDay ,SellingPricePerMeal ,BudgetPricePerMeal ,BestTimeToCall ,TradingDaysPerWeek ,TradingWeeksPerYear ,AccountActive ,PrimaryContact_SiteUID ,PrimaryContactUID ,LastCallStartTime ,IsProfileCall }, User.Identity.Name))
                {
                    TempData["StatusBar"] += "ExampleModel '" + ID + "' successfully added.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View();
        }

        //
        // GET: /ExampleModel/Edit/5
        public ActionResult Edit(Int32 SiteUID,Int32 UID)
        {
            return View(ExampleModel.GetModelByPrimaryKey(new {SiteUID,UID}));
        }

        //
        // POST: /ExampleModel/Edit/5
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(Int32 SiteUID ,Int32 UID ,String ID ,String Name ,Decimal Turnover ,String StreetAddress ,String City ,String PostalCode ,String Telephone1 ,String Telephone2 ,String Email ,String Fax ,String Notes ,String Capacity ,Int16 NumberOfMealsPerDay ,Decimal SellingPricePerMeal ,Decimal BudgetPricePerMeal ,String BestTimeToCall ,Byte TradingDaysPerWeek ,Byte TradingWeeksPerYear ,Boolean AccountActive ,Int32 PrimaryContact_SiteUID ,Int32 PrimaryContactUID ,DateTime LastCallStartTime ,Boolean IsProfileCall )
        {
            if (ViewData.ModelState.IsValid)
            {
                if (ExampleModel.Update(new {SiteUID,UID}, new {SiteUID ,UID ,ID ,Name ,Turnover ,StreetAddress ,City ,PostalCode ,Telephone1 ,Telephone2 ,Email ,Fax ,Notes ,Capacity ,NumberOfMealsPerDay ,SellingPricePerMeal ,BudgetPricePerMeal ,BestTimeToCall ,TradingDaysPerWeek ,TradingWeeksPerYear ,AccountActive ,PrimaryContact_SiteUID ,PrimaryContactUID ,LastCallStartTime ,IsProfileCall }, User.Identity.Name))
                {
                    TempData["StatusBar"] += "ExampleModel '" + ID + "' successfully updated.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View(ExampleModel.GetModelByPrimaryKey(new {SiteUID,UID}));
        }

        public ActionResult Delete(Int32 SiteUID,Int32 UID)
        {
            if (ExampleModel.Delete(new {SiteUID,UID}))
            {
                TempData["StatusBar"] += "ExampleModel successfully deleted.";
            }
            else
            {
                TempData["StatusBar"] += "ExampleModel delete operation failed!.";
            }
            return RedirectToAction("List");
        }

        public ViewResult List()
        {
            return View();
        }

        public ViewResult Grid(string RowClickEventHandlerFunctionName)
        {
            ViewData["RowClickEventHandlerFunctionName"] = RowClickEventHandlerFunctionName;
            return View();
        }

        public JsonResult GridData()
        {
            int totalOjectCount;
            var ExampleModelsList = EmExtJSGridFilterHelper.GetResults(Request, DataMan.ObjectContext.ExampleModel, out totalOjectCount);
            var list = new ArrayList();
            foreach (var ExampleModel in ExampleModelsList) //populate data containers with read data
            {
                list.Add(new
                             {
                                 ExampleModel.SiteUID ,ExampleModel.UID ,ExampleModel.ID ,ExampleModel.Name ,ExampleModel.Turnover ,ExampleModel.StreetAddress ,ExampleModel.City ,ExampleModel.PostalCode ,ExampleModel.Telephone1 ,ExampleModel.Telephone2 ,ExampleModel.Email ,ExampleModel.Fax ,ExampleModel.Notes ,ExampleModel.Capacity ,ExampleModel.NumberOfMealsPerDay ,ExampleModel.SellingPricePerMeal ,ExampleModel.BudgetPricePerMeal ,ExampleModel.BestTimeToCall ,ExampleModel.TradingDaysPerWeek ,ExampleModel.TradingWeeksPerYear ,ExampleModel.AccountActive ,ExampleModel.PrimaryContact_SiteUID ,ExampleModel.PrimaryContactUID ,ExampleModel.LastCallStartTime ,ExampleModel.IsProfileCall
                             });
            }
            return Json(new {dataitExampleInc = list.ToArray(), totalItExampleInc = totalOjectCount});
        }
    }
}

T4 Template used to Generate the Controller

<#@ template language="C#" HostSpecific="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="System.Data.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Data.Objects.DataClasses" #>
<#@ import namespace="System.Data.Linq.Mapping" #>
<#
MvcTextTemplateHost mvcHost = (MvcTextTemplateHost)(Host);
#>
<#
Type controllerType = Assembly.LoadFile("D:\\work_items\\ExampleInc\\ExampleInc-Alt-21\\ExampleInc.Models\\bin\\Debug\\ExampleInc.Models.dll").GetType("ExampleInc.Models." + mvcHost.ControllerRootName);

PropertyInfo&#91;&#93; piArray = controllerType.GetProperties(BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);

string actionMethodParamerters = "", anonTypePropValString = "", gridDataAnonTypePropValString = "";
foreach (PropertyInfo pi in piArray)
		{
			Type currentPropertyType = GetUnderlyingType(pi.PropertyType);

			if(IsBindableType(currentPropertyType) && IsNeededProp(pi.Name))
			{
				actionMethodParamerters += currentPropertyType.Name + " " + pi.Name + " ,";
				anonTypePropValString += pi.Name + " ,";
				gridDataAnonTypePropValString += mvcHost.ControllerRootName.ToLower() + "." + pi.Name + " ,";
			}
		}
actionMethodParamerters = actionMethodParamerters.Remove(actionMethodParamerters.Length - 1); //remove the last extra ","
anonTypePropValString = anonTypePropValString.Remove(anonTypePropValString.Length - 1); //remove the last extra ","
gridDataAnonTypePropValString = gridDataAnonTypePropValString.Remove(gridDataAnonTypePropValString.Length - 1); //remove the last extra ","
#>
using System;
using System.Collections;
using System.Web.Mvc;
using ExampleInc.Filters;
using ExampleInc.Lib.Helpers;
using ExampleInc.Models;
using ExampleInc.Models.UoW;

namespace <#= mvcHost.Namespace #>
{
	[Authorize]
    [NavigationFilter]
    public class <#= mvcHost.ControllerName #> : Controller
    {
        //
        // GET: /<#= mvcHost.ControllerRootName #>/
        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

<#
if(mvcHost.AddActionMethods) {
#>
        //
        // GET: /<#= mvcHost.ControllerRootName #>/Details/5
        public ActionResult Details(<#= GetMethodParamertersString(controllerType) #>)
        {
            return View(<#= mvcHost.ControllerRootName #>.GetModelByPrimaryKey(new {<#= GetDataAnonTypePropValString(controllerType) #>}));
        }

        //
        // GET: /<#= mvcHost.ControllerRootName #>/Create
        public ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /<#= mvcHost.ControllerRootName #>/Create
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(<#= actionMethodParamerters #>)
        {
            if (ViewData.ModelState.IsValid)
            {
                // Attempt to add the user

                if (<#= mvcHost.ControllerRootName #>.Create(new {<#= anonTypePropValString #>}, User.Identity.Name))
                {
                    TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> '" + ID + "' successfully added.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View();
        }

        //
        // GET: /<#= mvcHost.ControllerRootName #>/Edit/5
        public ActionResult Edit(<#= GetMethodParamertersString(controllerType) #>)
        {
            return View(<#= mvcHost.ControllerRootName #>.GetModelByPrimaryKey(new {<#= GetDataAnonTypePropValString(controllerType) #>}));
        }

        //
        // POST: /<#= mvcHost.ControllerRootName #>/Edit/5
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(<#= actionMethodParamerters #>)
        {
            if (ViewData.ModelState.IsValid)
            {
                if (<#= mvcHost.ControllerRootName #>.Update(new {<#= GetDataAnonTypePropValString(controllerType) #>}, new {<#= anonTypePropValString #>}, User.Identity.Name))
                {
                    TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> '" + ID + "' successfully updated.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View(<#= mvcHost.ControllerRootName #>.GetModelByPrimaryKey(new {<#= GetDataAnonTypePropValString(controllerType) #>}));
        }

        public ActionResult Delete(<#= GetMethodParamertersString(controllerType) #>)
        {
            if (<#= mvcHost.ControllerRootName #>.Delete(new {<#= GetDataAnonTypePropValString(controllerType) #>}))
            {
                TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> successfully deleted.";
            }
            else
            {
                TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> delete operation failed!.";
            }
            return RedirectToAction("List");
        }

        public ViewResult List()
        {
            return View();
        }

        public ViewResult Grid(string RowClickEventHandlerFunctionName)
        {
            ViewData["RowClickEventHandlerFunctionName"] = RowClickEventHandlerFunctionName;
            return View();
        }

        public JsonResult GridData()
        {
            int totalOjectCount;
            var <#= mvcHost.ControllerRootName.ToLower() #>sList = EmExtJSGridFilterHelper.GetResults(Request, DataMan.ObjectContext.<#= mvcHost.ControllerRootName #>, out totalOjectCount);
            var list = new ArrayList();
            foreach (var <#= mvcHost.ControllerRootName.ToLower() #> in <#= mvcHost.ControllerRootName.ToLower() #>sList) //populate data containers with read data
            {
                list.Add(new
                             {
                                 <#= gridDataAnonTypePropValString #>
                             });
            }
            return Json(new {dataitems = list.ToArray(), totalItems = totalOjectCount});
        }
<#
}
#>
    }
}
<#+
public static List<PropertyInfo> GetEntityKeyProperties(Type type)
{
	List<PropertyInfo> keyProperties = new List<PropertyInfo>();

	PropertyInfo[] properties = type.GetProperties();

	foreach (PropertyInfo pi in properties)
	{
		System.Object[] attributes = pi.GetCustomAttributes(true);

		foreach (object attribute in attributes)
		{
			if (attribute is EdmScalarPropertyAttribute)
			{
				if ((attribute as EdmScalarPropertyAttribute).EntityKeyProperty == true)
				{
					keyProperties.Add(pi);
				}
			} else if(attribute is ColumnAttribute) {
				if ((attribute as ColumnAttribute).IsPrimaryKey == true)
				{
					keyProperties.Add(pi);
				}
			}
		}
	}

	return keyProperties;
}

public bool IsBindableType(Type type)
{
	bool isBindable = false;

	if (type.IsPrimitive || type.Equals(typeof(string)) || type.Equals(typeof(DateTime)) || type.Equals(typeof(decimal)) || type.Equals(typeof(Guid)) || type.Equals(typeof(DateTimeOffset)) || type.Equals(typeof(TimeSpan)))
	{
		isBindable = true;
	}

	return isBindable;
}

public bool IsNeededProp(string propName)
        {
            switch (propName)
            {
                case "CreationDate":
                case "LastModifiedDate":
                case "LastModifiedBy":
                case "EffectiveDate":
                case "ExpiryDate":
                case "ServerEntryDate":
                    return false;
            }
            return true;
        }

public string GetMethodParamertersString(Type viewDataType)
        {
			List<PropertyInfo> primaryKeys = GetEntityKeyProperties(viewDataType);

			if(primaryKeys.Count > 0) {
				string result = "";
				foreach(PropertyInfo pk in primaryKeys)
				{
					result += String.Format("{0} {1},", GetUnderlyingType(pk.PropertyType).Name , pk.Name);
				}

				return result.Remove(result.Length - 1); //remove the last extra ","
			} else {
				return "UID=Model.PrimaryKey";
			}
        }

public string GetDataAnonTypePropValString(Type viewDataType)
        {
			List<PropertyInfo> primaryKeys = GetEntityKeyProperties(viewDataType);

			if(primaryKeys.Count > 0) {
				string result = "";
				foreach(PropertyInfo pk in primaryKeys)
				{
					result += String.Format("{0},", pk.Name);
				}

				return result.Remove(result.Length - 1); //remove the last extra ","
			} else {
				return "UID=Model.PrimaryKey";
			}
        }

public Type GetUnderlyingType(Type type)
{
	Type currentPropertyType = type;
			Type currentUnderlyingType = System.Nullable.GetUnderlyingType(currentPropertyType);
			if(currentUnderlyingType != null) {
				currentPropertyType = currentUnderlyingType;
			}

			return currentPropertyType;
}
#>

hope this will help someone/myself in the feuture 🙂

February 27, 2009

EntitySqlException: “The ORDER BY sort key(s) type must be order-comparable.”

Filed under: Uncategorized — Tags: , , , — yasi8h @ 8:42 am

Think: In the Entity Framework…

you have two models like this:

Item{ID, Name, Category}, Category{ID, Name}

With a Relationship like this:

Item belongs to a Category. and a Category may have zero or more Items.

if you try to sort the Items by their Category(which doesn’t make sense in the first place! :P) by doing something like:

YourObjectContext.Items.OrderBy(“it.Category”)

you would get the error that is specified in the title of this post. .

but if all you want to do is sort all the Items by the Name of their respective Categories. you should do something like this:

YourObjectContext.Items.OrderBy(“it.Category.Name”)

it will work just fine. you can use this doted notation to specify properties of properties(nested properties). this can be used with OrderBy’s Where caluses…etc this is a faily simple thing i know 🙂 but it was bit tricky to find this out, for me atleast.

February 5, 2009

A Grid with Ajax/Pagination/Sorting/Filtering on ASP.net MVC with ExtJS and Enitiy Framework

Filed under: Uncategorized — Tags: , , , , , , , — yasi8h @ 9:18 am

I am new to all these JS libraries like jQuery and ExtJS(there are many more out there…). when i got started with asp.net mvc i looked in to jQuery. and i was absolutely amazed with all the functionality it offered with very simple and neat syntax! a while ago i did a post on “Ajax Enabling MVCContrib Grid’s Pagination with jQuery”. In which i added AJAX/pagination support to MVCContrib’s Grid. but with time i needed more and more features from the grid. like sorting and filtering(through AJAX of course). then i came across the wonderful JS library called ExtJS. it have grate UI components done and ready to be used. i love all ExtJS stuff that is related to UI. and their components seems to be very extensible as well.

Ext JS Overview

Ext JS is a cross-browser JavaScript library for building rich Internet applications. It includes:

  • High performance, customizable UI widgets
  • Well designed and extensible Component model
  • An intuitive, easy to use API
  • Commercial and Open Source licenses available

i liked there Grid and it had all the features i needed. so i decided to go ahead with it.

extjs_grid_screenshot

the main points i had to look at:

  • including a ExtJS distribution with my asp.net mvc project which is already using jQuery
  • inserting the relevant JS to render the grid in my views
  • creating the necessary controller actions to support the grid’s actions(get data/sort/filter data…etc)

Including a ExtJS distribution

ExtJS JS library can use other libraries(such as jQuery…) as its base library. so been a fan of jQuery, i choose to build a custom version of ExtJS which uses jQuery as its base.  this version of the library includes the jQuery adapter within it self. but you still need to include the jQuery library by your self. it is NOT included in the custom version that you built for your self.  so in your site.master(you can put these where ever you like, not necessary in the site.master. just make sure these are included where ever you use them) you have to include the libraries in the following order: include jQuery first, then include any jQuery plugins you use and finally include ExtJS(any of your custom JS files should be included afterward).

now you are done including the JS libraries necessary . but ExtJS’s UI components need a few more things to be included in your site.master for them to work correctly. you need to include the necessary style sheets/images used by ExtJS components. to get these download ExtJS SDK. this includes all the style sheets and the images among other things. you will need to include the necessary plugins as well.

  • ext-2.2resourcescssext-all.css
  • ext-2.2resourcesimages – images used by ExtJS components
  • all JS files under ext-2.2examplesgrid-filteringgrid – includes necessary plugins needed for the grid filtering functionality

now you are ready to roll. note that we can use what ever jQuery we want when we are working with extjs components. and as we are using the jQuery core(and also because we are already using jQuery in out mvc project) the size of the ExtJS library is much lower.

Rendering the grid

looking at the samples(look under ext-2.2examplesgrid-filtering) included in the extjs sdk. its not that hard to figure out how to render the grid using javascript. the extjs API reference would be helpful as well.

i ran across a bug in the GridView component when i tried to use disable ‘autoHeight’ ‘feature’ of the grid. how ever there was a fix available for it in the the extjs forums.  following is the JS code that i used in my view(in which the grid resides). i just included them in a script tag within the view. but the best way to use them would be to extract them in to some helper methods and then using the helpers.

some notes about ExtJs grids

  • a grid is ‘bound’ to a data store.
  • when rendering a grid you will have to create a data record, column modal and a data reader(JsonReader in this case).
  • column modal – how the columns are structured
  • data record – how a data record(or a person object, in the context of this example) is structured. what fields does it contain…etc
  • data reader – where to get/ how to read data
  • filters and paging is connected to the grid in a ‘pluggable’ manner

        //Sugguested Fix for the bug: "Grid autoHeight disables horizontal scrolling too"
        Ext.override(Ext.grid.GridView, {
            layout: function() {
                if (!this.mainBody) {
                    return;
                }
                var g = this.grid;
                var c = g.getGridEl(), cm = this.cm,
                expandCol = g.autoExpandColumn,
                gv = this;
                var csize = c.getSize(true);
                var vw = csize.width;
                if (vw < 20 || csize.height < 20) {
                    return;
                }
                if (g.autoHeight) {
                    csize.height = this.mainHd.getHeight() + this.mainBody.getHeight();
                    if (!this.forceFit) {
                        csize.height += this.scrollOffset;
                    }
                }
                this.el.setSize(csize.width, csize.height);
                var hdHeight = this.mainHd.getHeight();
                var vh = csize.height - (hdHeight);
                this.scroller.setSize(vw, vh);
                if (this.innerHd) {
                    this.innerHd.style.width = (vw) + 'px';
                }
                if (this.forceFit) {
                    if (this.lastViewWidth != vw) {
                        this.fitColumns(false, false);
                        this.lastViewWidth = vw;
                    }
                } else {
                    this.autoExpand();
                }
                this.onLayout(vw, vh);
            }
        });
        //end bug fix here
        //////////////////

        Ext.onReady(function() {
            Ext.QuickTips.init(); //for enabling tool tips

            Ext.menu.RangeMenu.prototype.icons = {
                gt: '/Scripts/extjs/plugins/grid-filter/img/greater_then.png',
                lt: '/Scripts/extjs/plugins/grid-filter/img/less_then.png',
                eq: '/Scripts/extjs/plugins/grid-filter/img/equals.png'
            };
            Ext.grid.filter.StringFilter.prototype.icon = '/Scripts/extjs/plugins/grid-filter/img/find.png';

            createAndShowGrid();
        });

        function createAndShowGrid() {

            order = Ext.data.Record.create(&#91;
	        { name: 'Id' },
	        { name: 'FirstName' },
	        { name: 'Email' }
        &#93;);

            orderReader = new Ext.data.JsonReader({
                root: 'dataitems',
                totalProperty: 'totalItems' //number of total records
            },
		    order
	    );
            //data store creation
            ds = new Ext.data.Store({
                proxy: new Ext.data.HttpProxy({
                    url: '/Person/ListJson'
                }),
                reader: orderReader,
                sortInfo: { field: 'Id', direction: "ASC" },
                remoteSort: true
            });

            columnModel = new Ext.grid.ColumnModel(&#91;{
                header: 'Person Id',
                dataIndex: 'Id'
            }, {
                header: 'Person Name',
                dataIndex: 'FirstName'
            }, {
                header: 'Person Email',
                dataIndex: 'Email'
            }
	    &#93;);

            columnModel.defaultSortable = true;

            var filters = new Ext.grid.GridFilters({
                filters: &#91;
	                { type: 'string', dataIndex: 'Id' },
	                { type: 'string', dataIndex: 'FirstName' },
	                { type: 'string', dataIndex: 'Email' }
	            &#93;
            });

            var pagingBar = new Ext.PagingToolbar({
                pageSize: 10,
                store: ds,
                displayInfo: true,
                displayMsg: 'Displaying Persons {0} - {1} of {2}',
                emptyMsg: "No Persons to display",
                plugins: filters
            });

            grid = new Ext.grid.GridPanel({
                store: ds,
                cm: columnModel,
                width: 500,
                autoHeight: true,
                title: 'Persons List',
                frame: true,
                loadMask: true,
                stripeRows: true,
                plugins: filters,
                bbar: pagingBar // paging bar on the bottom
            });

            grid.render('personGrid');
            grid.store.load({ params: { start: 0, limit: 10} });
        }
<div id="personGrid"

&#91;/sourcecode&#93;
<h4>Controller actions to support the grid's actions(get data/sort/filter data...etc)</h4>
A ExtJS grid can be fed data in various ways. i am going use a controller action to give the grid the data it needs to render it self. the data would be in json format. we would have a controller action like the following, that returns a JsonResult. we can use the Json method in the mvc framework to serialize the data to json with ease. i am doing it by constructing a anonymous type that hosts the data and then serializing it to json. please note that the following example uses the Entity Framework to deal with the back-end data. although this is example uses the EF. you can easily port it to use any other ORM technology(Linq...etc).

source of /Person/ListJson action


public JsonResult ListJson()
        {
            int totalOjectCount;
            var personsList = ExtJSGridFilterHelper.GetResults(Request, new ObjectEntities().Person,out totalOjectCount);
            var list = new ArrayList();
            foreach (var person in personsList) //populate data containers with read data
            {
                list.Add(new
                             {
                                 Id = person.ID,
                                 FirstName = person.FirstName,
                                 person.Email
                             });
            }
            return Json(new { dataitems = list.ToArray(), totalItems = totalOjectCount });
        }

note that the json result that is returned, basically contains a array of items and a another property called totalItems. this total items property tell the grid, the number of(obviously ) total items that it will have to display. this is helpfule for the grid when pagination is enabled. and you should make sure that this property is specified in your json output if you want the pagination to work correctly.

example json output that is produced by the above action

{"dataitems":[{"Id":"5","FirstName":"jhon Lalic","Email":"jhon@gmail.com"},{"Id":"6","FirstName":"Maureen Joesph","Email"
:"maureen@gmail.com"}],"totalItems":2}

this contains two records. and the totalItems property.

ExtJSGridFilterHelper is a helper class that harbours most of the logic related to implementing sorting/paging and filtering of data. i am not going to explain what the source code is doing, step by step. but you can always read the source ;). 

parsing of parameters sent by the grid…(for the full source code please look at the source of ExtJSGridFilterHelper)

parameters related to paging

  • start
  • limit

parameters related to the sorting

  • sort => the name of the property to be sorted(as given in the client side(aka field name))
  • dir => direction
if (sort != null) //do sorting
            {
                objectsList = query.OrderBy("it." + sort).ToList();
                if (dir == "DESC") objectsList.Reverse();
            }
            else
            {
                objectsList = query.ToList();
            }

parameters related to the filters

public Filter(int id, HttpRequestBase request)
            {
                Id = id;
                Field = request.Params[string.Format("filter[{0}][field]", id)];
                DataType = request.Params[string.Format("filter[{0}][data][type]", id)];
                DataValue = request.Params[string.Format("filter[{0}][data][value]", id)];
                Datacomparison = request.Params[string.Format("filter[{0}][data][comparison]", id)];
            }

how filters are processed, extract from the getExpression() method

</pre>
<pre>public FilterExpressionResult getExpression()
            {
                string expressionString = null;
                var expressionParams = new List(); //paramerters collection
                switch (DataType)
                {
                    case "string":
                        expressionString = string.Format("(it.{0} like '{1}%')", Field, DataValue);
                        break;
                    case "boolean":
                        expressionString = string.Format("(it.{0} = {1})", Field, (DataValue == "true") ? 1 : 0);
                        break;
                    case "numeric":
                        switch (Datacomparison)
                        {
                            case "gt":
                                Datacomparison = ">";
                                break;
                            case "lt":
                                Datacomparison = "";
                                break;
                            case "lt":
                                Datacomparison = "<";
                                break;
                            default:
                                Datacomparison = "=";
                                break;
                        }

                        expressionParams.Add(new ObjectParameter("Param" + Id, DateTime.Parse(DataValue)));
                        expressionString = string.Format("(it.{0} {2} {1})", Field, "@" + "Param" + Id, Datacomparison);
                        break;
                    case "list":
                        var split = DataValue.Split(new&#91;&#93; { ',' });
                        var r = new string&#91;split.Length&#93;;
                        for (var i = 0; i < split.Length; i++)
                        {
                            r&#91;i&#93; = string.Format("(it.{0} = '{1}')", Field, split&#91;i&#93;);
                        }
                        expressionString = string.Format("({0})", string.Join("OR", r));
                        break;
                }
<pre>

source of ExtJSGridFilterHelper

public class ExtJSGridFilterHelper
{
public static List GetResults(HttpRequestBase request, ObjectQuery query, out int totalOjectCount)
{
var sort = request.Params[“sort”];
var dir = request.Params[“dir”];

//get non null values
int intStart, intLimit;
if (!int.TryParse(request.Params[“start”], out intStart)) intStart = 0;
if (!int.TryParse(request.Params[“limit”], out intLimit)) intLimit = 10;

List objectsList;

totalOjectCount = query.Count();
var i = 0;
var exspressions = new List();
var parameters = new List();
while (true) //check for filters starting from 0, if one exists move to the next one(0 -> 1…), .
{
if (!Filter.checkExistence(i, request.Params)) break; //…if not stop looking further
var expression = new Filter(i, request).getExpression();
exspressions.Add(expression.Expression);
parameters.AddRange(expression.Parameters);
i++; //keep track of index
}
var exspression = string.Format(“({0})”, string.Join(“AND”, exspressions.ToArray()));
//build the final expression
if (exspression != “()”) query = query.Where(exspression, parameters.ToArray()); //filter collection on the expression

if (sort != null) //do sorting
{
objectsList = query.OrderBy(“it.” + sort).ToList();
if (dir == “DESC”) objectsList.Reverse();
}
else
{
objectsList = query.ToList();
}

if (intStart + intLimit > objectsList.Count)
intLimit = objectsList.Count – intStart; //make sure the range we select is valid
objectsList = objectsList.GetRange(intStart, intLimit);
return objectsList;
}

#region Nested type: Filter

public class Filter
{
public string Datacomparison { get; set; }
public string DataType { get; set; }
public string DataValue { get; set; }
public string Field { get; set; }

public int Id { get; set; }

public static bool checkExistence(int filterIndex, NameValueCollection @params)
{
return (@params[string.Format(“filter[{0}][field]”, filterIndex)] != null);
}

public Filter(int id, HttpRequestBase request)
{
Id = id;
Field = request.Params[string.Format(“filter[{0}][field]”, id)];
DataType = request.Params[string.Format(“filter[{0}][data][type]”, id)];
DataValue = request.Params[string.Format(“filter[{0}][data][value]”, id)];
Datacomparison = request.Params[string.Format(“filter[{0}][data][comparison]”, id)];
}

public FilterExpressionResult getExpression()
{
string expressionString = null;
var expressionParams = new List(); //paramerters collection
switch (DataType)
{
case “string”:
expressionString = string.Format(“(it.{0} like ‘{1}%’)”, Field, DataValue);
break;
case “boolean”:
expressionString = string.Format(“(it.{0} = {1})”, Field, (DataValue == “true”) ? 1 : 0);
break;
case “numeric”:
switch (Datacomparison)
{
case “gt”:
Datacomparison = “>”;
break;
case “lt”:
Datacomparison = “”;
break;
case “lt”:
Datacomparison = “<"; break; default: Datacomparison = "="; break; } expressionParams.Add(new ObjectParameter("Param" + Id, DateTime.Parse(DataValue))); expressionString = string.Format("(it.{0} {2} {1})", Field, "@" + "Param" + Id, Datacomparison); break; case "list": var split = DataValue.Split(new[] { ',' }); var r = new string[split.Length]; for (var i = 0; i < split.Length; i++) { r[i] = string.Format("(it.{0} = '{1}')", Field, split[i]); } expressionString = string.Format("({0})", string.Join("OR", r)); break; } return expressionString != null ? new FilterExpressionResult { Expression = expressionString, Parameters = expressionParams } : null; } #region Nested type: FilterExpressionResult public class FilterExpressionResult { public string Expression { get; set; } public List Parameters { get; set; } } #endregion } #endregion } [/sourcecode] ah! that's it for now. i might post some helpers that can be used in your views when you want to render the grid. the advantage of using this would be, so you won't need to repeat all that javascript code in each and every view that you want to display a grid in. hope this will be helpful to someone. cheers!

January 12, 2009

Ajax Enabling MVCContrib Grid’s Pagination with jQuery

Filed under: Uncategorized — Tags: , , , , — yasi8h @ 8:16 am

The MVCContrib project(at http://www.codeplex.com/MVCContrib ) provides a nice Grid ‘control'(and a lot more of course…) for use with MVC framework. Currently it does not support some what magical generation of javascript. so that you can have a ajaxified grid control. but with jQuery its pretty easy to ajaxify it. Today i finished ajaxifiying the paging of the grid, with a little bit of jQuery. So this is how i did it. i am sure that there must be betters ways of doing it. but this is what i came up with 😛

Structure

First of all for this to work you will need some ‘structure’ in your app. let’s take a little example. if you want to display a list of People in your application using a grid. you will have a controller for people: PeopleController. this would have a two actions. one would be List. while the other one would be ListForGrid. List action would be a plain action that will just return a normal view: List.aspx. while ListForGrid would return a asp ‘control’ or a partial view: ListForGrid.ascx. ListForGrid.ascx would know how to render a grid. it will use the normal Grid helpers to render a grid that displays people. List.aspx will contain an empty div:grid_holder and some java script that would add the Ajax goodness.

How it works

when a user asks for the People/List. the List.aspx would be rendered. and upon the document.ready() it would call ListForGrid action via ajax and load the resulting html in to the page. ListForGrid uses the ListForGrid.ascx to render the grid. ListForGrid action will also accept the necessary parameters to support Grid’s pagination(ListForGrid(int? page)).

this is all good. but still we have not archived our main goal. the rendered grid will have pagination links that would take us back to theListForGrid action directly. so if we click on them we would go to that resulting view with out any ajax ‘magic’. to get the grid to render them with ajax we are going to have to use jQuery to ‘hook’ in to the click event’s of the pagination related links( first | prev | next | last) that are in the rendered grid. and we will ‘intercept’ any events(clicks) and load the grid through ajax as necessary. so if you click on the next link in the grid we will stop the page from going to the relevant page, we will ‘read’ what ‘location’ that he is trying to take us to and we will load that through ajax.

now if you don’t understand what i am trying to tell you, please take a look at the source code bellow. i am sure you will understand that 🙂

relavant JS/jQuery source code
$gridHolder = "#grid_holder";
$ListForGridURL = "/People/ListForGrid";

//load the grid when the document loads. this code will run only once
$(document).ready(function() {
    $($gridHolder).load($ListForGridURL, null, function() {
        //now the grid is loaded call ajazPaging,
        //this will hook on to the click events
        ajaxPaging();
    });
});

//hook on to the click events(in a recursive way) of all pagination
//related links, and when clicked load the updated grid through AJAX
function ajaxPaging() {
    //this will zero in on the needed <a> elements in the Grid
    $(".paginationRight > a")
        .click(function(event) {
            //stop the browser from going to the relevant URL
            event.preventDefault();
            //this.href will give us the href value of the current
            //element, which have the URL from which we should update our grid
            $($gridHolder).load(this.href, null, function() {
                //call the function recursively so that the same code would
                //run when the user click on the pagination links after the loading happens
                ajaxPaging();
            });
        });
}

Update: with the release of jQuery 1.3 you can write this in a more simpler way using the live(). more info

Live Events

jQuery now supports “live events” – events that can be bound to all current – and future – elements. Using event delegation, and a seamless jQuery-style API, the result is both easy to use and very fast.

$gridHolder = "#grid_holder";
$ListForGridURL = "/People/ListForGrid";

//load the grid when the document loads. this code will run only once
$(document).ready(function() {
    $($gridHolder).load($ListForGridURL, null, function() {
        //now the grid is loaded call ajazPaging,
        //this will hook on to the click events

    //hook on to the click events of all pagination
    //related links, and when clicked load the updated grid through AJAX
    //this will zero in on the needed <a> elements in the Grid
    $(".paginationRight > a")
        .live("click", function(event) {
            //stop the browser from going to the relevant URL
            event.preventDefault();
            //this.href will give us the href value of the current
            //element, which have the URL from which we should update our grid
            $($gridHolder).load(this.href);
        });
    });
});
rest of the sample source code(controller, actions and views…)
<em>PeopleController </em>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

using MvcContrib.Pagination;
using MvcContrib;

namespace MvcLocalization.Controllers
{
    public class PeopleController : Controller
    {
        public ActionResult List()
        {
            return View();
        }

        public ActionResult ListForGrid(int? page)
        {
            ViewData["people"] = GetListOfPeopel().AsPagination(page ?? 1, 1);
            return View("UserListGrid");
        }
    }
}

<em>
List.aspx</em>


<h2>People</h2>
<div id="grid_holder"></div>

        $gridHolder = "#grid_holder";
        $ListForGridURL = "/People/ListForGrid";

        //load the grid when the document loads. this code will run only once
        $(document).ready(function() {
            $($gridHolder).load($ListForGridURL, null, function() {
                //now the grid is loaded call ajazPaging, this will hook on to the click events
                ajaxPaging();
            });
        });

//hook on to the click events(in a recursive way) of all pagination
related links, and when clicked load the updated grid through AJAX
        function ajaxPaging() {
            //this will zero in on the needed <a> elements in the Grid
            $(".paginationRight > a")
                 .click(function(event) {
                       //stop the browser from going to the relevant URL
                     event.preventDefault();
                    //this.href will give us the href value of the current <a> element, which have the URL from which we should update our grid
                     $($gridHolder).load(this.href, null, function() {
                          //call the function recursively so that the same code would run when the user click on the pagination links after the loading happens
                         ajaxPaging();
                     });
                 });
        }

    


<em>ListForGrid.ascx</em>


<%
    Html.Grid(
        "people",
            column =>
            {
                column.For(p => p.Name);
                column.For(p => p.Age);
                column.For(p => p.Address);
            }
            );
%>

Aha! that’s it. jQuery rocks eh!
hope this will be useful to someone/myself.

Note 1: if you are completely new to MVCContrib Grid, you might want to read this
Note 2: a article on “Using MVCContrib Grid in a Web 2.0 World with jquery and AJAX” (btw this article does not cover implementing paging through ajax…)

December 4, 2008

ASP.net Membership Password Hashing Algorithm

Filed under: Uncategorized — Tags: , , — yasi8h @ 1:47 am

Recently i had the chance to work with ASP.net membership(http://msdn.microsoft.com/en-us/library/yh26yfzy.aspx). after deciding on adding the database schema required by asp.net to our application database it self. there were some questions left on how to use the data in these (asp.net services)tables(the data related to user logins were in aspnet_Membership table) with some other non server side application who use the same database. specifically i needed to find out how asp.net password hashing algorithm works. so when another application wants to use the that user login data for something, it knows how to generate the correct hash and do the authentication for the users.

It was apparent that it was using a “hash + (random?)salt per user” to store the passwords(well after all this is configurable. in my case it was configured to use ‘hash’ method to store the passwords for the users).  after much googling i found out that there are some options that we can give asp.net on what hashing algorithm to use via <machineKey> element in the config file. this article noted that asp.net by default used SHA1. but still i didn’t know how it exactly generated the hash(ie: is it password + salt or is it salt + password…etc). so i used Reflector to find out. the interesting code is in System.Web.Security.MembershipProvider.EncodePassword.

so with that code(- some unneeded logic) you can successfully generate the same password hash that asp would.

internal string EncodePassword(string pass,  string salt)
{
   byte[] bytes = Encoding.Unicode.GetBytes(pass);
   byte[] src = Convert.FromBase64String(salt);
   byte[] dst = new byte[src.Length + bytes.Length];
   byte[] inArray = null;
   Buffer.BlockCopy(src, 0, dst, 0, src.Length);
   Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);

   HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
   inArray = algorithm.ComputeHash(dst);

   return Convert.ToBase64String(inArray);
}

hope this helps someone/myself(in the future)

November 27, 2008

ASP.NET MVC and Ruby on Rails

Filed under: Uncategorized — Tags: , , , , — yasi8h @ 5:31 pm

the last few days i have been learning about asp.net and about the new asp.net mvc framework. i am very new to asp.net. i’ve never ever used asp before. but as per a requirement at my work place i had to look in to it. i was rather excited to look in to it really. because i always admired mvc design pattern! 🙂 i am one of those people who love the separation of concerns. as a side project(and for my ‘final year’ project) i have been learning ruby and rails at home. so when i started looking in to asp.net mvc i had a considerable understanding of the rails framework. so while learning asp.net mvc, i really felt that its a lot similar to rails. there are lots of mvc frameworks out there and i am sure that most of them learn from each other. so thats is a grate thing. i mean as far as technology is concerned ‘copying’ is a good thing. so then you can build up something new on top of something that is already there. so anyways there were a lot of things that i felt was shared between rails and asp.net mvc. for example the way you write out the configuration for url routing in your application. in rails you put that stuff in application.rb(applicationcontroller). and you write all the configuration on the routing using ruby. so in asp.net mvc we do it in the applicationcontroller which is in the global.aspx. and in asp.net mvc there are many instances where the convention over configuration principal is applied. in controller classes. each and every public method is a action. but in the early version of the mvc fw this was not the case, you had to mark the action methods using a attribute. the url routing was configured using xml in the early releases, but later on you can now configure them in c#. i also noted that new additions to c# 3 language spec makes it a bit more like ruby. now i am not saying c# is like ruby. it is of course NOT. but microsoft have introduced some new features like anonymous methods… that is been used a lot in mvc fw(at least in the samples/blog posts found on the net…).

whats more it seems that mvc fw will have some kind of scaffolding capabilities in the future! 🙂 right now even there is the asp.net dynamic data project. it offers you something more like scaffolding. you can give it a model and it will generate all the CURD stuff for you. dynamically. and these forms have basic validation stuff built in. further more these generated asp pages use ajax controls. i am not very educated about dynamic scaffolding capabilities with rails so i can’t compare it with dynamic data. but i am sure rails have some exciting stuff none the less.

all in all, mvc(the design pattern) rocks! 🙂 that i am sure of. for me it have been easy to learn these two frameworks together in parallel. because most of the things that you learn in one framework can be applied to the other. i am not talking about very low level implementation details. but how things get done in the higher level. (ie: implementing authorization with the use of filters…). i love these two fws. microsoft been what it is, becomes inherently ‘evil’, so i can’t really show my love towards asp.net mvc fw. but anyways its exciting. i am enjoying learning mvc fm and rails. i have been missing out on these grate frameworks been in the mobile development side. but not anymore.

Create a free website or blog at WordPress.com.