Mar272010

HandleError attribute (MVC)

    In ASP.NET MVC you can decorate the action methods and the controllers with filter attributes to control their behavior.  These filters are injected into the pipeline, and depending on their type; they are executed before/after something happens. You can either use the filters that come with the framework, or write your own filters. Even though it is easy to test the functionality of the filters, it may not be easy to test if the controller/action is/are decorated with the filter (You can use reflection).  There are 4 different types of filters that come with the framework:

  • Authorization Filter: These type of filters have very high priority, and run before other types of filters or the action execution.
  • Action Filter: These type of filters have normal priority, and run before and after the action execution, by this way you can do some work before  an action is executed or after an action is executed. A good example could be logging the system activity, or calculating the time that takes to execute the action.
  • Result Filter: These type of filters have  normal priority, and run before and after the action result is executed, by this way, you can manipulate the action results.
  • Exception Filter: These filters are executed if an unhandled exception is thrown.

In this blog posting I will be explaining HandleError attribute which is a type of Exception Filter. You may want to use this filter to catch the exception for either logging the exception to a log file or display a nice error page so the user does not freak out.  If you create a default MVC project using Visual Studio, you will see that the HomeController class is decorated with HandleError filter.

[HandleError]
public class HomeController : Controller


This means whenever there is an exception that is not handled, render the Error view, so when there is an exception the framework will first look at the folder “Views/Home” and check if there is an Error.aspx page (assuming you are using the default WebForms Engine). If it can not find this page, it will check if there is a Error.ascx page, if there is none, then the same search will be applied to Shared folder. Finally if there is an Error.aspx/ascx page found it will be rendered, otherwise, in the production system, IIS will handle error, in the development system you may see the error below (depending on your web.config customErrors setting).

image

Or if you use the HandleError filter, and configure your system right, this may be the screen:

image

Of course you want to the second user friendly error page. Let’s first see what we should be doing to get the simple user friendly user page and then we will look into customizing this error page.

  1. Decorate the controller or the action method with HandleError
  2. Create an Error.aspx or Error.ascx page, and put this page either into the View folder of the controller or Shared folder.
  3. Open your web.config file and change the custom errors definition to customErrors mode=”On”


After this 3 simple steps, you have  an Error page that will be displayed whenever there is an unhandled exception.

HandleErrorAttribute is defined in the HandlErrorAttributed.cs (suprised?) file. It is a relatively simple and short code. If you check the source code, you will see that there are parameters you can customize.  Here are the list of the parameters that you can customize:

  • View: You can define a new View besides the Error to render for the error message. If you don’t specify a View name, default View name is Error
  • Master: You can define a master page for the View that will be rendered. If you don’t specify a master name, that it is empty by default, which means it will use the default master page.
  • ExceptionType: The exception type that you want to handle, by default it is the very generic System.Exception which means catch all the exceptions.

Note that there is another field you can customize however it is not a direct member of this class, but it comes with inheritance. 
Going back to our example, if we want to change error page name to let’s say “UnderMaintenance” , simple change your filter decoration to:

[HandleError(View="UnderMaintenance")]
public class HomeController : Controller


Or if you want to change the master page to Maintenance simply change your filter decoration to:

[HandleError(View="UnderMaintenance", Master="Maintenance")]
public class HomeController : Controller


Now let’s assume you want to redirect the user to different pages for different types of exceptions, then you have to use the ExceptionType property.

  • If the View you are trying to render is not found, then InvalidOperationException will be thrown.
  • If you are trying to connect to SqlServer, and there is an error, then System.Data.SqlClient.SqlException will be thrown

For other types of exception you can check MSDN, but let’s now concentrate on these 2 exceptions, and create 2 different error pages for 2 different types of Exceptions. Here is the right decoration for this goal:

