Site Meter
 
 

Monthly Archives: February 2009

xVal 0.8 (Beta) Now Released

This morning I’ve released xVal 0.8 (Beta). It’s a fairly substantial update – it adds the following features:

  • Support for NHibernate.Validator (in addition to the existing support for System.ComponentModel.DataAnnotations and Castle Validator)
  • Internationalization of default client-side messages
  • Much easier to implement custom validation logic (server-side and client-side)
  • Fluent syntax for defining ad-hoc validation rules directly in a view
  • Support for comparison validators (e.g., “PasswordConfirm” must equal “Password”)
  • Works with ASP.NET MVC Release Candidate
  • Better performance (rules are now cached per model type)
  • A few bugfixes

To recap, xVal is a validation framework bridge for ASP.NET MVC. It lets you use whatever server-side validation technology you prefer (and there are plenty that work well with ASP.NET MVC), and then xVal makes client-side validation happen automatically. On the client, you can enable either jQuery.Validation or ASP.NET (WebForms-style) native validation, or you can write your own plugin.

image

This is a “beta” quality release, in that the feature set is more or less right for 1.0. The short term goal now is not to add any more major features, but instead to focus on providing proper documentation (instead of just blog posts) and make sure it works robustly in all circumstances.

Now I’ll quickly explain the new features in 0.8.

Support for NHibernate.Validator

If you’re using NHibernate, you might also be using NHibernate.Validator. It’s a server-side validation framework that plugs directly into NHibernate’s pipeline, so you can be sure objects never reach the database unless they satisfy your rules.

xVal can now detect NHibernate.Validator rules and translate many of them into client-side rules. To make this work, assuming you already have an application that uses NHibernate.Validator, add a reference to xVal.dll and  xVal.RulesProviders.NHibernateValidator.dll and then enable it in your Global.asax.cs file:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    xVal.ActiveRuleProviders.Providers.Add(new NHibernateValidatorRulesProvider(ValidatorMode.OverrideXmlWithAttribute));
}

If you want to use jQuery.Validator on the client, then add the following references to your master page:

<head runat="server">
    <asp:PlaceHolder runat="server">
        <script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery-1.3.2.min.js") %>"></script>
        <script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery.validate.js") %>"></script>
        <script type="text/javascript" src="<%= Url.Content("~/Scripts/xVal.jquery.validate.js") %>"></script>
    </asp:PlaceHolder>
</head>

Or, if you want to use ASP.NET Native validation on the client, then add the following references to your master page instead:

<head runat="server">
    <asp:PlaceHolder runat="server">
        <script type="text/javascript" src="<%= Page.ClientScript.GetWebResourceUrl(typeof(System.Web.UI.Page), "WebForms.js") %>"></script>
        <script type="text/javascript" src="<%= Page.ClientScript.GetWebResourceUrl(typeof(System.Web.UI.Page), "WebUIValidation.js") %>"></script>
        <script type="text/javascript" src="<%= ResolveUrl("~/Scripts/xVal.AspNetNative.js")%>"></script>
    </asp:PlaceHolder>
</head>

Finally, apply client-side validation in any view by using Html.ClientSideValidation():

<%= Html.ClientSideValidation<member>() %>

Note that before Visual Studio will recognize Html.ClientSideValidation(), you must either add <%@ Import Namespace="xVal.Html" %> to the top of your view, or reference the namespace globally in your web.config file:

<system.web>
  <pages>
     <namespaces>
        <!-- leave rest as-is -->
        <add namespace="xVal.Html"/>
    </namespaces>
  </pages>
</system.web>

So now, if your model classes define NHibernate.Validator rules, such as:

public class Member
{
    public virtual int MemberID { get; internal set; }
 
    [NotNullNotEmpty] [Length(3, 50)]
    public virtual string Name { get; set; }
 
    [NotNullNotEmpty] [Email] [Length(200)]
    public virtual string EmailAddress { get; set; }
}

… then those rules will be applied on the client:

image

Note that NHibernate.Validator support is pretty early at the moment. I’m interested to get any feedback about how the client-side interpretation of the rules could be improved (for example, should Date properties automatically be required?).

Internationalization of default client-side messages

It’s always been possible to internationalize the server-generated validation messages – just nominate the resource type and resource string name when defining a rule:

[Required(ErrorMessageResourceType = typeof(MyResourceType), ErrorMessageResourceName = "ValueRequired")]

But it’s inconvenient to have to type that out every time. Wouldn’t it be better if you could also internationalize the default client-generated messages too? Now you can.

To try this out, download the demo project and also the latest xVal release. Copy xVal.Messages.en-US.js from the xVal release into the demo project’s ~/Scripts/ folder, and then reference it from the master page:

<head runat="server">
    <asp:PlaceHolder runat="server">
        <!-- Leave the other references as they are -->
        <script type="text/javascript" src="<%= Url.Content("~/Scripts/xVal.Messages.en-US.js") %>"></script>
    </asp:PlaceHolder>
</head>

If you run the project now, it will appear that nothing’s changed. However, simply by referencing xVal.Messages.en-US.js, you’ve told xVal to use messages defined in that file instead of using its built-in defaults. If you want to change the English messages, just edit xVal.Messages.en-US.js.

To support another language, make a copy of xVal.Messages.en-US.js, rename it to refer to the other language (e.g.,  xVal.Messages.es-ES.js), put that copy in your ~/Scripts/ folder, and then edit the messages so they’re in the other language.

image

Now, change the reference in your master page:

<script type="text/javascript" src="<%= Url.Content("~/Scripts/xVal.Messages.es-ES.js") %>"></script>

And that’s it! You’ve now got Spanish messages:

image

If you want to change the language dynamically at runtime, you should add some code that varies which xVal.Messages.*.js file gets referenced, perhaps according to the current thread culture.

Update: Thanks to the community for contributing a comprehensive set of default translations. Language files for Brazilian Portuguese, Danish, Spanish, Dutch, Polish, French, German, and Swedish will be included in the next release.

Implementing Custom Validation Logic

You could previously create custom server-side validation logic by subclassing whatever base class your server-side validation framework uses for validation rules. For System.ComponentModel.DataAnnotations, that means subclassing ValidationAttribute. For Castle Validator, it means making an attribute that implements IValidator.

Now, whatever server-side validation framework you use, you can also make custom rules run custom client-side logic by implementing xVal’s ICustomRule interface.

To try this out, download the demo project and add a new class to the DomainModel project called DivisibleByAttribute:

public class DivisibleByAttribute : ValidationAttribute, ICustomRule
{
    private int Divisor { get; set; }
    public DivisibleByAttribute(int divisor)
    {
        Divisor = divisor;
        ErrorMessage = string.Format("Must be a multiple of {0}.", divisor);
    }
 
    public override bool IsValid(object value)
    {
        if(!(value is int))
            return false;
        return ((int)value) % Divisor == 0;
    }
 
    public CustomRule ToCustomRule()
    {
        return new CustomRule(
            "DivisibleByN",            // JavaScript function name
            new { divisor = Divisor }, // Params for JavaScript function
            ErrorMessage               // Message if invalid
        );
    }
}

Since this derives from ValidationAttribute, your DataAnnotations runner will enforce it on the server when you attach it to some model property:

[Required] [Range(1, 20)] [DivisibleBy(4)] public int NumGuests { get; set; }

To run similar logic on the client, implement a JavaScript function called DivisibleByN:

<script type="text/javascript">
    function DivisibleByN(value, element, params) {
        return value % params.divisor == 0;
    }
</script>

Note that you should put this before any call to Html.ClientSideValidation(). Now, you’ll get client-side and server-side validation of your custom rule:

image

If you want to internationalize your custom rule’s message, then alter DivisibleByAttribute’s ToCustomRule() function so that it uses the CustomRule constructor that lets you specify a resource type and a resource string.

Defining Ad-Hoc Validation Rules in a View

xVal focuses on model based validation. That means you normally define rules in your model layer. However, sometimes it’s useful to throw in a few extra rules that apply only to a specific MVC view. There’s now a quick and easy way of doing this.

To try it out, download the demo project, go to the PlaceBooking.aspx view, and then edit the Html.ClientSideValidation() call to the following:

<%= Html.ClientSideValidation<booking>("booking")
    .AddRule("Name", new RegularExpressionRule("^[a-z]{3}\\d{3}$", RegexOptions.IgnoreCase) {
                        ErrorMessage = "Must be three letters followed by three numbers."
                     })
    .AddRule("BookingDate", new RangeRule(DateTime.Now, null) {
                                ErrorMessage = "Must be in the future."
                            })
%>

(Note: you’ll also need to add a <%@ Import Namespace="xVal.Rules"%> directive to the top of the view.)

Now, your view’s ad-hoc rules will be combined with the model rules:

image

If you’re the sort of person who doesn’t like model based validation at all, you could use this feature to build your complete rules configuration inline in a view. Just start with a RuleSet.Empty and add ad-hoc rules to it:

<%= Html.ClientSideValidation("booking", RuleSet.Empty)
     .AddRule("Name", new RequiredRule())
     .AddRule("BookingDate", new RequiredRule())
     .AddRule("BookingDate", new DataTypeRule(DataTypeRule.DataType.Date))
     /* Etc */
%>

The advantages of model-based validation are clear (DRY), but this gives you extra flexibility that might come in handy.

