24 July 2012

MVC jqGrid advanced scenario

The MvcJqGrid is an Html Helper that eases greatly the implementation of the jqGrid in MVC 3 with the Razor view engine or MVC WebForms.
In my previous article (see here) I have explained a simple scenario to use the MvcJqGrid.

In a more advanced scenario, the requested type preferred should be POST, since the default is GET. The SetRequestType method allows the defintion of the request type.
The previous example uses an array that must return the values in the same order as the column definitions. A more generic solution is to return a json object with named properties. The MvcJqGrid supports this scenario by the SetJsonReader method.
The SetJsonReader method is used to configure the grid jsonReader so that the json data doesn't have to match the column model (ColModel) order of jqGrid.
Another feature that is advanced, is the capability to search in each desired column, using different search types.
The SetSearchType sets the seach type for the column. The seach type can be:
  • Input (Default)
  • Select. The SetSearchTerms method receives the collection of strings that define the select options.
  • Datepicker. The SetSearchDateFormat method allow the definition of the date format.
To enable the toolbar searching SetSearchToolbar must be set to true.
The SetSearchOnEnter is used to define when the search is executed:
  • true : the search is executed when the user presses enter
  • false: the search is executed after the user stops typing

The SetRowList creates a dropdownlist in the pager of the grid with the specified number of rows per page.

Now we can create an example using the customer class.
The view:
@(Html.Grid("customerGrid") .SetRequestType(RequestType.Post) .SetJsonReader(new MvcJqGrid.DataReaders.JsonReader { Id="id", RepeatItems = false}) .SetCaption("Customers") .AddColumn(new Column("FirstName").SetWidth(100).SetLabel("First Name")) .AddColumn(new Column("LastName").SetWidth(100).SetLabel("Last Name")) .AddColumn(new Column("CountryName").SetLabel("Country") .SetSearchType(Searchtype.Select) .SetSearchTerms((string[])ViewBag.Countries)) .AddColumn(new Column("Phone").SetWidth(100)) .AddColumn(new Column("BirthDate").SetWidth(80).SetSearchType(Searchtype.Datepicker) .SetSearchDateFormat("yy-mm-dd")) .AddColumn(new Column(" ").SetSearch(false).SetCustomFormatter("buttonize") .SetWidth(25) .SetAlign(Align.Right)) .SetUrl(Url.Action("Search", "Customer")) .SetAutoWidth(true) .SetRowNum(10) .SetRowList(new[] { 10, 15, 20, 50 }) .SetViewRecords(true) .SetPager("pager") .SetSearchToolbar(true).SetSearchOnEnter(false) )

The SetCustomFormatter was explained previously and it allows to format the content of the column using a javascript function.
The controller that returns json with named properties:
public class CustomerController : Controller
{
    public ActionResult Index()
    {
        var countries = new CountryRepository().Search();
        ViewBag.Countries = (from c in countries select c.Name).ToArray();
        return View();
    }

