Mar
Some days ago, our product owner presented us a feature we must add to the product: we had to implement paging on several tables displayed on a specific page. My estimate on this item was lower by more than half of the rest of the team and, after some talks, it was evident that they never used the combination of update panels, object data sources and grid views. I showed them a demo and they were all convinced!
Here’s how to proceed. First, we’ll create a data component object that will act as a repository for our ObjectDataSource.
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using DevOne.Demos.AspNetGridViewPagingWithAjax.Models;
namespace DevOne.Demos.AspNetGridViewPagingWithAjax.DataSources
{
[DataObject]
public class EmployeeDataComponent
{
private static List<Employee> _employees = null;
static EmployeeDataComponent()
{
_employees = new List<Employee>();
// Random names from http://random-name-generator.info/random
_employees.Add(new Employee { Id = 1, FirstName = "Aubrey", LastName = "Fisher" });
_employees.Add(new Employee { Id = 2, FirstName = "Glenda", LastName = "Robbins" });
// ...
}
public IEnumerable<Employee> FindAll()
{
return _employees.ToArray();
}
public int CountAll()
{
return _employees.Count();
}
}
}
You probably noticed that I’ve decorated my class with the DataObjectAttribute. This attribute will allow the ObjectDataSource designer to filter the class that acts as a data component. You can use the GridView’s smart tag to create the HTML code for you.
or simply add the HTML code as bellow:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataSourceID="ObjectDataSource1">
<Columns>
<asp:BoundField DataField="Id" HeaderText="Id" SortExpression="Id" />
<asp:BoundField DataField="FirstName" HeaderText="FirstName"
SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName"
SortExpression="LastName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="FindAll"
TypeName="DevOne.Demos.AspNetGridViewPagingWithAjax.DataSources.EmployeeDataComponent">
</asp:ObjectDataSource>
To add AJAX paging, we’ll add a ScriptManager and an UpdatePanel, then we’ll move the existing GridView and ObjectDataSource inside the UpdatePanel’s ContentTemplate. Finally, set the GridView’s AllowPaging property to True. Run the website and you should end up with a result like this one:
If you capture the network traffic with a monitoring tool (Internet Explorer Developer Tools, Firebug, Fiddler,…) you’ll notice that when you change the table’s page, only the required data are transferred to the client which is the desired goal.
So far so good you could say. But, the way we used the ObjectDataSource, we’ll always query the whole employees’ list; meaning that a lot too much data is being transferred from you business layer to the presentation layer. We can improve our code to favor performance here. First, add the EnablePaging property on the ObjectDataSource and set up it to True. To display the number of available pages, you’ll have to specify the method on the data component to call in order to have the number of record in total (this is computed using the PageSize property of the GridView). At this stage, your code should look like this:
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataSourceID="ObjectDataSource1" AllowPaging="True">
<Columns>
<asp:BoundField DataField="Id" HeaderText="Id" SortExpression="Id" />
<asp:BoundField DataField="FirstName" HeaderText="FirstName"
SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName"
SortExpression="LastName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="FindAll"
TypeName="DevOne.Demos.AspNetGridViewPagingWithAjax.DataSources.EmployeeDataComponent"
EnablePaging="True" SelectCountMethod="CountAll">
</asp:ObjectDataSource>
</ContentTemplate>
</asp:UpdatePanel>
The data component’s FindAll method should now be updated to accept two parameters and to make the paging itself. Note that the parameters’ name is the default ones used by the ObjectDataSource. You can modify them with the StartRowIndexParameterName field and the MaximumRowsParameterName field respectively.
public IEnumerable<Employee> FindAll(int startRowIndex, int maximumRows)
{
return _employees.Skip(startRowIndex * maximumRows).Take(maximumRows).ToArray();
}
Run the website and you now have the exact same result than previously (using your HTTP monitoring tool to verify) but, now, the data layer is optimized to fetch only records being displayed instead of the whole table. The paging is done with built-in ASP.NET components and no line of code in the page behind file!
Mar
A lot of people, mainly colleagues and project managers, ask me to summarize in a few words what is Scrum and how it is put in place. Here's a summary about what I’m telling them about it. Key words are in bold.
- Scrum is an agile framework for developing products, generally done in a few months, whose functionalities are listed in a product backlog by priorities defined by the product owner.
- A release is created with one or more iterations called sprints. In collaboration with the product owner, the sprint definition is designed by the team and the tasks are created.
- During a sprint, a daily stand up meeting (called scrum) is organized by the scrum master to give directions on how to apply scrum and to control the progress of the team compared to the initial sprint commitment.
- At the end of a sprint, the team has produced an increment: a partial product that can be delivered to the users.
If you are interested in scrum, visit the Scrum Alliance web site for more information.
Mar
How to use enumeration type with ASP.NET MVC forms (part 2)
In my previous post, I showed you how to use an enumeration inside a form. It was a nice tip but let’s go to the next level: validation! Of course, we want validation, but we want it to be done on the server side AND the client side to avoid unnecessary server round trips.
The first thing to implement is a variation on the RequiredAttribute class. Out of the box, this attribute is unable to check if the enumeration is filled or not. In fact, as an enumeration is a value type, so it always has a value. The usual practice on enumeration is to have the first value to NotSet or Undefined member. We’ll implement a RequiredEnumAttribute based on this tip.
using System;
using System.ComponentModel.DataAnnotations;
namespace DevOne.Framework.ComponentModel.DataAnnotations
{
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class EnumRequiredAttribute : RequiredAttribute
{
private const string NOT_SET_VALUE = "NotSet";
public string NotSetValue { get; set; }
public EnumRequiredAttribute()
: this(NOT_SET_VALUE)
{
}
public EnumRequiredAttribute(string notSetValue)
: base()
{
#region Validation
if (String.IsNullOrWhiteSpace(notSetValue))
{
throw new ArgumentNullException("notSetValue");
}
#endregion
NotSetValue = notSetValue;
}
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
var notSet = Enum.Parse(value.GetType(), NotSetValue);
return !Enum.Equals(value, notSet);
}
}
}
Now we can add the EnumRequiredAttribute to our enumeration and specify which member is the not set value.
using System.ComponentModel.DataAnnotations;
namespace TestMvcApplication.Models
{
public class Person
{
[Required, StringLength(50), Display(Name = "Last name")]
public string LastName { get; set; }
[Required, StringLength(50), Display(Name = "First name")]
public string FirstName { get; set; }
[EnumRequired("Undefined"), Display(Name = "Gender")]
public Gender Gender { get; set; }
}
public enum Gender
{
[Display(Name = "Choose your gender")]
Undefined,
[Display(Name = "Male")]
Male,
[Display(Name = "Female")]
Female
}
}
Let’s check if our new required attribute is working on the server side when the default model binder creates our Person object. In our controller, the ModelState is invalid and the error message is displayed to the user.
Now, it is time to add the necessary Javascript code to do the client-side validation. There’s a base class in the MVC framework used to create client-side validation rule: ModelClientValidationRule. Let’s override it for our enumeration validation routine.
using System.Web.Mvc;
namespace DevOne.Framework.Web.Mvc
{
public class ModelClientValidationEnumRequiredRule : ModelClientValidationRule
{
public ModelClientValidationEnumRequiredRule(string errorMessage, string notSetValue)
{
base.ErrorMessage = errorMessage;
base.ValidationType = "enumrequired";
base.ValidationParameters.Add("notsetvalue", notSetValue);
}
}
}
In this rule, we tell the validation engine what error message should be displayed, what validation type need to be called with which parameters. The corresponding Javscript file that must be referenced in the project is the following:
// DevOne.Framework.Web.Mvc
Sys.Mvc.ValidatorRegistry.validators.enumrequired = function (rule) {
var notSetValue = rule.ValidationParameters.notsetvalue;
return function (value, context) {
return value != notSetValue;
}
}
You can see in this code that we check that the value is different from the notSetValue value given in the constructor of the EnumRequiredAttribute. The last step is to register this specific model client validation rule. This is done in the Global.asax.cs file with the usage of an adapter. This adapter is a link between the required attribute and the model client validation rule.
using System.Collections.Generic;
using System.Web.Mvc;
using DevOne.Framework.ComponentModel.DataAnnotations;
namespace DevOne.Framework.Web.Mvc
{
public class EnumRequiredAttributeAdapter : DataAnnotationsModelValidator<EnumRequiredAttribute>
{
public EnumRequiredAttributeAdapter(ModelMetadata metadata, ControllerContext context, EnumRequiredAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
return new ModelClientValidationEnumRequiredRule[] { new ModelClientValidationEnumRequiredRule(base.ErrorMessage, Attribute.NotSetValue) };
}
}
}
Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EnumRequiredAttribute), typeof(EnumRequiredAttributeAdapter));
}
Now that the plumbing is done, let’s check if the client-side validation is working. Don’t forget to register the needed Javascript librairies and you specific code to validate the required enumeration (CustomMvcValidation.js in my case) and to enable it either in the view with the HtmlHelper or in the web.config.
That’s it! I hope you enjoyed this two parts how to for ASP.MVC and that you’ll make your own validation mechanisms!
Mar
How to use enumeration type with ASP.NET MVC forms (part 1)
Today, I’ll blog about using enumeration inside an ASP.NET MVC forms. As the subject is wide enough, I’ll split it into two parts. Let’s start with part 1: display the enumeration as a dropdown box.
Imagine that you have a registration form that asks your user for their last name, first name and gender. You’ve designed a model based on the data you need to collect and you’ve set up data annotations to enable validation.
using System.ComponentModel.DataAnnotations;
namespace TestMvcApplication.Models
{
public class Person
{
[Required, StringLength(50), Display(Name = "Last name")]
public string LastName { get; set; }
[Required, StringLength(50), Display(Name = "First name")]
public string FirstName { get; set; }
[Display(Name = "Gender")]
public Gender Gender { get; set; }
}
public enum Gender
{
[Display(Name = "Choose your gender")]
Undefined,
[Display(Name = "Male")]
Male,
[Display(Name = "Female")]
Female
}
}
Looks great, now you’ll set up a controller and a view to allow the creation of a Person. We won’t talk about the controller as it’s far beyond the scope of this article but let’s have a look a the generated view when you add it with the create template and strongly-typed to our Person class.
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.LastName) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.LastName) %>
<%: Html.ValidationMessageFor(model => model.LastName) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.FirstName) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FirstName) %>
<%: Html.ValidationMessageFor(model => model.FirstName) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
</asp:Content>
Well, the template engine did a neat work but it didn’t create a label nor a dropdown box for our Gender enumeration. Actually, it is not capable to do it. Let’s do it by ourselves. We’ll need an helper method to transform the available enumeration members to a SelectList.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
namespace DevOne.Framework.Web.Mvc.Extensions
{
public static class EnumExtensions
{
public static IEnumerable<SelectListItem> ToSelectList<T>(this T selected)
where T : struct, IConvertible
{
var type = typeof(T);
if (!type.IsEnum)
{
throw new ArgumentException("T must be an enum type.");
}
foreach (var value in Enum.GetValues(type))
{
var attribute = type.GetField(value.ToString()).GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() as DisplayAttribute;
string name = attribute != null ? attribute.GetName() : Enum.GetName(type, value);
yield return new SelectListItem { Selected = selected.Equals(value), Text = name, Value = value.ToString() };
}
}
}
}
If you read the code, you’ll notice two things. First, our extensions method does not restrict the type on Enum, because this is impossible by the .NET framework. So we add a where restriction that matches as close as possible the Enum class. Of course, there’s a type validation before proceeding. Second, we take the advantages of the data annotations set up on our Gender enumeration.
Let’s now add a gender section in the view, and see the result!
<div class="editor-label">
<%: Html.LabelFor(model => model.Gender) %>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.Gender, Model.Gender.ToSelectList())%>
<%: Html.ValidationMessageFor(model => model.Gender)%>
</div>
You’ll notice the use of the ToSelectList() extension method. Here’s the result:
You can now take the advantages of using the enumeration combined with the data annotations on you model! Stay tuned for the part 2 of this article: how to validate the enumeration.


