The System.ComponentModel.DataAnnotations assembly added with .NET 3.5 SP1 is quite useful. It contains various attributes that allow you to specify various constraints on the properties of your class. Ideal for model classes; constraints on the data are kept inside your model, nice and succinctly. However ASP.NET MVC version 1 does not contain out of the box server side support for them. Version 2 will but I can't wait that long... Whilst you can use something like the excellent xVal for client side validation of data annotations being a good programmer you'll also want to have the same validation server side in case someone turns off JavaScript in their browser.
Sadly I haven't been able to find a good example of how to do this. The examples I have found tend to require manual calls to some method to perform the validation, such as this one in Steve Sanderson's blog about how to use xVal. I don't want to have to do an extra manual call for my validation; I might forget! The ideal would be a model binder that is aware of the data annotations; I could set it as the default binder and forget about it. The only example of a model binder I could find was in the ASP.NET site on CodePlex but that required a newer version of the DataAnnotations assembly (basically the .NET 4.0 one) which seemed like overkill to me. How hard can it be to write one?
Turns out not very... All you have to do is:
protected override bool OnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
// Run everything by the default implementation first before checking data annotations.
var isValid = base.OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, value);
// Loop through any validation attributes.
foreach (var validationAttribute in propertyDescriptor.Attributes.OfType<ValidationAttribute>())
{
// Are we valid?
if (!validationAttribute.IsValid(value))
{
// No. Let's add the error to the model state and set our isValid status to false.
string key = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
// Add the error to the model state.
bindingContext.ModelState.AddModelError(key, validationAttribute.FormatErrorMessage(propertyDescriptor.DisplayName)));
isValid = false;
}
}
return isValid;
}
Okay, it's not quite as simple as that. You also have to take into account any MetadataTypeAttributes that might be found on your model class. This attribute allows you to specify another type to put your metadata on. You can specify identical properties on this other type and decorate them with the DataAnnotations attributes instead. Pointless? Well yes, it should be really; why not just decorate your properties? However it comes in useful if you're working with auto-generated code that doesn't allow you to add attributes, such as the evil and crappy LINQ to SQL designer. By using the MetadataTypeAttribute you can create a partial version of your LINQ to SQL entity class, add the attribute to that and specify constraints for the auto generated properties in some other class. (Whether you should be using LINQ to SQL entities as your model is an ethical debate I am going nowhere near)
If you can't be bothered to do all this of course then please feel free to download one I made earlier. This binder takes into account MetadataTypeAttributes and I've even thrown in a demo project so you can see the thing in action. Enjoy!
Code that relies on the order of things generally annoys me. It tends to be weak, fragile code because someone somewhere will come along and change the order. Most of the time nothing to bad will happen. Names in a drop down will no longer be alphabetical, that sort of thing. But I've seen code 'oh crap yellow screen of death' break due to people relying on the order of data. The code they'd written depended on the order of items coming out of the database, however they didn't have an ORDER BY clause in their SQL. So one day someone inserted a record in the table, the data came back differently, their code couldn't handled that and a lovely NullReferenceException graced the front page of the website. Not good. If your code must depend on order then your code should make sure things are in order!
I found order behaviour in the wild yesterday, in the RSS Best Practices Profile. Whilst using the W3C's Feed Validator to check the markup of this site's RSS was up to scratch I came across the recommendation that 'all item elements should appear after all of the other elements in a channel.' Why? Perhaps it can be used as an optimisation for some RSS parsers, although if that's the case it should probably be compulsory rather than a recommendation because then something will depend on that fact and possibly break if that isn't the case in some feed. The only valid reason I can think of is that it makes reader the XML by hand slightly easier. If anyone knows the real answer please drop me a line; it's annoying the hell out of me...
I've been using the Search Engine Optimization Toolkit add-in for IIS 7 to analyse this website. If you've not used it before I recommend you give it a try; it's very good for picking up random bugs, spurious URL routing and dodgy links that you'd never have spotted otherwise. For example it would report issues on this site whereby the same content was accessible by two different URLs. Turns out some links would have a trailing slash, some not. (Mainly due to ASP.NET MVC's ActionLink being rubbish and missing off trailing slashes. Why does it not return links in the format specified by the route???) The SEO toolkit picked them up and now they're hopefully all fixed.
One set of problems I had trouble fixing was the root URL to this site, http://www.mrkwatkins.co.uk/. Currently I only have a blog so that URL redirects to the root URL of my blog, http://www.mrkwatkins.co.uk/Blog/. However the SEO Toolkit would report errors with the content returned from the redirect. For example it would tell me that there was no <h1> tag in the content. Now whether or not the toolkit should be analysing the content returned from a redirect or not I have no idea. What I do know is that they were the last errors on my report and needed removing!
The redirect was done using the standard RedirectResult in ASP.NET MVC. This in turn uses the standard Response.Redirect from ASP.NET. Trouble is that Response.Redirect spits out the following content as well as the 302 status code and location HTTP header:
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="http://www.mrkwatkins.co.uk/Blog/">here</a>.</h2>
</body></html>
So to fix the problems we need to change this XHTML. Turns out that is pretty simple to do. You can return whatever content you like from the request as normal. All you need to do is set the status code to 302 using the Response.StatusCode property and set the Location HTTP header using the Response.RedirectLocation property. I've wrapped all this up in a custom ViewResult class:
using System.Web;
using System.Web.Mvc;
using KWatkins.Validation;
namespace KWatkins.MrKWatkins.Web.Mvc
{
/// <summary>
/// A <see cref="ViewResult" /> that redirects the user to another location; allows
/// you to customize the XHTML returned by the redirection rather than use the
/// standard (invalid XHTML) retured by <see cref="HttpResponse.Redirect(string)" />.
/// </summary>
/// <remarks>
/// The redirect location is added to the <see cref="ViewRedirectResult.ViewData" />
/// with the key specified by <see cref="ViewRedirectResult.RedirectLocationViewDataKey" />.
/// </remarks>
public sealed class ViewRedirectResult : ViewResult
{
/// <summary>
/// The key used to store the <see cref="RedirectLocation" /> in the
/// <see cref="ViewRedirectResult.ViewData" />.
/// </summary>
public const string RedirectLocationViewDataKey = "RedirectLocation";
private readonly string redirectLocation;
/// <summary>
/// Initializes a new instance of the <see cref="ViewRedirectResult"/> class.
/// </summary>
/// <param name="redirectLocation">The redirect location.</param>
public ViewRedirectResult(string redirectLocation)
{
Validate.Argument(redirectLocation, "redirectLocation").IsNotNull().IsNotEmpty();
this.redirectLocation = redirectLocation;
ViewData[RedirectLocationViewDataKey] = redirectLocation;
}
/// <summary>
/// When called by the action invoker, renders the view to the response.
/// </summary>
/// <param name="context">The context within which the result is executed.</param>
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.StatusCode = 302;
context.HttpContext.Response.RedirectLocation = redirectLocation;
base.ExecuteResult(context);
}
/// <summary>
/// Gets the redirect location.
/// </summary>
/// <value>The redirect location.</value>
public string RedirectLocation
{
get
{
return redirectLocation;
}
}
}
}
You can then setup a new Redirect method in your base controller to create a ViewRedirectResult for you:
/// <summary>
/// Returns a <see cref="ViewRedirectResult" /> that redirects to the specified URL.
/// </summary>
/// <param name="url">The URL to redirect to.</param>
/// <returns>The <see cref="ViewRedirectResult" /> that redirects to the specified URL.</returns>
protected static new ViewRedirectResult Redirect(string url)
{
var result = new ViewRedirectResult(url)
{
ViewName = "Redirect"
};
return result;
}
All that remains is to create the Redirect view. We need this to be valid XHTML with a few extras to keep the SEO Toolkit happy; we need a content type, a description and a <h1>:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>MrKWatkins - Object Moved</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<meta name="description" content="This object has been moved." />
</head>
<body>
<div>
<h1>Object Moved</h1>
<p>
Object moved to <a href="<%= HttpUtility.HtmlAttributeEncode((string)ViewData["RedirectLocation"]) %>"><%= ViewData["RedirectLocation"] %></a>.
</p>
</div>
</body>
</html>
Et voila. The content of your redirects is now valid XHTML and the spurious warnings from your SEO Toolkit report go away.
LINQ to SQL isn't bad. Apart from a few annoying bugs with the designer (i.e. The designer.cs getting deleted and not regenerated when you update the model if you have a partial code file and it's insistence on adding in connection strings all over the shop being the two biggest) and some missing features (proper many-to-many relationships) it's pretty good. We use it at work and it performs well. One thing that can happen if you're not careful though is you end up with a massive .dbml files that contain all the tables in your database. This can be less than ideal, especially if your application has several areas. For example you might work for a company that makes cars and boats. (Yeah I admit that's quite unlikely but please endulge me...) Both sections of the company would need access to customer information but the car guys wouldn't need details of the boats and vice versa. Rather than have one big DataContext with all the tables in is there anyway we could structure things a bit better?
The short answer is yes, thanks to the 'Base Class' property in the .dbml designer. (Click on the .dbml somewhere and have a look in the Properties window) Make youself a .dbml for your core entities, then make .dbmls for each of your other areas and set the 'Base Class' property for these 'child' .dbmls the core DataContext. So you might have a CoreDataContext that contains Customer entities and then a CarDataContext that has Car entities and a BoatDataContext with Boat entities. Both the CarDataContext and BoatDataContext will have access to the Customer entities but not to each other's entities.
Of course just doing that isn't that much use. The benfit of O/R mappers like LINQ to SQL is the easy use of relationships between entities. So for our Car.dbml to be useful we would like our Car entity to have a reference to a Customer entity. But if we load Car.dbml into our designer we will not see any entities from the parent Core.dbml. So how do we create the relationship?
Manually is the answer. If we inspect the designer file for a .dbml we can copy the code for a similar one-to-many property and tweak it for our use:
public partial class Car
{
private EntityRef<Customer> _Owner;
[AssociationAttribute(Name = "Owner_Car", Storage = "_Owner", ThisKey = "OwnerId", OtherKey = "Id", IsForeignKey = true)]
public Customer Owner
{
get
{
return _Owner.Entity;
}
set
{
Customer previousValue = _Owner.Entity;
if (((previousValue != value) || !_Owner.HasLoadedOrAssignedValue == false))
{
SendPropertyChanging();
if ((previousValue != null))
{
_Owner.Entity = null;
}
_Owner.Entity = value;
if ((value != null))
{
_OwnerId = value.Id;
}
else
{
_OwnerId = 0;
}
SendPropertyChanged("Owner");
}
}
}
}
We can now have an Owner property on our Car entity that we can use like a normal LINQ to SQL property:
using (var carDataContext = new CarDataContext(Connection))
{
// Grab our Customer.
var customer = carDataContext.Customers.Where(c => c.Id == customerId).Single();
// Create a Car.
var car = new Car
{
Registration = "T1 KARR",
Owner = customer
};
// Save it.
carDataContext.Cars.InsertOnSubmit(car);
carDataContext.SubmitChanges();
}
But what about the other way around? What if the boat department what to get a list of all the boats a customer owns? I.e. can we get a Customer.Boats property? The answer to that is not really... The approach I tried was to define a subclass of Customer with the property and then replace the table in the DataContext. I.e. something like:
public sealed class BoatCustomer : Customer
{
private EntitySet<Boat> _Boats;
public BoatCustomer()
{
_Boats = new EntitySet<Boat>(new Action<Boat>(Attach_Boats), new Action<Boat>(Detach_Boats));
}
[AssociationAttribute(Name = "Customer_Boat", Storage = "_Boats", ThisKey = "Id", OtherKey = "OwnerId")]
public EntitySet<Boat> Boats
{
get
{
return _Boats;
}
set
{
_Boats.Assign(value);
}
}
private void Attach_Boats(Boat entity)
{
SendPropertyChanging();
entity.Owner = this;
}
private void Detach_Boats(Boat entity)
{
SendPropertyChanging();
entity.Owner = null;
}
}
public partial class BoatDataContext : CoreDataContext
{
public new Table<BoatCustomer> Customers
{
get
{
return GetTable<BoatCustomer>();
}
}
}
Sadly this, and other similar approaches, don't work... InvalidOperationExceptions with messages like "Data member 'Int32 Id' of type 'KWatkins.DataContextInheritance.Customer' is not part of the mapping for type 'BoatCustomer'. Is the member above the root of an inheritance hierarchy?" start getting thrown around the place. The problem is that the attribute mapping doesn't look into base classes for some members. (If you open the lid with Reflector you'll see gratuitous use of the BindingFlags.DeclaredOnly enum value) So what are our alternatives?
In the end I settled on an extension method on Customer that returns a query:
public static IQueryable<Boat> Boats(this Customer customer, BoatDataContext dataContext)
{
Validate.Argument(customer, "customer").IsNotNull();
Validate.Argument(dataContext, "dataContext").IsNotNull();
return from boat in dataContext.Boats
where
boat.OwnerId == customer.Id
select boat;
}
(See this post for details about the Validate class) This method returns a query that returns all the Boats for a Customer. Sadly it's not a proper EntitySet so we can't add and remove members from it. But I haven't found this to be a major problem; I usually use properties like this for querying rather than inserting/deleting so it hasn't bothered me much. It's a shame we have to pass in a BoatDataContext but LINQ to SQL entities don't have a reference to a DataContext so there isn't much we can do.
There you go. By use of inheritance you can help split up your .dbml files and not end up with one massive, unwieldy DataContext. Feel free to download some example source code that demonstrates all of this. Note that I used Visual Studio 2010 to write it though; I wanted to see if some of the LINQ to SQL bugs have been fixed. (The .designer.cs classes getting deleted I mentioned above seems to have been fixed) You'll therefore have to setup your own projects if you want a VS2008 version...
In my last post I talked about the CodePlex project called NUnit for Team Build that transforms the output from NUnit into a format that Team Foundation Server can understand. My article then discussed how to upload these results to TFS without using the MSTest program.
Well Richard Banks, the owner of the NUnit for Team Build project, got in touch and the source from my last article has been integrated with his work. Head over to the CodePlex site now to get the latest version! A big thanks to Richard for actually uploading the code for me; although I meant to I got distracted by the cricket...
The project doesn't end here though... I'm currently working on improving the code base, mainly by getting some unit tests in there. Seems to me a project all about unit tests should probably have some of it's own... I'm also trying to transform and upload NCover 1.5.8 output, although that is proving a little trickier. Will keep you posted...