    public JsonResult Search(GridSettings gridSettings)
    {
        List<CustomerSearchResult> customers = null;
        int totalRecords;
        CustomerRepository customerRepository = new CustomerRepository();
            
        CustomerSeachFilter filter = new CustomerSeachFilter();
        if (gridSettings.IsSearch)
        {
            filter.FirstName = gridSettings.Where.rules.Any(r => r.field == "FirstName") ? 
                    gridSettings.Where.rules.FirstOrDefault(r => r.field == "FirstName").data : string.Empty;
            filter.LastName = gridSettings.Where.rules.Any(r => r.field == "LastName") ? 
                    gridSettings.Where.rules.FirstOrDefault(r => r.field == "LastName").data : string.Empty;
            filter.CountryName = gridSettings.Where.rules.Any(r => r.field == "CountryName") ? 
                    gridSettings.Where.rules.FirstOrDefault(r => r.field == "CountryName").data : string.Empty;
            filter.Phone = gridSettings.Where.rules.Any(r => r.field == "Phone") ? 
                    gridSettings.Where.rules.FirstOrDefault(r => r.field == "Phone").data : string.Empty;
            filter.BirthDate = gridSettings.Where.rules.Any(r => r.field == "BirthDate") ? 
            filter.BirthDate = gridSettings.Where.rules.Any(r => r.field == "BirthDate") ? 
                    DateTime.ParseExact(gridSettings.Where.rules.FirstOrDefault(r => r.field == "BirthDate").data, 
                                        "yyyy-MM-dd", null) : DateTime.MinValue;
        }

        customers = customerRepository.Search(filter, 
                                              gridSettings.SortColumn, 
                                              gridSettings.SortOrder, 
                                              gridSettings.PageSize, 
                                              gridSettings.PageIndex, 
                                              out totalRecords);

        var jsonData = new
        {
            total = totalRecords / gridSettings.PageSize + 1,
            page = gridSettings.PageIndex,
            records = totalRecords,
            rows = (
                from c in customers
                select new
                {
                    id = c.CustomerID,
                    FirstName = c.FirstName,
                    LastName = c.LastName,
                    BirthDate = c.BirthDate.ToString("yyyy-MM-dd"),
                    CountryName = c.CountryName,
                    EmailAddress = c.EmailAddress,
                    Phone = c.Phone,
                })
        };
        return Json(jsonData);
    }
}
The MvcJqGrid search feature also allows the configuration of the seach:
  • SetSortName - Defines the default seach field for the grid. 
  • SetSortOrder - Defines the default sort order, using the SortOrder enumerator for the grid. 
  • SetDefaultSearchValue - Sets the default search value for a specific column.

20 July 2012

MVC Data Annotation Validators - The Solution

This "Problem - Solution" is a two series article . So this is the second part: The Solution.
The first part explains the problems facing the implementation of the MVC Data Annotation Validators in a Service Oriented Architecture (SOA) with clear separation of the layers. You can read it here

The solution I found is to use Fluent Validation framework with an Inversion of Control container (IoC) to instantiate my validators.

The Fluent Validation framework is a validation engine that can be used in several scenarios. In this specific solution, I will be focusing in the integration with MVC without using any attributes.
To use Fluent Validation with ASP.Net MVC I am going to use an Inversion of Control container to instantiate the validators.

The difference between an Inversion of Control (IoC) and any other kind of frameworks is that the control gets inverted. The objects in a application are controlled by the Inversion of Control Containers and the application is completely unaware of what the IoC does. A IoC container manages it's life-cycle, invoks methods and is fully autonomous form the application.

The solution implemented by Fluent Validation is to use a custom Validator Factory.
The process to implement can be described in the following steps:
1) Create a Validator Factory for FluentValidation that inherits from ValidatorFactoryBase. Override the CreateInstance method to call the IoC container that is responsible for instantiating the validators.
public class StructureMapValidatorFactory : ValidatorFactoryBase
{
    public override IValidator CreateInstance(Type validatorType)
    {
        return ObjectFactory.TryGetInstance(validatorType) as IValidator;
    }
}

I am going to use StructureMap as the IoC Container.
2) Create a StructureMap Controller Factory that inherits from the MVC DefaultControllerFactory.
public class StructureMapValidatorFactory : ValidatorFactoryBase
{
    public override IValidator CreateInstance(Type validatorType)
    {
        return ObjectFactory.TryGetInstance(validatorType) as IValidator;
    }
}

3) Register your validator types with StructureMap, using the FluentValidation AssemblyScanner. The AssemblyScanner automatically registers all of the validator classes in a particular assembly with a IoC container.
public class MyRegistry : StructureMap.Configuration.DSL.Registry
{
    public MyRegistry()
    {
        FluentValidation.AssemblyScanner.FindValidatorsInAssemblyContaining<CustomerValidator>()
            .ForEach(result =>
            {
                For(result.InterfaceType)
                    .Singleton()
                    .Use(result.ValidatorType);
            });

    }
}

