Showing posts with label Asp.Net MVC. Show all posts
Showing posts with label Asp.Net MVC. Show all posts

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.

29 May 2012

Customer KnockoutJS Validation

This article is the second on KnockoutJS series I am writing.
The first article was introductory: Customer KnockoutJS and MVC demo using JSON

Now I am going to focus on KnockoutJS and validation. I am going to use a KnockoutJS Plugin for model and property validation that is named Knockout Validation. You can download it from here.

The Asp.Net MVC Controller have the actions to Get and Add a customer. This example has a new customer property: the country.
This property allows to add an input type select to KnockoutJS and validation.
namespace KnockoutDemo.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "";

            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public JsonResult Get(int customerID)
        {
            // Get the customer ...
            Customer customer = new Customer {CustomerID = customerID, 
                                              FirstName = "John", 
                                              LastName = "Doe", 
                                              IsMale = true, 
                                              CountryID = 1 };
            return Json(customer);
        }

        [HttpPost]
        public JsonResult Add(Customer customer)
        {
            // Save the customer ...

            // return status message 
            var message = string.Format("Customer: {0} {1} Added. IsMale: {2} Age:{3}  CountryID: {4} ",
                                        customer.FirstName, customer.LastName, customer.IsMale.ToString(), 
                                        customer.Age.ToString(), customer.CountryID.ToString());
            return Json(message);
        }

    }
}

The Asp.Net MVC model is the customer with the new property:
 namespace KnockoutDemo.Models
{
    public class Customer
    {

        public int CustomerID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool IsMale { get; set; }
        public int Age { get; set; }
        public int CountryID { get; set; }
    }
}


The Asp.Net MVC Layout includes the new knockout validation plugin:
       
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/json2.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.validation.js")" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>Knockout Demo</h1>
            </div>
            <div>&nbsp;</div>
        </header>
        <div>&nbsp;</div>
        <div>&nbsp;</div>
        <section id="main">
            @RenderBody()
        </section>
        <footer>
        </footer>
    </div>
</body>
</html>
And the Asp.Net MVC view has the KnockoutJS and validation specifics:
@{
    ViewBag.Title = "Add Customer";
}



@ViewBag.Message

Customer Number:

First Name: Last Name: Age: Male Country:


The KnockoutJS now has a observableArray of countries in the VewModel, that is bind to the country select.
Note the data-bind oprtions of the select:
- options: controls what options should appear in a drop-down lis
- optionsValue: The name of the ViewModel property to bind to the option value
- optionsText: The name of the ViewModel property to bind to the option text
- value: The name of the ViewModel property to bind to the selected value
- optionsCaption: The option that is used to make the user select a value on the select list

There is also the validation plugin specifics in the VewModel declaration:
- The extend is used to extend the observables with the validation rules. (you can read about all of them here)
In the example I am using the required  (required: true) and number (number: true) validation rules.
- validatedObservable in the view model declaration
- isValid() - Before saving the data validate that the model is valid
- validation.init - Used to configure the validation engine

In the example the invalid inputs are painted in red, that is where the css class is used by the validation.

07 May 2012

Customer KnockoutJS and MVC demo using JSON

After reading about KnockoutJS I have decided to create a simple demo using JSON to comunicate with the web server.
The application retrieves a Customer from an ASP.Net MVC Action and sends it to be saved.

The KnockoutJS is a JavaScript Model View ViewModel (MVVM) framework.
The View Model object contains properties which values are specified as ko.observable(). Knockout will automatically updates the UI when the view model changes.
KnockoutJS has a declarative binding syntax where the HTML view elements are bind with our view model object. Knockout uses the "data-bind" attribute in the HTML elements for the data binding.

To learn the basics goto the KnockoutJS site by pressing here

Pr-requisites:
1) KnockoutJS
2) json2.js - for json parsing
3) ASP.NET MVC
4) jQuery

The Asp.Net MVC Controller have the actions to Get and Add a customer.

namespace KnockoutDemo.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "";

            return View();
        }


        [AcceptVerbs(HttpVerbs.Post)]
        public JsonResult Get(int customerID)
        {
            // Get the customer ...
            Customer customer = new Customer {CustomerID = customerID, FirstName = "John", LastName = "Doe", IsMale = true };

            return Json(customer);
        }


        [HttpPost]
        public JsonResult Add(Customer customer)
        {
            // Save the customer ...

            // return status message 
            var message = "Customer: " + customer.FirstName + " " + customer.LastName + " Added.";
            message += " IsMale: " + customer.IsMale.ToString();
            return Json(message);
        }

    }
}

The Asp.Net MVC model is the customer:
    public class Customer
    {
        public int CustomerID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool IsMale { get; set; }
    }


The Asp.Net MVC Layout:
    
 <!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/json2.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.validation.js")" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>Knockout Demo</h1>
            </div>
            <div>&nbsp;</div>
        </header>
        <div>&nbsp;</div>
        <div>&nbsp;</div>
        <section id="main">
            @RenderBody()
        </section>
        <footer>
        </footer>
    </div>
</body>
</html>



And finally the Asp.Net MVC view has the KnockoutJS specifics:
@{
    ViewBag.Title = "Add Customer";
}

<h2>
@ViewBag.Message</h2>
<form action="" method="post">
<b>Customer Number: </b> <span data-bind="text: CustomerID"></span><br />
<br />
<b>First Name: </b><input data-bind="value: FirstName" style="width: 200px;" type="text" /> <br />
<br />
<b>Last Name: </b><input data-bind="value: LastName" style="width: 200px;" type="text" /> <br />
<br />
<input data-bind="checked: IsMale" type="checkbox" /><b>Male</b><br />
<br />
<input data-bind="click: KnockoutDemoNamespace.addCustomer" type="button" value="Add Customer" /><br />
<div id="message">
</div>
</form>


The KnockoutJS has the following specifics:
1) View Model object: customerViewModel
2) HTML View: The HTML from the page with the KnockoutJS specific attributes for data binding
The demo performs a data bind with a span, and the input types text, checkbox and button.
Note that the "data-bind" has the html element attribute to be affected and the View Model property associated.
3) View Model activation: ko.applyBindings(viewModel);