[HandleError(View="Error", ExceptionType=typeof(InvalidOperationException))
[HandleError(View="DatabaseError",ExceptionType=
typeof(System.Data.SqlClient.SqlException)) public class HomeController: Controller


By this declaration we are telling the system that when there is an InvalidOperationException render the Error.aspx/ascx, when there is a SqlException  error, render DatabaseError.aspx/ascx view.

What happens when we add another HandleError that handles the SystemException which is basically all the exceptions, so if our declaration is like below, and assume SqlException is thrown:

[HandleError(ExceptionType=typeof(InvalidOperationException))]
[HandleError(ExceptionType=typeof(System.Data.SqlClient.SqlException))]
[HandleError(ExceptionType = typeof(SystemException))]
 public class HomeController : Controller


The framework will call  the all filters that can catch this type of exception, in this case, framework will call handlerror twice, one for the SqlException, and one for the SystemException.

You can write your own HandleError filter, and apply it to the controller or action. Inside this filter, you have to set the ExceptionHandled flag to true, so that framework won’t throw the yellow dead screen. If there are more than 1 filter that can catch the error message, all of them have to set the ExceptionHandled flag to true, or framework will throw the yellow dead screen.
The default HandleError attribute also creates a model for your view incase you want to get more information about the error, if you change your error page’s model to HandleErrorInfo, then you can get more information from the framework about the error. A simple example for the Error.aspx could be:

<%@ Page ..  Inherits="System.Web.Mvc.ViewPage<HandleErrorInfo>" %>
Sorry for the error, We are working hard on it to fix the problem.
<ul>
    <li>Action: <%=Model.ActionName %></li>
    <li>Controller: <%=Model.ControllerName %></li>
    <li><%=Model.Exception.ToString() %></li>
</ul>

 

Of course in the real work application, you don’t want to display this information to your user, but you can use this information to write a log to your log system.
Hopefully this article help you to understand HandleError attribute.

May the force be with you.



Tags: , , ,

E-mail | Permalink | Trackback | Post RSSRSS comment feed 0 Responses

Mar262010

T4MVC

    I presented ASP.NET MVC last month at San Diego .Net User group, I think overall it was a successful presentation. I got some good comments after the presentation, and yesterday, one member of the user group asked me my demo code for the T4MVC. Unfortunately I write my demos on the fly during my presentation ( I am not afraid to write code :) ), and usually 2-3 weeks after my presentation I wipe out the virtual pc that I use for the presentation. I don’t like to turn down people, I offered him to write a simple code that utilizes T4MVC, and then writing a blog that explains T4MVC came to my mind :)

While I am writing code, I am trying to avoid literal strings/magic numbers as much as possible and try to use strongly typed helpers/variables/etc instead. Obvious reasons are to have compile time support, intellisense,  easier maintainable code. Sometimes it is not that easy to replace the literal strings, such as if you have a link to a stylesheet, or an image source in the img tag, the strings are not easily replaced. Won’t you like the idea that the compiler will throw a compile time error when the css file is not found, or the image is not found?
Ex:

<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />


The above 1 line of html is taken from the sample mvc application you get, when you go File=>New App=>MVC App..  How are we going to replace the source of the css link with a strongly typed helper? You can do this in a few different ways, but the real easy way is to use an open source project called T4MVC.  You can find more information about this project at Codeplex 
Basically it is a T4 template that helps you to remove the magic strings from your MVC projects, and it runs on Visual Studio 2008 as well as Visual Studio 2010. As a quick example the html line above that adds the css could be converted into:

<link href="<%=Links.Content.Site_css %>" rel="Stylesheet" type="text/css" />


As you can see, we remove the magic string of “../../Content/Site.css” and put a strongly typed helper. Now  let’s download the files from the codeplex, and work on the project that Visual Studio creates when you create a new MVC application. The next thing is drag the the files “T4MVC.settings.t4” and “T4MVC.tt” to the root of your project into the solution explorer. Visual Studio will pop up a warning:
image

As these two files are script files that use using T4 template, the scripts will have full access to your project, and Visual Studio is friendly! warning. Just click OK and skip this window.  Even before you compile, if you check the solution explorer, there are more files generated by the T4 template.

image