4) Configure MVC to use FluentValidation MVC integration in Global.asax Application_Start
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    // Configure structuremap
    ObjectFactory.Configure(cfg => cfg.AddRegistry(new MyRegistry()));
    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());

    // Configure FluentValidation to use StructureMap
    var factory = new StructureMapValidatorFactory();

    // Tell MVC to use FluentValidation for validation
    ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(factory));
    DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
}

5) Now the real fun can begin. The FluentValidation library can be used in MVC.
Let's create a customer validator class, where the First Name and Last Name are mandatory and we also want to set a custom error message.
Also perform the same validation on the email and guarantee that the email follows the basic rules.
The validation class must inherit from AbstractValidator.
The rules are defined using lambda expressions.
public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.FirstName).NotEmpty().WithMessage("Please fill the first name.");
        RuleFor(customer => customer.LastName).NotEmpty().WithMessage("Please fill the last name.");
        RuleFor(customer => customer.EmailAddress).EmailAddress().WithMessage("Please fill a valid email address.")
                                                    .NotEmpty().WithMessage("Please fill the first email.");
    }

}

The FluentValidation has Built in Validators or you can define your custom validators.
You can read more about the fluent validation here
Structuremap download and documentation can be found here

19 July 2012

MVC Data Annotation Validators - The Problem

This "Problem - Solution" is a two series article . So this is the first: The Problem.

Validating objects through metadata is the approach of Validation with the MVC Data Annotation concept.
Metadata allows us to bind specific configurations to the business objects without changing or extending any of the objects implicit responsibilities.
In ASP.NET MVC, the concept is to use a specialized view model for each view and to pass the data needed. In this approach, the validation attributes must be placed in view model class.
If you have a separation between each layer of the application, and that separation implies that you have different projects for each layer. The model can be defined by the classes generated by the entity framework in you data access layer or by your business layer.

I have a database first approach using the Entity Framework and to use the MVC validation I could create a partial class with a MetadataType attribute to define the validator class. This is called a "buddy class".
A simple example is:
[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{
    // Class generated by the Entity Framework designer
}

public class CustomerMetadata
{
   [Required]
   public object Id;

   [Required]
   [StringLength(250)]
   public object FirstName;
}

The above approach works great, but if you have service oriented architecture with WCF Services the validation attributes will be ignored by the WCF Serializer.
The reason is obvious, the service and operation contracts are described by the Web Service Definition Language (WSDL), but the W3C specification doesn't support these attributes, so they are simple are ignored.

Possible solutions to this problem are:
1) Using buddy classes for partial classes of the WCF proxy
2) Share an assembly with your entities between your WCF client and WCF service and reuse those types

None of the above solutions are what I consider a good option and both will create tight coupling between your service and ASP.NET MVC application.

Another problem I face is that I have a project with all my service Proxies that is referenced by the MVC application or any client.
Since you cannot have two partial classes referring to the same class in two different assemblies (projects) and I don't want to have MVC specific client side validation on my reusable proxy library, so another solution is in order.

Above all i think that if you want to have client side validation you should to it on the client side.
The validation must be as simple as defining some rules that don't need to be associated directly with your model class.

I also wanted to take advantage of all the MVC validation infrastructure with client and server side validation. I don't want to reinvent the wheel and develop all that code manually.

The solution I found is to use Fluent Validation framework with an Inversion of Control container (IoC) to instantiate my validators.
The Inversion of Control (IoC) pattern is all about removing dependencies from your code.
Fluent Validation is a small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your business objects.
This post explains the problem, on my next post I will explain the implementation of Fluent Validation with MVC as the solution.

Meanwhile if you can't wait, you can read about the fluent validation here

13 July 2012

ASP.Net MVC Html Helper for the jqGrid

