Saturday, November 19, 2011
Link for all materials on MVC from Microsoft
http://msdn.microsoft.com/en-us/library/gg416514(v=vs.98).aspx
Progressively enable the mobile web with MVC4, HTML5 and JQuery Mobile. Here the presentation from Phil Haack, senior program manager, Microsoft.
http://channel9.msdn.com/events/BUILD/BUILD2011/TOOL-803T
Enjoy !!!
Wednesday, November 16, 2011
Populate a Dropdown in MVC
Scenario- How to develop a reusable component to populate all dropdowns in MVC applications.
Design-
Repository class - LookupRepository.cs that will fetch lookup values from DB.
Static Extension class - LookupExtension.cs will have method that will extend Dictionary object and return list of SelectListItems.
Static Helper class - LookupHelper.cs will call the repository, cast the result to list of SelectListItems and build SelectList.
Model class
Controller code
Index.chtml code
LookupHelper.cs
LookupExtensions
Design-
Repository class - LookupRepository.cs that will fetch lookup values from DB.
Static Extension class - LookupExtension.cs will have method that will extend Dictionary object and return list of SelectListItems.
Static Helper class - LookupHelper.cs will call the repository, cast the result to list of SelectListItems and build SelectList.
Model class
public class Sample
{
public int Country {get; set;}
public SelectList Countries {get; set;}
}
Controller code
public ActionResult Index()
{
var model = new Sample();
model.Countries = LookupHelper.Countries;
return View(model);
}
Index.chtml code
@model Sample
@Html.DropDownListFor(m => m.Country, Model.Counties, new { @style = "width:86%" })
LookupHelper.cs
public static SelectList Counties
{
get
{
return new SelectList(_lookupRepository.Countries.ToSelectListItems(), "Text", "Value");
}
}
LookupExtensions
public static IEnumerableToSelectListItems(this IDictionary items, string selected = "1")
{
return items.Select(k =>
new SelectListItem
{
Selected = (k.Value == selected),
Text = k.Key,
Value = k.Value
});
}
Labels:
Bind Dropdown in MVC
Review on Telerik ASP.NET MVC Extensions
We used Telerik ASP.NET MVC Extensions GPL 2.0 in our last project extensively. Beside some initial challenges, we able to successfully delivered the project on time. Some of the coolest controls that we used are: Grid, TabStrip, PanelBar, Window, Numeric, Upload.
The features that I liked the most are:
1. In line editing feature of Grid like ASP.NET Grid control.
2. Client side events like OnLoad, OnEdit, OnDelete etc. supported by Telerik controls.
3. Row level formatting in the Grid.
4. Client/Server Template for bound columns.
5. Upload control supports uploading multiple files.
Challenges-
1. No proper support for Dropdown in inline editing Grid.
Reference : http://demos.telerik.com/aspnet-mvc/
The features that I liked the most are:
1. In line editing feature of Grid like ASP.NET Grid control.
2. Client side events like OnLoad, OnEdit, OnDelete etc. supported by Telerik controls.
3. Row level formatting in the Grid.
4. Client/Server Template for bound columns.
5. Upload control supports uploading multiple files.
Challenges-
1. No proper support for Dropdown in inline editing Grid.
Reference : http://demos.telerik.com/aspnet-mvc/
Labels:
Telerik MVC Extensions review
ASP.NET MVC 4 Developer Preview features
Enhancements to Default Project Templates - The Web Site created using Default Project Template gives more modern look.
Mobile Project Template - This is the most important feature in this release. This helps you to create site specifically for mobile and tablet browsers. This is based on JQuery Mobile - open source for touch optimized UI JQuery Mobile Documenation.
MVC 4 Mobile Features
Display Modes - This feature allows the application to select views depending on the browser i.e. Desktop Browser or Mobile. It supports views like _Layout.chtml for Desktop browser, _Layout.mobile.chtml for mobile browser.
Task support for Async Controller - Async Controller returns an Task. The async Controller actions also support timeout.
Azure SDK - Supports for Windows Azure SDK (Sept 2011 1.5)
Looking forward for next release...
Monday, November 14, 2011
How to render Model validation errors on Ajax Calls in ASP.NET MVC
As we all know that Web Development without Jquery & Ajax is an impractical idea. Most of us use Data Annotations for validation purposes. How these Data Annotatation will work on client side when you have Ajax calls in your application. MVC doesn't take care of firing these validations when Ajax calls are made. The below approach can help you handle this situation.
1. MVC generates a spantag i.e. span class="data-valmsg-for=Field Name" for each field that has Html.ValidationMessageFor() in it's markup.
2. Let controller action validates the model and returns you a Json object with all validation messages back to your Ajax call. This collection will have both the Key i.e. Field Name and the validation message.
3. The Ajax call will loop thru this Array and render this messages in the span of appropriate field element.
Base Controller Code
Application Contoller Code
Cliend side code
1. MVC generates a spantag i.e. span class="data-valmsg-for=Field Name" for each field that has Html.ValidationMessageFor() in it's markup.
2. Let controller action validates the model and returns you a Json object with all validation messages back to your Ajax call. This collection will have both the Key i.e. Field Name and the validation message.
3. The Ajax call will loop thru this Array and render this messages in the span of appropriate field element.
Base Controller Code
public JsonResult ValidateModel(string status = "")
{
bool isValid = ModelState.IsValid;
return Json(new
{
Valid = isValid,
NewId = 0,
Errors = GetErrorsFromModelState(),
Status = !string.IsNullOrWhiteSpace(status) ?
status :
isValid ? "Success" : "Failure"
});
}
public DictionaryGetErrorsFromModelState()
{
var errors = new Dictionary();
foreach (var key in ModelState.Keys)
{
// Only send the errors to the client.
if (ModelState[key].Errors.Count > 0)
{
errors[key] = ModelState[key].Errors;
}
}
return errors;
}
public void ClearValidationError()
{
foreach (var key in ModelState.Keys)
{
if (ModelState[key].Errors.Count > 0)
{
ModelState[key].Errors.Clear();
}
}
}
Application Contoller Code
public ActionResult Edit(TemplateViewModel model)
{
bool returnStatus = false;
var id = SaveNewTemplate(model);
returnStatus = true;
JsonResult validation = ValidateModel();
return Json(new { success = returnStatus, id = id, message = message, Validation = validation });
}
Cliend side code
$.ajax({
type: "POST",
url: form.attr("action"),
dataType: "json",
async: false,
cache: false,
data: form.serialize(),
success: function (result) {
if (result.success) {
if (result.Validation != null && !result.Validation.Data.Valid) {
$.each(result.Validation.Data.Errors, function (key, value) {
if (value != null) {
var container = $('span[data-valmsg-for="' + key + '"]');
container.removeClass("field-validation-valid").addClass("field-validation-error");
container.html(value[value.length - 1].ErrorMessage);
}
});
}
}
});
How to handle Session Timeout in Jquery calls in MVC
In my last project, we used Jquery Ajax calls severly for almost all postbacks in the application. Then we had this situration - Jquery Ajax calls throwing 503 Server error (Object reference null) all the places on Server Session Expiration. Here how we handled this particular problem.
Step 1 - First came up a extension method which will check if server session expires and the code is as follows:
Step 2 - Design an Attribute that will call the above routine on every action executed in any controller of the Application. You can apply this attribute on your BaseController at the class level. The code is as follows:
Step 3 - Equip your Ajax Calls with Error Handler that will check for Error Status Code = 504 and redirect the user to SessionTimeout page.
Step 1 - First came up a extension method which will check if server session expires and the code is as follows:
public static bool IsSessionExpired(this HttpSessionStateBase session)
{
if (session == null || !session.IsNewSession)
{
return false;
}
// If it says it is a new session, but an existing cookie exists, then it must have timed out
string sessionCookie = HttpContext.Current.Request.Headers["Cookie"];
return sessionCookie != null && sessionCookie.Contains("ASP.NET_SessionId");
}
Step 2 - Design an Attribute that will call the above routine on every action executed in any controller of the Application. You can apply this attribute on your BaseController at the class level. The code is as follows:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class SessionExpireAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
HttpContext ctx = HttpContext.Current;
// check if session is supported
if (ctx.Session != null)
{
if (filterContext.HttpContext.Session.IsSessionExpired())
{
var viewData = filterContext.Controller.ViewData;
viewData.Model = username;
filterContext.HttpContext.Response.StatusCode = 504;
filterContext.Result = new ViewResult { ViewName = "SessionTimeout", ViewData = viewData };
}
}
}
}
Step 3 - Equip your Ajax Calls with Error Handler that will check for Error Status Code = 504 and redirect the user to SessionTimeout page.
//$.Ajax Get/Post Calls
$("#NewTemplate").live("click", function () {
if (confirmNavigation($(this))) {
$("#selectedTemplateId").val(0);
$.ajax({
type: "GET",
url: Urls.NewTemplate,
async: false,
cache: false,
success: function (cb) {
if (!cb) {
alert("Unable to load the screen");
}
else {
uiChanges = true;
$("#divTemplate").html(cb);
}
},
error: function (xhr, textStatus, errorThrown) {
redirectOnSessionTimeout(xhr.status);
}
});
}
});
//$.load Ajax Calls
function loadAuditTableGrid(name) {
$("#auditTableGrid").load(
Urls.AuditTable,
{ auditTableName: name },
function (response, status, xhr) { redirectOnSessionTimeout(xhr.status); });
}
//$.getJSON way
reqJSON(Urls.CheckForActiveState, { id: selectedId},
function (result) {
if (!result.success)
$("#Message").show();
else
$("#Message").hide();
}, function (xhr, textStatus, errorThrown) {
redirectOnSessionTimeout(xhr.status);
});
function reqJSON(url, params, success, error) {
var CallParams = {};
CallParams.type = params.Method || "POST";
CallParams.url = url;
CallParams.processData = true;
CallParams.data = params;
CallParams.dataType = "json";
CallParams.success = success;
if (error) {
CallParams.error = error;
}
$.ajax(CallParams);
}
function redirectOnSessionTimeout(status) {
if (status == "504") {
location.href = Urls.SessionTimeout;
}
}
Custom validation using MVC3 ValidationAttribute
In this Demo, Let's create a custom validation attribute "DateGreaterAttribute" that validates that a Template EndDate is later than the given StartDate. This validation attribute will be applied like any other Data Annotations.
In the above code, we are implementing a class initializer and IsValid() from the base class. The constructor allows you to provide a default error message in case of validation failure.
The IsValid() method provides 2 parameters,
1. object value - It's the value entered by the user for End Date field.
2. validationContext - Describes the context in which a validation check is performed. The 2 important property that we use here are -
a) ObjectType - Get the type of the object to validate i.e. Template. The GetProperty()method provided by it, returns property information on the Start Date parameter.
b) ObjectInstance - Get the object to validate i.e. Template. This instance is used to get the value of Start Date parameter.
public partial class Template
{
public Template()
{
}
public int TemplateId { get; set; }
public string Name { get; set; }
public byte Active { get; set; }
public System.DateTime Effective { get; set; }
[DateGreater(DateStartProperty="Effective", ErrorMessage="The Expiration Date must be after the Effective Date")]
public NullableExpiring { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public int ChangedBy { get; set; }
public System.DateTime ChangedAt { get; set; }
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DateGreaterAttribute : ValidationAttribute
{
public DateGreaterAttribute() : base("The End Date must be later then Start Date")
{
}
public string DateStartProperty { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string dateEndString = (value != null) ? value.ToString() : string.Empty;
if (string.IsNullOrWhiteSpace(dateEndString))
{
// A blank end date means no comparison. Use Required attribute to force an end date entry.
return ValidationResult.Success;
}
// Get the start date and compare to the end date
bool valid = false;
PropertyInfo dateStartPropertyInfo = validationContext.ObjectType.GetProperty (DateStartProperty);
if (dateStartPropertyInfo != null)
{
object dateStartValue = dateStartPropertyInfo.GetValue(validationContext.ObjectInstance, null);
string dateStartString = (dateStartValue != null) ? dateStartValue.ToString() : string.Empty;
DateTime dateStart;
if (DateTime.TryParse(dateStartString, out dateStart))
{
DateTime dateEnd;
if (DateTime.TryParse(dateEndString, out dateEnd))
{
valid = (dateStart < dateEnd);
}
}
}
return valid ? ValidationResult.Success : new ValidationResult(ErrorMessage);
}
}
In the above code, we are implementing a class initializer and IsValid() from the base class. The constructor allows you to provide a default error message in case of validation failure.
The IsValid() method provides 2 parameters,
1. object value - It's the value entered by the user for End Date field.
2. validationContext - Describes the context in which a validation check is performed. The 2 important property that we use here are -
a) ObjectType - Get the type of the object to validate i.e. Template. The GetProperty()method provided by it, returns property information on the Start Date parameter.
b) ObjectInstance - Get the object to validate i.e. Template. This instance is used to get the value of Start Date parameter.
Custom Validation using ASP.NET4 IValidateableObject
Let's pick up the Template class of my previous blog for this demo and implement duplicate Template Name check validation.
The class design of the Template class is as follows:
In the code above, we have implemented the Validate() method of IValidatableObject to check if there is any Template with the same name. The "yield" construct is used to append this validation to other server side validations applied to the Template model class.
The class design of the Template class is as follows:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public partial class Template : IValidatableObject
{
public Template()
{
}
public int TemplateId { get; set; }
public string Name { get; set; }
public byte Active { get; set; }
public System.DateTime Effective { get; set; }
public NullableExpiring { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public int ChangedBy { get; set; }
public System.DateTime ChangedAt { get; set; }
public IEnumerableValidate(ValidationContext validationContext)
{
if (this.State == System.Data.EntityState.Added)
{
using (SampleDBEntities db = new SampleDBEntities())
{
if (db.Templates.ToList().Exists(m => m.Name.ToUpper() == this.Name.ToUpper()))
{
yield return new ValidationResult("Duplicate template name", new List{ "Name" });
}
}
}
}
}
In the code above, we have implemented the Validate() method of IValidatableObject to check if there is any Template with the same name. The "yield" construct is used to append this validation to other server side validations applied to the Template model class.
Sunday, November 13, 2011
Using EF 4.1 in Web Applications
Scenario - Internet Applications being stateless in nature, how to use the Entity Framework in n-Tier ASP.NET Applications.
Challenge - The biggest challenge of using EF in web applications is - the short lived life cycle of a web page prevents entities from remaining attached to an ObjectContext and providing the required state information of these entities to SaveChanges for updates. While it works fine in scenarios where the Page instantiates an instance of ObjectContext, do the Insert/Update/Delete operations and call the SaveChanges in the same request. At the end of the request, the Page is disposed along with the dependent objects.
But it fails in scenarios where the user makes a series of updates in several requests and finally commit the save operation.
It's easiest to make a call to SaveChanges when you have a long-running ObjectContext that is not an option in the web scenario.
Without the long-running context, there are two possible paths to take.
The first involves persisting the entities in memory on the server-most likely in the user's session. When the user wants to update you can attach those entities to a new context and update them using the new values coming from the controls on the page, then call SaveChanges The downside is scalability when your user base grows.
The second path for allowing SaveChanges to do its job would entail performing the query again prior to saving. You can then update the newly queried entities with the values coming from the client. The state information of entities are supplied from the application logic.
We have taken the second path. Here how we achieved this through coding.
DB Design -
CRUD Operations
public void InsertUpdateTemplate(Template template)
{
using (SampleDBEntities db = new SampleDBEntities (this.EFConnectionString))
{
if (template.Added)
{
db.Templates.Add(template);
}
else
{
var templateEntity = db.Templates.Find(template.TemplateId);
db.Entry(templateEntity).CurrentValues.SetValues(template);
db.Entry(templateEntity).State = template.State;
var fixedDeposites = db.FixedDeposites;
foreach (FixedDeposite deposite in template.FixedDeposites.Where(i => i.State != EntityState.Unchanged))
{
if (deposite.Added)
{
db.Entry(deposite).State = EntityState.Added;
}
else
{
var entity = fixedDeposites.Find(deposite.FixedDepositeId);
db.Entry(entity).CurrentValues.SetValues(deposite);
db.Entry(entity).State = deposite.State;
}
}
var accounts = db.SavingAccounts;
foreach (SavingAccount account in template.SavingAccounts.Where(i => i.State != EntityState.Unchanged))
{
if (account.Added)
{
db.Entry(account).State = EntityState.Added;
}
else
{
var entity = accounts.Find(account.SavingAccountId);
db.Entry(entity).CurrentValues.SetValues(account);
db.Entry(entity).State = account.State;
}
}
}
db.SaveChanges();
}
}
Where :
[MetadataType(typeof(TemplateMetadata))]
public partial class Template : BaseEntity, IValidatableObject
{
}
public class TemplateMetadata
{
//Place for applying Data Annotations on EF generated class.
[Required(ErrorMessage = "Template must have a name")]
public string Name { get; set; }
}
where:
Model Metadata
ASP.NET MVC 2 introduced an extensible model metadata system where developers could implement a class which derived from ModelMetadataProvider to provide meta-information about the models in the system. In ASP.NET MVC 3, You can find the metadata provider findable via the dependency resolver. This is place to apply data annotations on EF generated classes.
public abstract class BaseEntity
{
public abstract bool Added { get; }
public bool Modified { get; set; }
public bool Deleted { get; set; }
public EntityState State
{
get
{
if (this.Added)
{
return EntityState.Added;
}
else if (this.Modified)
{
return EntityState.Modified;
}
else if (this.Deleted)
{
return EntityState.Deleted;
}
return EntityState.Unchanged;
}
}
}
References : http://msdn.microsoft.com/en-us/library/orm-9780596520281-01-21.aspx
Subscribe to:
Posts (Atom)