Monday, November 14, 2011

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:


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.



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 Nullable Expiring { 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:


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 Nullable Expiring { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public int ChangedBy { get; set; }
public System.DateTime ChangedAt { get; set; }

public IEnumerable Validate(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 -
DB Desing


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

Tuesday, September 6, 2011

IIS 404 error while running ASP.NET MVC on IIS 7 (Production)

Fix : ASP.NET MVC dlls are missing. Check if you have following dlls in the bin of deployment folder.

System.Web.MVC
System.Web.Razor
System.Web.Abstractions
Microsoft.Web.Infrastructure
System.Web.WebPages
System.Web.WebPages.Razor
System.Web.WebPages.Deployment
System.Web.Helpers

Unrecognized attribute 'targetFramework'

Scenario - "Unrecognized attribute 'targetFramework'" error while running ASP.NET/ASP.NET MVC Application.

Fix - The AppPool is using a wrong .NET framework. You can check it by following steps.

IIS -> Application Pools -> Double click on "Your Application Pool" -> .NET Framework Version.

Enjoy !!!

Sunday, September 4, 2011

How to configure Code Coverage in TFS 2010

Prerequisite - You should have Visual Studio 2010 Ultimate or Visual Studio 2010 Premium edition.

Step - 1: Create a .testconfig file using Add New Item menu.
Step - 2: Double click the .testconfig file to open Test Settings window.
Step - 3: Choose Data and Diagnostics tab and check you have following settings.
Role - Local Machine only
Code Coverage - Checked
Step - 4: Double click the Code Coverage to open Code Coverage Detail window. Add all your assemblies in this window that should come under code coverage.
Now if you run your tests, you should also see Code Coverage in Code Coverage tab (Test - Windows -> Code Coverage Result)