The  jqGrid is a great grid with a lot of features, but the free version must be developed using JavaScript.
If you are developping in MVC, having a free Html Helper would be great.
This is where the MvcJqGrid enters.

The MvcJqGrid is an Html Helper that eases greatly the implementation of the jqGrid in MVC 3 with the Razor view engine or MVC WebForms .

To use the MvcJqGrid you can NuGet the package and then include the namespace in the view
@using MvcJqGrid
@using MvcJqGrid.Enums

or the web.config in the namespaces of the system.web.webPages.razor section.
A simple example:

@(Html.Grid("CustomerGrid")
    .SetCaption("Customers")
    .AddColumn(new Column("CustomerId").SetLabel("Id"))
    .AddColumn(new Column("Name"))
    .AddColumn(new Column("Company"))
    .SetUrl(Url.Action("List","Customer"))
    .SetAutoWidth(true)
    .SetRowNum(10)
    .SetViewRecords(true)
    .SetPager("pager"))

The Html is self explanatory:
  • Html.Grid("CustomerGrid") - Creates the grid with id CustomerGrid
  • SetCaption - Sets the grid caption
  • AddColumn - Adds the columns to the grid
  • SetUrl - The Action and Controller that returns the json formatted for the jqGrid SetLable - Sets the Label of the column that appears in the grid 

A simple MVC controller for the grid is:
public ActionResult List(GridSettings gridSettings)
{
   CustomerRepository repository = new CustomerRepository();
   string name = string.Empty;
   string company = string.Empty;
            
   if (gridSettings.IsSearch)
   {
       name = gridSettings.Where.rules.Any(r => r.field == "Name") ? gridSettings.Where.rules.FirstOrDefault(r => r.field == "Name").data : string.Empty;
        company = gridSettings.Where.rules.Any(r => r.field == "Company") ? gridSettings.Where.rules.FirstOrDefault(r => r.field == "Company").data : string.Empty;
    }

    var customers = repository.List(name, company, gridSettings.SortColumn, gridSettings.SortOrder);
    int totalCustomers = customers.Count;
    var jsonData = new
    {
        total = totalCustomers / gridSettings.PageSize + 1,
        page = gridSettings.PageIndex,
        records = totalCustomers,
        rows = (
                from c in customers
                select new
                {
                    id = c.CustomerID,
                    cell = new[]
                    {
                        c.CustomerID.ToString(),
                        string.Format("{0} {1}", c.FirstName, c.LastName),
                        c.CompanyName,
                        c.EmailAddress
                    }
        }).ToArray()
    };

    return Json(jsonData, JsonRequestBehavior.AllowGet);
}
A more complex example, with a column for operations like edit and delete is:
@(Html.Grid("CustomerGrid")
    .SetCaption("Customers")
    .AddColumn(new Column("CustomerId").SetLabel("Id").SetSearch(false))
    .AddColumn(new Column("Name"))
    .AddColumn(new Column("Company"))
    .AddColumn(new Column("EmailAddress").SetLabel("Email Address").SetSearch(false))
    .AddColumn(new Column("Last Modified").SetSearch(false))
    .AddColumn(new Column("Telephone").SetSearch(false))
    .AddColumn(new Column(" ").SetSearch(false).SetCustomFormatter("buttonize").SetWidth(60).SetAlign(Align.Right))
    .SetUrl(Url.Action("List","Customer"))
    .SetAutoWidth(true)
    .SetRowNum(10)
    .SetRowList(new[] { 10, 15, 20, 50 })
    .SetViewRecords(true)
    .SetPager("pager")
    .SetSearchToolbar(true)
    .SetSearchOnEnter(false))

Where buttonize is a javascript function where the operation column Html is returned.
   function buttonize(cellvalue, options, rowobject) {
        return 'eXtreme Programming';
    }

For more information and live examples go to the MvcJqGrid site by pressing here
The about tab has the links to the license, the source code and documentation.