Thursday, July 30, 2009

NHibernate and ASP.NET - UpdateModel problem

Using NHibernate as a persistence layer is fun. Not only does it hide the complexity of a database completly but also allows one to use an object model instead of playing around with database query results.
In the case of NHibernate it's even better as it provides session management for web applications so with minimum effort you can just use lazy loading from within your views very easily.

At some point though problems will emerge. One of them is using ISession.Get without specifying the type (which might be unknown at the time of querying or might depend on the actual request from the page) with Controller.UpdateModel method. Here's how it goes:

1. UpdateModel is a generic method that expects the actual type that's being mapped to as the generic parameter.
2. It then passes the processing to TryUpdateModel which is a generic method as well.
3. TryUpdateModel takes this generic parameter, retrieves it's type metadata and uses it (among other things) to fill in a ModelBindingContext structure.
4. This ModelBindingContext is then passed on to IModelBinder.BindModel method.

It's all nice and dandy if we can specify the model's type. But if we don't know the actual type it results in processing of a model of type Object and none of the properties will get mapped. Because of the fact that there's no specialized model binder for type Object it gets passed on to the DefaultModelBinder implementation held by ModelBinders.Binders.DefaultBinder.

There's however an extremly nice and easy solution to this. We're going to use the decorator pattern to wrap up the current default model binder and provide it with the necessary information from NHibernate's ISessionFactory.

Here's an example implementation of such a model binder that can provide this additional implementation. I've called it NHibernateAwareModelBinder.


public class NHibernateAwareModelBinder : IModelBinder
{
private readonly ICollection mappedClasses;
private readonly IModelBinder defaultModelBinder;

public NHibernateAwareModelBinder(
ICollection mappedClasses,
IModelBinder defaultModelBinder)
{
this.mappedClasses = mappedClasses;
this.defaultModelBinder = defaultModelBinder;
}

public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
{
string modelType = bindingContext.Model
.GetType().FullName;
if (mappedClasses.Contains(modelType))
{
bindingContext.ModelType =
Type.GetType(modelType);
}
}
return defaultModelBinder.BindModel(
controllerContext,
bindingContext);
}
}

And here's how you'd use this class in your Application_Start event:

ModelBinders.Binders.DefaultBinder =
new NHibernateAwareModelBinder(
SessionFactory.GetAllClassMetadata().Keys,
ModelBinders.Binders.DefaultBinder);

As you can see we're using the current default model binder as the actual binder but we're decorating it's behavior with type resolution. Nice, isn't it?

Have fun!

Saturday, July 11, 2009

Localizing ASP.NET pages using resources

Localizing ASP.NET pages using resource files is an easy thing to do. It requires just a few steps to get everything up and running but all of those steps have to be completed because otherwise it will not work.

  • Set the UICulture and Culture attributes on a page to "Auto"
  • Create an ASP.NET folder named App_LocalResources
  • In App_LocalResources create a resource file using the following pattern:
page.ext.lang-variant.resx

for example:

Default.aspx, en-us -> Default.aspx.en-us.resx
Default.aspx, pl -> Default.aspx.pl.resx

  • On the element that needs localization set the attribute meta:resourcekey to something meaningful, for example:

<asp:Label ID="LblText" Text="Hello, world!" runat="server" />

becomes

<asp:Label ID="LblText" Text="Hello, world!" meta:resourcekey="LblText" runat="server" />

  • Add resource key for the property to modify using the following pattern:

resourcekey.property

for example:

LblText.Text

  • DO NOT FORGET TO REBUILD AND RE-RUN THE APPLICATION!


Here's a complete and working solution for you to play around

ASP.NET-Localization.zip

Have fun!

Thursday, June 11, 2009

StyleCop and FxCop integration in VS 2008 Express

Recently I took interest in improving the quality of the applications I write by forcing them to be checked by StyleCop and FxCop during the build.

StyleCop was really easy to implement - just one line, well documented, nothing one would struggle with:

1. Install StyleCop using MSI installer
2. Add the following line to your project right after the line that imports C# tasks (by default it's nearly at the end of the project file!):

<Import Project="$(ProgramFiles)\MSBuild\Microsoft\StyleCop\v4.3\Microsoft.StyleCop.targets" />

FxCop on the other hand wasn't so easy to find out but it was just as easy to manage:

1. Install FxCop using MSI installer
2. Add the following task to <AfterBuild> target:

<Exec Command='"$(ProgramFiles)\Microsoft FxCop 1.36\FxCopCmd.exe" /file:"$(TargetPath)" /console'/>