What next?

The next step is to let people start using this and ask for feedback about how it could be improved. I’ll shy away from adding major new features right now because I don’t personally have time to manage an enormous project. I’m more interested in any bugs you might find or ways in which the current feature set could be applied better. There are already a few issues registered on Codeplex that I’ll be attending to, or better still, you could submit a patch!

Just a reminder: if you’re able to provide translations of the language file into any other major language, and if you’re happy for me to include your translation in the next release, please send it to me.Update: Thanks to everyone for all the translations. We’ve now covered all the main ones, so no more are needed.

With thanks to MarkJPerry, DavidHayden, sjmueller, trevor, goneale, imm102, lobex, DerekMcDermott, richardu, Peter Mounce, Billy McCafferty, Mike Saunders, Andrew Scott, Vadim, and various others who have provided useful feedback on the previous release.

kick it on DotNetKicks.com

Using the ASP.NET MVC source code to debug your app

Most ASP.NET MVC developers know that the framework source code is available. This means you can:

  • Read the source code to see how things work
  • Edit, compile, and use your own custom build with your own modifications (though this is almost certainly not necessary, as the framework gives you plenty of extensibility options)

Instead of just reading the MVC Framework source code in the abstract, one of the best ways of understanding what’s going on is to put it directly into your application’s solution. You can include the System.Web.Mvc C# project in your solution (as if you created it!), and then handily step into and out of its code while debugging, or use Visual Studio’s Go to Declaration command (or ReSharper’s) to navigate from your framework class references directly into the source. Personally, I find this very useful.

Attaching the System.Web.Mvc source code to your solution

Here’s how to include the System.Web.Mvc project source in your solution:

  1. Download the ASP.NET MVC source code from CodePlex (be sure to get the version that corresponds to whatever version of ASP.NET MVC your app already uses). Extract the ZIP file to some convenient location.
  2. Add System.Web.Mvc as an existing project. In Visual Studio, open your existing application, then go to File –> Add –> Existing Project. Find and select System.Web.Mvc.csproj, which will be in the source code you just extracted from the ZIP file, probably in the MVC\src\SystemWebMvc subdirectory. You’ll now have System.Web.Mvc in Solution Explorer:
    image
  3. Remove your app’s existing reference to System.Web.Mvc. Just find it in the list of references, right-click it, and choose Remove.
    image 
    This eliminates the reference to the GAC version of System.Web.Mvc (the one that was registered when you first installed ASP.NET MVC).
  4. Add a project reference to the source code version of System.Web.Mvc. Right-click on your app in Solution Explorer, choose Add Reference, go to the Projects tab, and select System.Web.Mvc.
    image 

That’s it! You can now compile and run your application, and you’ll have a live reference to the framework source code, so you can step in and out of it, and can jump directly from any reference in your own source code to the corresponding point in the System.Web.Mvc source code. This can make debugging much easier.

Stop being so ambiguous!

Actually, there is a bit of a snag. If you have view pages without code-behind files (which is the default, since the Release Candidate), then when you try to run your app, you’ll get the following error:

image

You’ve now got one self-compiled instance of System.Web.Mvc.dll in your application’s \bin folder, plus the official original copy in your GAC from when you first installed ASP.NET MVC. So how is the framework supposed to decide which one to use?

Well, the only reason it considers the GAC version is that your web.config file tells it to. Go and edit your web.config file and comment out the System.Web.Mvc GAC-version reference:

image

Now it will only consider the version in your \bin folder, so the ambiguity ("Parser Error”) will go away.

Update: Wait, there’s more! Another config change you need to make – if you want to run a custom build of ASP.NET MVC – is in /Views/web.config (note: that’s not your top-level web.config). Since the RC, strongly-typed views depend on a clever page parser filter. Find this string:

pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"

… and change it to:

pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

That just tells the page compiler that it can use your custom build of ViewTypeParserFilter (and that it doesn’t have to use the GAC version). If you don’t, then strongly-typed views will throw the error “‘object’ does not contain a definition for ‘YourModelType‘”.

What about deployment?

When you come to deploy your app to another server, remember that your web.config file now doesn’t reference the GAC version of System.Web.Mvc, and the rest of your code is compiled against your custom build. To make it work on another server, you can either:

  • Deploy your custom build of ASP.NET MVC in the \bin folder. Your custom build of the assembly will already be in your \bin folder, so this is what will happen automatically.
  • Reverse the steps you just took above (which means manually editing your app’s .csproj file to reintroduce a reference to the GAC version of System.Web.Mvc, rather than the project reference). You could probably make this part of your automated build process.

I have found this technique extremely useful when trying to understand what the MVC Framework is actually doing with my application.