T4MVC examined your project, created a list of all the magic strings, and  prepared strongly typed helpers. One of the generated files is T4MVC.cs and it has 2 classes for your use in your project. Scripts and Content classes (it is not a coincidence that you have these folders in the project), both are static classes under the namespace of Links.  These 2 classes are full with const strings. These const strings are used to replace your magic strings in your project, and if you add more magic strings in the code, just compile the project, and T4MVC will create the new const strings too.
Now if you check the html code above one more time, you will see that we use that Links namespace, and Content class. In this Content class, T4MVC converted the magic string to a const string named Site_css. 

Let’s assume we want to add the Jquery-1.3.2.js file to our site.master page, we could either use the old magic string way:

<script src="../../Scripts/jquery-1.3.2.js" 
        type="text/javascript" language="javascript" />    


or, you can use T4MVC to help us and write:

<script src="<%=Links.Scripts.jquery_1_3_2_js %>" 
          type="text/javascript" language="javascript" />


This time we used Links namespace and Scripts class, as you can notice the folder names become the class names.
To display another helper, I added a new image to this project with the name of “hour_glass.jpg”.  To display this image I can either use the magic strings such as:

<img src="../../Content/hour_glass.jpg" alt=""/>


or I can use T4MVC to and write this:

<img src="<%=Links.Content.hour_glass_jpg %>" alt="" />


Up to now we remove the magic strings for the image and scripts, and styles, let’s see what we can do with links. In the default project, we have to links in the master page:

<li><%= Html.ActionLink("Home", "Index", "Home")%></li>
<li><%= Html.ActionLink("About", "About", "Home")%></li>


Let’s rewrite these with T4MVC helpers:

<li><%=Html.ActionLink("Home",MVC.Home.Index()) %></li>
<li><%=Html.ActionLink("About",MVC.Home.About()) %></li>


So what happened here? T4MVC created a static class called Home, (it also created the static classes for Shared, and Account), and created the function Index() which basically calls the Index action  of HomeController. If the Index action has a parameter such as an integer then T4MVC will generated the code accordingly; an action that has a parameter of int:

public virtual ActionResult Index(int? id)


could be called like:

<li><%=Html.ActionLink("Home",MVC.Home.Index(5)) %></li>


So far, if you changed the signature of Index(), or delete the image , or change the name of the style you will get a compile error as they are all strongly typed helpers. Also Visual Studio helps you with intellisense to fill in those magic strings.
What if we want to remove the magic strings in the controllers too? Such as in the default MVC project, if you open the admin controller Logoff action is:

public virtual ActionResult LogOff()
{
    FormsAuth.SignOut();
    return RedirectToAction("Index", "Home");
}

This function has a magic string of “Index” and “Home”, we can use T4MVC to change these into strongly typed helpers too such as:

public virtual ActionResult LogOff()
{
   FormsAuth.SignOut();
   return RedirectToAction(MVC.Home.Index());
}


How about the routes in the global.asax.cs file? An example route:

routes.MapRoute(
   "Default",                                             
   "{controller}/{action}/{id}",                          
    new { controller = "Home", action = "Index", id = "" } 
 );


and this is how it is replaced using T4MVC:

routes.MapRoute("t4mvc",
  "{controller}/{action}/{id}",
   MVC.Home.Index()
 );

And the form we have in the Logon.aspx page of the AccountController:

<% using (Html.BeginForm()) { %>


could be converted into:

<% using (Html.BeginForm(MVC.Account.LogOn())) { %>


T4MVC works in routes, in views, in controllers, almost anywhere :), it has some problems too such as if you create a new folder, and add content to that folder, T4MVC won’t recognize this new folder, however it has good community support and these small items may be fixed, it also has some other support for MVC 2.0 and you can check these from its codeplex website.



Tags: ,

E-mail | Permalink | Trackback | Post RSSRSS comment feed 4 Responses

Mar192010

TempData in MVC 1.0

    TempData is an interesting structure in MVC 1.0, and could be a little problematic if you don’t know the internals of it while using, so in MVC 2.0 TempData structure is changed. Today I want to analyze MVC 1.0’s TempData structure.

TempData is used to store information that will be consumed only once. A good example could be some information/error messages that will be passed to the view, and if the user refreshes the page, the message will be gone. For example:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Order order)
{
    if(order.Validate()==false)
    {
        TempData["Msg"]="There are problems with order, please fix'em";
        return RedirectToAction("ValidationError");
    }
    //... some save to database action 
   TempData["Msg"]="Your order will be shipped ASAP";
  return RedirectToAction("OrderComplete");
}