That's it. Alternatively you could just add a post-build event using project options editor. This event would then look like that:

"%ProgramFiles%\Microsoft FxCop 1.36\FxCopCmd.exe" /file:"$(TargetPath)" /console

Pretty easy, ain't it?

Here you have a ready-to-check example of the above.

FxCop+StyleCop-example.zip

Enjoy!

Thursday, June 4, 2009

Mercurial - using "hg serve" to push changes

I've just discovered Mercurial. It's amazing!!! It's a distributed version control system, similar in its flows to git and others from the bunch so if you're already familiar with the concept you'll have no issues working with it.

One thing however is really amazing: it contains a built-in web user interface! Just while being inside the repository type "hg serve" and browse to http://localhost:8000. And there's more to it. "hg serve" can serve as a real server!

There's one thing worth mentioning. If you'd like to use it for hosting a repository there's one thing you have to add to .hgrc (or Mercurial.ini in case you're working in Windows) to make it work:

[web]
allow_push = *
push_ssl = false

Otherwise while trying to push the changes you'll get a message saying something SSL and stuff.

And like I said - Mercurial is great - go check it out!

Sunday, May 31, 2009

Flexigrid

Did you ever wanted to have an all-in-one solution for grids in your application? One that does Ajax refreshes, supports sorting, filtering and pagination out of the box. How about an API that's actually usable?

I've got some good news for you - it's there! It is called Flexigrid and is in fact a plugin for jQuery.

For the purpose of demonstration I've put together an example that does pretty much everything I've mentioned above.

FlexigridExample.zip

Here are some points worth noticing in the provided example:

1. The CSS in Internet Explorer is trashed. I don't know how to fix it.
2. The real power that feeds the data is to be found in BookController and is well documented.
3. Since there's not much documentation for Flexigrid itself I suggest taking a look at the list of options in flexigrid.js.

Enjoy this fantastic library!

Wednesday, May 20, 2009

Creating custom controls in ASP.NET MVC

Today I've been going over the process of creating a simple ASP.NET MVC control. At the first glance it might seem unnecessary since we have the HtmlHelper class. However the main difference is that MVC Controls have superior design time support!

Let's get down to business, shall we?

First you need to import Microsoft.Web.Mvc.dll assembly to your project.
Next you create a class that inherits from Microsoft.Web.Mvc.Controls.MvcControl. I put my controls into Controls folder in my solution so my controls are in the namespace (for example) MvcApplication1.Controls.
The actual creation of the control is really simple: you override the Render method and use the writer object to pass on some text to the rendering pipeline.
Here's a ready-to-use example:

using System;
using System.Web.Mvc;
using Microsoft.Web.Mvc.Controls;

namespace MvcApplication1.Controls {
public class Status : MvcControl {
public String Key { get; set; }

protected override void Render(System.Web.UI.HtmlTextWriter writer) {
TagBuilder tag = new TagBuilder("span");
String data = String.Empty;
if (!DesignMode)
data = ViewData[Key] != null ? ViewData[Key].ToString() : "";
else
data = "#" + Key;
tag.SetInnerText(data);
writer.Write(tag.ToString());
}
}
}

Remember to compile your project before moving on to the next step!!!

Next you need to do one thing to get your controls to the page:

Register your namespace with prefix for ASP.NET WebForms engine by adding the following line to your Web.config file in section system.web/pages:

<add tagPrefix="custom"
namespace="MvcApplication1.Controls"
assembly="MvcApplication1"/>

From now on (after a few moments when VS/VWD thinks if your control is worth using :D) you'll get the IntelliSense support for your new control with all the public properties it has.

Here's an example usage of the control described above:

<custom:Status runat="server" Key="Example" />

As you can see this control is rendered by the server (runat="server") and has one property (or rather an attribute) called Key. Taking a look at the code reveals that the actual meaning of this field is that it serves as a key in ViewData map.

Bye!

"Error creating control" in VS and VWD

There's a nasty bug in VisualStudio 2008 SP1 and Visual Web Developer 2008 SP1 that makes the usage of custom controls virtually impossible. Everytime you try to set a property (via Properties or directly in code) you'll get a gray error message saying that the value of your property cannot be set.

This is a known issue and there's a simple remedy to that. Close VS/VWD, install this hotfix, start VS/VWD again and you're good to go.

http://code.msdn.microsoft.com/KB961847/Release/ProjectReleases.aspx?ReleaseId=2646

Happy coding!