As you can see in the code above, we are passing a message to one of the actions informing about the status of the order. If the user refreshes the page, TempData[“Msg”] will be cleared and the message will be gone. How does MVC 1.0 accomplish this? Basically, it stores the the value in the session, and after the roundtrip it deletes the value, but more technically:
If you open the MVC source code, and go to the Controller.cs file line 100, you will see that the controller has a member called TempDataProvider which is by default a SessionStateTempDataProvider. This provider implements an interface that has the below signature:

public interface ITempDataProvider {
  Dictionary<string, object> LoadTempData(ControllerContext controllerContext);
  void SaveTempData(ControllerContext controllerContext, 
IDictionary<string, object> values); }

So basically its job is to load or save a dictionary, and the SessionStateTempDataProvider is using the Session to load or save the dictionary. This dictionary is storing your temp values. 
Let’s check what SessionStateTempDataProvider LoadTempData does. The first thing the provider does is to get the dictionary from the controllercontext’s session. If the session does not return a value for the dictionary (in case you haven’t used the TempData in your code , then the session value is empty), then a new Dictionary<string,object> is instantiated and returned back to the caller.
If the session value has a value for the dictionary, then it is taken from the session, the session value is cleared, and dictionary is returned. After this point, there is no value for the dictionary in the session, so it is caller’s job to save the dictionary back to the session.
Here is the code from the mvc source code:

public virtual IDictionary<string, object> LoadTempData(ControllerContext 
controllerContext)
{ HttpContextBase httpContext = controllerContext.HttpContext; if (httpContext.Session == null) { throw new InvalidOperationException
(MvcResources.SessionStateTempDataProvider_SessionStateDisabled); } Dictionary<string, object> tempDataDictionary =
httpContext.Session[TempDataSessionStateKey] as Dictionary<string, object>; if (tempDataDictionary != null) { // If we got it from Session, remove it so that no other request gets it httpContext.Session.Remove(TempDataSessionStateKey); return tempDataDictionary; } else{ return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); } }


SaveTempData is an easy operation, just take the passed the Dictionary and save it to the controllercontext’s session.  So far what we have seen is just a provider that stores a dictionary to the session, and reads a dictionary from the session. How does the controller use this provider to store and load the temp values? If you open the ControllerBase.cs file and go to line 21, you will see that the ControllerBase has a public property TempData of TempDataDictionary type. One of the problem is happening in this area. This dictionary has a Load and Save public functions, that uses SessionStateTempDataProvider (by default), to save and load the values. In order to do that, it uses 2 HashSets, one for the initial keys (the values that come back with the post back), the other HashSet is either for the keys that are newly added to the Dictionary or modified keys from the initial keys. By this way, the dictionary keeps track of who is added, who is supposed to be removed, who is updated.

The controller executes ExecuteCore() to invoke the action. The first line in this function is TempData.Load. This is where the controller asks the TempData dictionary to load the dictionary, and TempData dictionary uses SessionStateTempDataProvider to load the dictionary from the session. The last line of the ExecuteCore is TempData.Save which simply takes the dictionary back from the controller, uses SessionStateTempDataProvider and saves it to the session. Do not forget that the unmodified values are removed from the dictionary when the Save method is called.

What are the problems with this structure?
First of all TempData by default stores the dictionary in a session, and once it is read, it cleans the session. What if the user opens a new tab right before your TempData value is read by page? Session is shared between the tabs of the same browser, so TempData may lose the data in the session. The same problem may occur when there is an ajax call to the controller, or there is a redirecttoaction. Because of these problems MVC 2.0 has fixed some issues with TempData and we will look into the source for MVC 2.0 in a later posting.



Tags:

E-mail | Permalink | Trackback | Post RSSRSS comment feed 0 Responses

Mar152010

HashSet

    It is been sometime I start using and enjoying HashSet as a collection in C#. Especially if you are using NHibernate as an ORM solution, you will see HashSet in one-to-many relations in your POCO objects. 
HashSet<T> lives at System.Collections.Generic namespace. It is a collection of T that you can add/remove/contains etc elements but one of the most important feature of it is, having only the unique elements. So simply if you already have “1” in the collection, and try to add the same number again, it simply won’t be added to the collection. The collection is not ordered, and uses hash algorithm the complexity of add/remove/contains is O(1) (if you don’t know what it is, you can not be faster than this). Let’s work on some sample codes and check the behavior of this collection.
In our first application, let’s create a HashSet<int> , add some numbers and print them to the console.

HashSet<int>numbers = new HashSet<int>(); numbers.Add(1); numbers.Add(3);
numbers.Add(4); numbers.Add(2);
numbers.Add(1); numbers.Add(3);

foreach (var number in numbers)
{
  Console.Out.WriteLine("{0}",number);
}

 

In the code above, I created a HashSet<int> added the numbers, 1,3,4,2,1,3, then iterated through the collection and printed them to the console.The output is: “1,3,4,2” as the other repetitive numbers are not added to the collection. Actually Add<T>() returns a boolean value. If the value is added to the collection it returns true, else false.  How about when we have reference types instead of values types. This time let’s create the class “Person” with 2 simple property, and add it to the Hash collection.

Person person1 = new Person {ID = 1, Name = "person1"};
Person person2 = new Person {ID = 1, Name = "person1"};
Person person3 = new Person {ID = 2, Name = "person1"};
HashSet<Person> persons = new HashSet<Person>();
persons.Add(person1);
persons.Add(person2);
persons.Add(person3);
foreach (var person in persons)
{
   Console.Out.WriteLine(person);
}
.....
public class Person
{
   public int ID { get; set; }
   public string Name { get; set; }
   public override string ToString()
   {
        return String.Format("{0}-{1}", ID, Name);
   }
}

In this example I created 3 person objects, and 2 of them has the exact same values for the properties, added them to the collection, and print all of them to the console. The result is we have all 3 objects in the collection. When we called Add<person> the hash collection, checks if the value exists in the collection or not. The way it works, is, it uses the default IEqualityComparer to figure out the equality. In our sample, as we newed up all the objects, they are all different objects, so how can we change the code that, if the value of the ID fields are same, the objects are treated as the same object, and won’t be added to hash again. You may already guess the answer, we have to write an IEqualityComparer for our person object, and pass this to the hash collection. Here is the new updated code:

Person person1 = new Person {ID = 1, Name = "person1"};
Person person2 = new Person {ID = 1, Name = "person1"};
Person person3 = new Person {ID = 2, Name = "person1"};
HashSet<Person> persons = new HashSet<Person>(new PersonComparer());
persons.Add(person1);
persons.Add(person2);
persons.Add(person3);
foreach (var person in persons)
{
   Console.Out.WriteLine(person);
}
....
public class PersonComparer:IEqualityComparer<Person>
{
   public bool Equals(Person x, Person y)
   {
       return x.ID == y.ID;
   }
    public int GetHashCode(Person obj)
    {
       return obj.ToString().GetHashCode();
     }
}
...
public class Person
{
   public int ID { get; set; }
   public string Name { get; set; }
   public override string ToString()
   {
        return String.Format("{0}-{1}", ID, Name);
    }
 }

 

This way, we can still ensure that all reference typed objects are unique too in the collection.

As HashSet is a set :), you can use some of the set functions too, and LINQ has 2 interesting extensions methods: Union and Intersect. Here is a sample code that uses these two extension methods:

HashSet<int> numbers1 = new HashSet<int> {1, 2, 3, 4, 5, 6, 7};
HashSet<int> numbers2 = new HashSet<int> {10, 9, 8, 7, 6, 5, 2};

IEnumerable<int>intersect = numbers1.Intersect(numbers2);
IEnumerable<int> union = numbers1.Union(numbers2);

As you can see, HashSet is really powerful, and useful.



Tags: ,

E-mail | Permalink | Trackback | Post RSSRSS comment feed 0 Responses