Skip navigation

I just read Jimmy Bogard’s post on how we do MVC ViewModels and I must say, I digg the approach. Personally, I find it a shame that the ViewData dictionary is so damn prominent.

In the comments there was a little bit of discussion on how to handle the ViewModel for the master page. Since there wasn’t much of elaboration on the topic, I thought I’d mention how I’m doing it for quite a while now, and I’m still ok with it :-).

I chose to use a ViewModel base class for the master page with the following in mind:

  • The base class only contains information that makes sense for every view page that is using the particular master page.
  • Just like Jimmy explains in his post, I also have very specific requirements. I’m not only talking about XHTML et. al., but things like unique titles, etc.
  • I’ve got my app split up in independent modules. The only thing they’ve got in common, is a base class for initialization and the fact that their views reside in the same project/directory structure on disk. This means that when I’m working in module X, I know what my master page and its (ViewModel-) requirements are and can subclass ViewModel as I see fit.

Before I continue: I certainly do not put ALL view model data of the master page in the base class, don’t get me wrong. There is quite some information that’s rendered by the master page, but that information is not coming from its model. Most things come from composed RenderActions (not partials!). Only view data that can be/needs to be provided by distinct action methods is defined in the base class. Every master page in my app (typically, one per module) inherits from ViewMasterPage<ViewModel> where ViewModel looks something like this:

/// <summary>

/// Represents the model that contains information for the view.
/// </summary>
public class ViewModel
{
	private readonly HtmlHeader header = new HtmlHeader();

	/// <summary>
	/// Represents the information for the (X)HTML head element.
	/// </summary>
	public HtmlHeader HtmlHeader { get { return header; } }
}

I guess it’s no surprise that to you that HtmlHeader looks like this (in my case):

public class HtmlHeader
{
	public string Title { get; set; }
	public string MetaDescription { get; set; }
	public string MetaKeywords { get; set; }
}

My master page itself uses the ViewModel as follows:

...
<head>
	<title><%= Html.Encode(Model.HtmlHeader.Title ?? Resources.Master.DefaultPageTitle) %></title>
	<meta name="description" content="<%= Html.Encode(Model.HtmlHeader.MetaDescription ?? Resources.Master.DefaultMetaDescription) %>" />

	<meta name="keywords" content="<%= Html.Encode(Model.HtmlHeader.MetaKeywords ?? Resources.Master.DefaultMetaKeywords) %>" />
	...
</head>
...

This model only contains information that is to be supplied by action methods. When an end user requests product information, the action method must provide the title, META keywords etc. When a user requests a content page, the ViewContent(string slug) method is responsible for getting the content and map its title, META description, author, copyright, etc. to the ContentViewModel.

But how can we be sure?

Ok so far so good, we’ve organized ourselves a bit, but how can we make sure we return the proper type of view model? Due to the loose coupling between the M, V and C and the way ControllerBase and Controller are designed, it practically requires a rewrite (encapsulation if we’re lucky) of the existing hierarchy…

But then again, if you’re doing TDD, you should be fine (’the_view_model_should_…’). If you’re not, the very first HTTP GET will blow up, so you’ll know something’s wrong soon enough either way. So, why go through all this hassle?

This does not mean there’s nothing that can’t be done! But before I (try to) do, I’m going to address my SRP issue! I currently investigating things like commands in stead of controllers and ActionControllers and how to fit in the ViewModel (maybe).

I’m just done updating my expression routing extensions class: First I’ve added support for extraction default values from Nullable parameters (oh boy, how could I miss that one!). Second, and more interestingly maybe, I’ve added support for automatically extracting constraints.

Let’s say you’ve got this route:

routes.MapRoute<ProductsController>(
	"brands/{title}-{brandId}/{pageIndex}",
	c => c.Brands(0, 0),
	cultureNames,
	null,
	new { brandId = @"\d+", pageIndex = @"\d+" });

The two regex values are there to ensure that request for ~/brands/somebrand-1/foo or ~/brands/somebrand-bar/1 are never even forwarded to the action method. It is part of the contract and does not make sense. (I have setup my routing in such a way that the final fallback/wildcard route maps to a content management module that renders a ‘page not found’ if the slug cannot be found in the database.)

If you take a look at the action method Brands(int, int?) you’ll see that it is perfectly possible to deduct these constraints from the expression. I decided to add support for this as follows:

  • Explicit constraints (passed to the MapRoute method), always win
  • If the type of the parameter is IConvertible (including nullable types), I automatically add a constraint
  • Strings parameters are ignored

Just like the GetActionDefaults, I’ve added a GetActionConstraints, that parses the method call as follows:

private static RouteValueDictionary GetActionConstraints(MethodCallExpression call, RouteValueDictionary constraints)
{
	if (constraints == null) constraints = new RouteValueDictionary(); 

	foreach (var parameter in call.Method.GetParameters())
	{
		// if there's an explicit constraint, keep it, if it's a string, just ignore it
		if(constraints.ContainsKey(parameter.Name) || parameter.ParameterType == typeof(string)) continue;

		var converter = TypeDescriptor.GetConverter(parameter.ParameterType);
		if(converter != null && converter.CanConvertFrom(typeof(string)))
		{
			constraints.Add(parameter.Name, new ConversionTestConstraint(converter));
		} 

		return constraints;
	}
}

The method adds a ConversionTestConstrating that takes the specific converter (we need this, since we can’t be sure we will be able to determine the type (or converter) the moment the constraint is called.

The ConversionTestConstraint is somewhat ugly, I admit, but in my defense: I have to catch a general Exception because most TypeConverters are implemented badly (in my opinion). The conversion will rarely fail, so I guess it’s not the end of the world, but still.

public class ConversionTestConstraint : IRouteConstraint
{
	private readonly TypeConverter converter;

	public ConversionTestConstraint(TypeConverter converter)
	{
		this.converter = converter;
	}

	public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
	{
		if (!values.ContainsKey(parameterName)) return true;

		try
		{
			converter.ConvertFrom(values[parameterName]);
			return true;
		}
		catch (Exception) // WTF?! ConvertFrom throws System.Exception with FormatException as InnerException
		{
			return false;
		}
	}
}

Now I can simply put the following, with the same result:

routes.MapRoute<ProductsController>(
	"brands/{title}-{brandId}/{pageIndex}",
	c => c.Brands(0, 0),
	cultureNames);

In yeterday’s post, I mentioned that I would give the IDataContractSurrogate a try to solve this issue. And guess what? Problem solved! Mine anyway, I’m not sure if it will help in all cases, but certainly for this one about arrays.

I started with a pass-through/noop implementation just to see if I could hook into the serialization process hoping to get some more insight. What I noticed was that GetObjectToSerialize got called several times before the exception was thrown. In the second run, I kept track of the parameter values and found out that I overlooked an IList<T> property somewhere deep in the object graph of the data contract. Since the property value was deserialized after retrieval over the wire it was now, of course, just a simple array…

Microsoft proposed to work around the issue by putting the data contract assembly in the GAC, however, there’s a much simpler solution: modify your object while it is being serialized.


public object GetObjectToSerialize(object obj, Type targetType)
{
	if (obj is Array && targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(IList<>))
	{
		var typeArg = targetType.GetGenericArguments()[0];  // typeof(T)
		var type = typeof(List<>).MakeGenericType(typeArg); // typeof(List<T>)
		return Activator.CreateInstance(type, obj);         // new List<T>(IEnumerable<T>)
	}

	return obj;
}

Put this in your IDataContractSurrogate implementation, pass it to the constructor of your serializer and you should be good to go. Every time the serializer is about to serialize an array (T[]) the surrogate will give it a wrapper (List<T>) instead.

The funny thing about all this is that it is not the first time I had to implement an IDataContractSurrogate, but for completely different reasons. The fact that my original unit test worked and that the bug was only triggered when running in IIS (or Cassini) and that putting the assembly in the GAC solved it got me so confused, that I only though about going the surrogate way later that day. I still wonder why putting the assembly in the GAC, solves the problem, though. And what’s got LoaderOptimization to do with it? The CLR moves in mysterious ways, I suppose, at least sometimes…

Lost a few hours on this one today! Here’s what happened: At some point in our application I get hold of an object graph (an instance of a DataContract) which I need to serialize and store in the database. No big deal, I thought…

As any good citizen, I started with a test. First a unit test, then a minor integration test. Fine, time to put it in the app. After doing so, however, both Cassini and IIS died on me with an ExecutionEngineException as soon as WriteObject got called. WTF?!

Apparently, there’s this bug that’s been bugging me today:

When data contract contains property of type IEnumerable<T> where T is another data contract .NET crashes after handling the request (.NET crashes after operation method is invoked and returns the result).

If a OperationContract is defined to return IList<T> where T is a type defined by a DataContract, and an array is returned, then WCF throws an ExecutionEngineException.

Both entries are describing a somewhat different context, but I was sure it was the same problem. Since I was using a shared assembly, I didn’t have the freedom to ‘play around’ with the data contracts to see if I could do something about it. What I did notice, though, was that I was able to serialize other, far more complex, data contract without any problem. Also, the data contracts has concrete Lists defined, not interfaces. Weird.

Anyway, putting the shared assembly in the GAC, did indeed solve the problem. Five minutes later someone tells me (I’m new there) we’re not allowed to put assemblies in the GAC on our servers… ain’t that just sweet? I think I’m gonna give the IDataContractSurrogate a try tomorrow, one never knows.

Update: I’ve added automatic constraints to this.

When you’re working with ASP.Net MVC, routing is a very important thing to get just right. Unfortunately, the default routing options are pretty fragile. By default you have to resort to ‘magic strings’, anonymous types and/or string-based dictionaries etc. Depending on your requirements, this kind of routing can become quite the PITA.

Looking at my personal requirements: I have about 120 routes to map (including localization and a fall-back to managed content in the database). One day I got so fed up with the default routing options, that I decided to go for my own MapRoute() extension method.

Here’s just a fraction of my current RegisterRoutes() method (I’ve removed localization, for simplicity):

routes.MapRoute<ProductsController>("search/{query}/{pageIndex}", c => c.Search("", 0));
routes.MapRoute<ProductsController>("new/{pageIndex}",            c => c.NewProducts(0));
routes.MapRoute<ProductsController>("soon/{pageIndex}",           c => c.ComingProducts(0));

Before my changes it looked like this (again, without localization; with localization, there would be 3 more per route):

routes.MapRoute("Search", "search/{pageIndex}", new { controller = "Products",	action = "Search", pageIndex = 0 });
routes.MapRoute("newproducts", "new/{pageIndex}", new { controller = "Products", action = "NewProducts", pageIndex = 0 });
routes.MapRoute("soonproducts", "soon/{pageIndex}", new { controller = "Products", action = "ComingProducts", pageIndex = 0 });

Notice the difference?

Getting rid of magic strings was not my only motivation, though. I have chopped-up my application in different modules. The two main modules are the actual customer front-end and the administrative product/category/brand/news/foo/bar management pages. As it happens, both have a ProductsController class… If you go for default routing, you’ll run in to issues: ASP.Net MVC won’t be able to distinguish the one class from the other and throw you a nice exception telling you to specify the namespaces to disambiguate the controllers. The default solution would be specifying the namespace using magic strings, yet again…

Here’s the extension method (the most extended overload):

public static void MapRoute<T>(this RouteCollection routes, string url, Expression<Func<T, ActionResult>> action, RouteValueDictionary defaults, RouteValueDictionary constraints)
	where T : Controller
{
	if (routes == null) throw new ArgumentNullException("routes");
	if (url == null) throw new ArgumentNullException("url");
	if (action == null) throw new ArgumentNullException("action");

	var methodCall = action.Body as MethodCallExpression;
	if (methodCall == null) throw new ArgumentException(string.Format("The action '{0}' does not represent a method call.", action), "action");

	// merge action defaults with explicit defaults
	defaults = GetActionDefaults(methodCall, defaults);

	// verify constraints
	if(constraints != null && constraints.Any(c => !(c.Value is IRouteConstraint || c.Value is string)))
		throw new ArgumentException("The constraints dictionary contains illegal elements. Constraint values must be strings (regex) or IRouteConstraint implementations.", "constraints");

	var route = new Route(url, new MvcRouteHandler())
	{
		Defaults = new RouteValueDictionary(defaults),
		Constraints = constraints,
		DataTokens = new RouteValueDictionary { { NamespacesKey, new[] { typeof(T).Namespace } } }
	};

	routes.Add(url, route);
}

Here’s the GetActionDefaults method that extracts the default values from the method signature:

private static RouteValueDictionary GetActionDefaults(MethodCallExpression call, RouteValueDictionary defaults)
{
	var controllerName = call.Method.DeclaringType.Name;
	if (controllerName.EndsWith(ControllerSuffix, StringComparison.OrdinalIgnoreCase))
		controllerName = controllerName.Remove(controllerName.Length - ControllerSuffix.Length, ControllerSuffix.Length);

	defaults.Add(ControllerKey, controllerName);

	var attributes = call.Method.GetCustomAttributes(typeof(ActionNameAttribute), true);
	defaults.Add(ActionKey, attributes.Length == 1 ? ((ActionNameAttribute) attributes[0]).Name : call.Method.Name);

	var parameters = call.Method.GetParameters();
	for (var i = 0; i < parameters.Length; i++)
	{
		var expression = call.Arguments[i] as ConstantExpression;
		if (expression != null) defaults.Add(parameters[i].Name, expression.Value);
	}

	return defaults;
}

Result:

  • Who needed that route name anyway?
  • No more magic string (except fot the url)
  • Namespaces are registered automatically, modules/areas no longer require special treatment
  • Defaults are resolved from the action expression (additional defaults are still suppported)
  • Constraints are checked as soon as the route is mapped (as good as it gets, I suppose)
  • If you’re happy with ‘http://domain.com/{cultureName}/…’ url’s, you’re good to go as well, just pass along an array of supported culture names:
public static void MapRoute<T>(this RouteCollection routes, string url, Expression<Func<T, ActionResult>> action, string[] cultureNames, RouteValueDictionary defaults, RouteValueDictionary constraints)
	where T : Controller
{
	routes.MapRoute(url, action, new RouteValueDictionary(defaults), constraints);

	foreach (var cultureName in cultureNames)
	{
		var d = new RouteValueDictionary(defaults) {{"lang", cultureName}};
		routes.MapRoute(string.Format("{0}/{1}", cultureName, url), action, d, constraints);
	}
}

Man, since about a week now, I’m forced at using ASP.Net WebForms. It’s been so long, that I almost forgot what a terrible PITA WebForms really is.

I can only hope I get them to move to something else real soon…

WebForms is a lie. It’s abstraction wrapped in deception covered in lie sauce presented on a plate full of diversion and sleight of hand. Nothing you do with Webforms has anything to do with the web – you let it do the work for you.

I couldn’t agree more with Rob’s post.

The past few evenings, I’ve tried to come up with something to shield exceptions in WCF. I am aware that there are quite some posts about this topic already, but I thought I’d share my findings/solution anyway. I’ve ran into a few issues with the approaches I found and learned something doing so…

I wanted this to be simple and yet flexible. The following bullets sum up the base requirements:

  • No need to apply FaultContractAttribute to all operations
  • Allow the user to map exceptions to faults (type and content) in a straightforward manner
  • No coupling whatsoever, no imposed library-use, exception or fault types
  • Simple and selective fallback to standard WCF behavior

Test First

Let’s start with a test that illustrates the idea (or skip to the implementation). The scenario is as follows. We have a simple service contract with two operations, one throws a custom ValidationException, the other a NotImplementedException. I have told the exception shielding behavior to shield two types of exceptions:

  • ValidationException: should map to a validation fault (including one simple property, but this could be anything, of course)
  • Exception: should set the ExceptionDetail, basically this comes down to IncludeExceptionDetailInFaults

Here are the types used to test the exception shielding:

[ServiceContract]
[ShieldExceptions	(
	new[] { typeof(ValidationFault), typeof(ExceptionDetail) },
	new[] { typeof(ValidationException), typeof(Exception) })]
public interface ITestService
{
	[OperationContract] void OperationOne();
	[OperationContract] void OperationTwo();
}

public class TestService : ITestService
{
	public void OperationOne()
	{
		throw new ValidationException() { CustomProperty = "OperationOne finds the request invalid." };
	}

	public void OperationTwo()
	{
		throw new System.NotImplementedException();
	}
}

public class ValidationException : Exception
{
	public string CustomProperty { get; set; }
}

[DataContract]
public class ValidationFault
{
	// .ctor maps exception to fault
	public ValidationFault(ValidationException exception)
	{
		FaultProperty = exception.CustomProperty;
	}

	[DataMember]
	public string FaultProperty { get; set; }
}

Notice the use of ShieldExceptions and the fact that there are no FaultContractAttributes on the operations.

And here is one of the tests (I’ve skipped the rest to reduce bloat) to verify the type of exception thrown in the client thread.

[Test]
[ExpectedException(typeof(FaultException<ValidationFault>))]
public void Calling_operation_that_throws_shielded_validation_exception_should_throw_corresponding_fault_exception_on_client()
{
	// Arrange
	var svc = ChannelFactory<ITestService>.CreateChannel(binding, new EndpointAddress(serviceUri));

	// Act
	svc.OperationOne();
}

As the test fixture above illustrates, you simply need to add the attribute ShieldExceptions with a list of exception types you wish to shield. For every exception you need to specify the corresponding fault (this is checked during validation of the behavior). The order in which you specify exception types should be from most specific to the least; being System.Exception. If you don’t specify System.Exception, you’ll get the standard WCF behavior for all exception you didn’t specify.

The mapping of exceptions to faults is simply done by the means of a constructor. The ShieldExceptions behavior will try to create a fault based on the .ctor. First it tries to find the .ctor with the specific exception type, then with a generic exception type and finally it falls back to the default .ctor.

Implementation

There are three parts to this solution: A custom IContractBehavior implementation, a custom IErrorhandler and an IClientMessageInspector.

The ShieldExceptionsAttribute does a few things. It validates if the specified fault and exception types make sense adds a custom error handler to the ChannelDispatcher and sets the required ExceptionShieldingMessageInspector. Besides adding all this behavior the most important thing the ShieldExceptionsAttribute does is updating the service contract with possible faults. If you check out the generated WSDL, you’ll see that all fault information is included.

public class ShieldExceptionsAttribute : Attribute, IContractBehavior
{
	private readonly Type[] knownFaultTypes;
	private readonly Type[] exceptionTypes;

	public ShieldExceptionsAttribute(Type[] knownFaultTypes, Type[] exceptionTypes)
	{
		this.knownFaultTypes = knownFaultTypes;
		this.exceptionTypes = exceptionTypes;
	}

	public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
	{
		foreach (var op in contractDescription.Operations)
		foreach (var knownFaultType in knownFaultTypes)
		{
			// Add fault contract if it is not yet present
			if (!op.Faults.Any(f => f.DetailType == knownFaultType))
				op.Faults.Add(new FaultDescription(knownFaultType.Name) { DetailType = knownFaultType, Name = knownFaultType.Name });
		}
	}

	public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
	{
		clientRuntime.MessageInspectors.Add(new ExceptionShieldingMessageInspector(knownFaultTypes));
	}

	public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
	{
		dispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(new ExceptionShieldingErrorHandler(knownFaultTypes, exceptionTypes));
	}

	public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
	{
		if(knownFaultTypes.Length != exceptionTypes.Length)
			throw new ArgumentException("The ShieldExceptions behavior needs a corresponding exception type for each possible fault to shield.");

		var badType = knownFaultTypes.FirstOrDefault(t => !t.IsDefined(typeof (DataContractAttribute), true));
		if(badType != null)
			throw new ArgumentException(string.Format("The specified fault '{0}' is no data contract. Did you forget to decorate the class with the DataContractAttirbute attribute?", badType));

		var badExceptionType = exceptionTypes.FirstOrDefault(t => t != typeof(Exception) && !t.IsSubclassOf(typeof(Exception)));
		if (badExceptionType != null)
			throw new ArgumentException(string.Format("The specified type '{0}' is not an Exception-derived type.", badExceptionType));
	}
}

Now, every time service operation code throws an exception, the ExceptionShieldingErrorHandler gets called. This handler analyzes the thrown exception and creates the appropriate FaultMessage that can travel back to the client.

public class ExceptionShieldingErrorHandler : IErrorHandler
{
	private readonly Type[] knownFaultTypes;
	private readonly Type[] exceptionTypes;

	public ExceptionShieldingErrorHandler(Type[] knownFaultTypes, Type[] exceptionTypes)
	{
		this.knownFaultTypes = knownFaultTypes;
		this.exceptionTypes = exceptionTypes;
	}

	public bool HandleError(Exception error)
	{
		return true; // session should not be killed, or should it?
	}

	public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
	{
		if (error is FaultException) return;

		var exceptionType = exceptionTypes.FirstOrDefault(t => error.GetType() == t || error.GetType().IsSubclassOf(t));
		if(exceptionType != null)
		{
			var faultType = knownFaultTypes[Array.IndexOf(exceptionTypes, exceptionType)];
			fault = Message.CreateMessage(version, CreateFaultException(faultType, error).CreateMessageFault(), faultType.Name);
		}
	}

	private static FaultException CreateFaultException(Type faultType, Exception exception)
	{
		var ctor = faultType.GetConstructor(new[] { exception.GetType() })   // .ctor with specific exception?
			      ?? faultType.GetConstructor(new[] { typeof(Exception) }); // .ctor with generic exception?

		var detail = ctor != null
			? ctor.Invoke(new[] { exception })
			: faultType.GetConstructor(Type.EmptyTypes).Invoke(null); // fall back to default .ctor

		// Create generic fault exception with detail and reason
		return Activator.CreateInstance(typeof(FaultException<>).MakeGenericType(faultType), detail, "Unhandled exception has been shielded.") as FaultException;
	}
}

The CreateFaultException makes sure the most appropriate .ctor is called. This allows you to map exceptions to faults just by initializing the fault from the constructor.

The final piece of the puzzle is a client message inspector. The fact that I have a IClientMessageInspector, is to work around one of the issues I encountered: When you do not specify a FaultContract attribute on the operation(s) in your ServiceContract, you will always get a non-generic (or should I say generic) FaultException in stead of the expected FaultException<T>, holding the detail. That is, if you use a ChannelFactory to create your proxies, which is something I tend to do whenever I can.

Generating proxies from the WSDL (without re-using types, that is) does not need this message inspection since the code will be based on the WSDL which contains all information.

Without the explicit FaultContractAttribute, there’s only one thing we miss: the client proxy doesn’t know about the fault detail. The reply message will contain it, but the channel will simply ignore it. Using this simple message inspector we can easily ‘fake’ the exact same behavior we have with the FaultContractAttribute: we read the detail ourselves.

public class ExceptionShieldingMessageInspector : IClientMessageInspector
{
	private readonly Type[] knownFaultTypes;

	public ExceptionShieldingMessageInspector(Type[] knownFaultTypes)
	{
		this.knownFaultTypes = knownFaultTypes;
	}

	public object BeforeSendRequest(ref Message request, IClientChannel channel)
	{
		return null; // no correlation required
	}

	public void AfterReceiveReply(ref Message reply, object correlationState)
	{
		if (!reply.IsFault) return;

		var action = reply.Headers.Action;
		var faultType = knownFaultTypes.FirstOrDefault(t => t.Name == action);

		if (faultType != null)
		{
			var detail = ReadFaultDetail(MessageFault.CreateFault(reply, int.MaxValue), faultType);
			var exceptionType = typeof(FaultException<>).MakeGenericType(faultType);
			var faultException = Activator.CreateInstance(exceptionType, detail, "Server exception has been shielded.") as Exception;

			throw faultException;
		}
	}

	private static object ReadFaultDetail(MessageFault reply, Type faultType)
	{
		using (var reader = reply.GetReaderAtDetailContents())
		{
			var serializer = new DataContractSerializer(faultType);
			return serializer.ReadObject(reader);
		}
	}
}

As you might know, I run a web shop written in ASP.Net MVC. The move to ASP.Net MVC is fairly recent (time frame of preview 4), the shop ran for a few years on ‘classic’ ASP.Net (am I glad that’s finally over), but provided basically the same functionality as the ported version now.

There are some improvements though (besides the obvious: a far better implementation). One of the most visible changes is the support for more SEO-friendly urls. Since the shop is available in three languages I decided to finally provide decent localized urls as well. My requirements were as follows:

  • Each domain has a default culture (.be defaults to ‘nl’, .fr to ‘fr’, etc.), but supports all others as well
  • When browsing the site with the default culture no culture url segment is required
  • If the culture url segment specifies the default culture, the request is redirected to a url without culture segment
  • Urls with a culture segment should look like ‘http://domain.com/[culture-name]/[content-url]‘

There are various ways of getting this done, but the most obvious one is a custom ActionFilterAttribute (+ optional controller base class, if you don’t like putting the attribute over and over again). An action filter alone won’t cut it, however. We’ll need to setup appropriate routes first…

Routing

In order to register localized routes as convenient as possible, I’ve created a little helper class with extension methods that provides a ‘MapLocalizedRoute‘ equivalent for each standard ‘MapRoute‘ overload.

/// <summary>
/// Provides extension methods to register localized routes.
/// </summary>
public static class RouteCollectionExtensions
{
	public static void MapLocalizedRoute(this RouteCollection routes, string name, string url)
	{
		MapLocalizedRoute(routes, name, url, null, null);
	}

	public static void MapLocalizedRoute(this RouteCollection routes, string name, string url, object defaults)
	{
		MapLocalizedRoute(routes, name, url, defaults, null);
	}

	public static void MapLocalizedRoute(this RouteCollection routes, string name, string url, string[] namespaces)
	{
		MapLocalizedRoute(routes, name, url, null, null, namespaces);
	}

	public static void MapLocalizedRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
	{
		MapLocalizedRoute(routes, name, url, defaults, constraints, null);
	}

	public static void MapLocalizedRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces)
	{
		MapLocalizedRoute(routes, name, url, defaults, null, namespaces);
	}

	public static void MapLocalizedRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
	{
		foreach (var cultureName in Locale.SupportedCultureNames)
		{
			var route = routes.MapRoute(
				string.Format("{0}-{1}", name, cultureName),
				string.Format("{0}/{1}", cultureName, url),
				defaults,
				constraints,
				namespaces);

			route.Defaults.Add(Locale.CultureSegmentName, cultureName);
		}
	}
}

MapLocalizedRoute will simply map a route for every supported culture name (as set in the Locale class). Because the culture name is a literal in the url, we need to add a ‘culture’ value to the default values of the route. Doing so gives you more control over your routing and avoids the possibility of handling unsupported or malformed culture names. With these extension methods we can do the following in our Application_Start:


Locale.DefaultCultureName = "nl";
Locale.SupportedCultureNames = new[] {"nl", "en", "fr"};

// ...

routes.MapLocalizedRoute("about", "about/", new { controller = "Root", action = "About" });
routes.MapLocalizedRoute("terms", "terms/", new { controller = "Root", action = "Terms" });
routes.MapLocalizedRoute("faq", "faq/", new { controller = "Root", action = "Faq" });

// ...

routes.MapLocalizedRoute("brands", "brands/{title}-{brandId}/{pageIndex}",
	new { controller = "Products", action = "Brands", pageIndex = "0" });
routes.MapLocalizedRoute("cat", "{name}-{categoryId}/{pageIndex}",
	new { controller = "Products", action = "Category", pageIndex = "0" });

// map non-localized routes here

In case you’re wondering why default culture routes are not mapped in MapLocalizedRoute and why the listing above mentions that you should map non-localized route after the localized ones. This is simply because we need to add the most common route patterns first.

Localize

Now let’s have a look at the LocalizeAttribute. For every localized url the OnActionExecuting will make sure the CurrentCulture and CurrentUICulture of the current thread are set as requested. If the requested culture is the default one, the filter will redirect the user to the default culture url (without culture segment).

Note that we don’t have to verify if the requested culture name is a supported one; the MapLocalizedRoute will make sure that only the supported languages are mapped.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class LocalizeAttribute : ActionFilterAttribute
{
	private static readonly Regex removeCultureSegmentRegex = new Regex(@"^/[a-zA-Z\-]+", RegexOptions.Compiled);

	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		if (!filterContext.RouteData.Values.ContainsKey(Locale.CultureSegmentName))
		{
			SetCurrentCultures(Locale.DefaultCultureName);
			return;
		}

		var culture = filterContext.RouteData.Values[Locale.CultureSegmentName].ToString();

		if (culture == Locale.DefaultCultureName)
		{
			RedirectToDefaultCultureUrl(filterContext);
			return;
		}

		SetCurrentCultures(culture);
	}

	private static void SetCurrentCultures(string cultureName)
	{
		Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
		Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName);
	}

	private static void RedirectToDefaultCultureUrl(RequestContext filterContext)
	{
		filterContext.HttpContext.Response.Redirect(removeCultureSegmentRegex.Replace(filterContext.HttpContext.Request.RawUrl, string.Empty), true);
	}
}

Finally, there just theLocale class that configures the default and supported cultures.

/// <summary>
/// Holds the default and supported culture names.
/// </summary>
public static class Locale
{
	static Locale()
	{
		DefaultCultureName = "en";
		SupportedCultureNames = new[] {"en", "nl", "fr"};
	}

	internal const string CultureSegmentName = "culture";

	public static string DefaultCultureName { get; set;}
	public static string[] SupportedCultureNames { get; set;}
}

In a previous post I wrote something about putting an NHibernate session in your operation context. Guess what? I ended up never using that implementation because shortly after actually putting that behavior in my app, I decided to make it more generic first.

I thought, what will I do with this or that operation of this or that service? I don’t need this kind of context, I need something else here. I was really not into creating a different behavior (and so on) every time something else needed to be scoped. So I thought, why not just configure the type of the context I want?

End Result

Let’s start with the goal. The end result allows you to configure a certain type of context by adding the following behavior extension to the system.ServiceModel section of your configuration file:

<extensions>
	<behaviorExtensions>

		<add name="callContextBehavior" type="typesafe.ServiceModel.CallContextServiceBehaviorExtensionElement, typesafe.ServiceModel, Version=0.2.0.0, Culture=neutral, PublicKeyToken=b7fa8d8d247605f2"/>
	</behaviorExtensions>
</extensions>
<behaviors>
	<serviceBehaviors>
		<behavior name="behavior">
			<callContextBehavior contextType="[ASSEMBLY QUALIFIED NAME OF CONTEXT TYPE]"/>

		</behavior>
	</serviceBehaviors>
</behaviors>

With this configuration I can ensure that every service operation of a certain service will be able to access the specified type of context. All I have to do is add behaviorConfiguration="behavior" to a service configuration element. I could do the same thing on endpoint level as well (if I add the behaviorExtension to an endpointBehavior).

If you don’t like config, you can alternatively go ahead and apply the behavior by the means of a CallContextBehavior attribute which you can apply on either service or operation level. (Doing so also makes unit testing easier to maintain/write.)

[CallContextBehavior(typeof(MyCallContext))]
public bool OperationHasContext()
{
	return CallContext.Current != null;
}

// or

[CallContextBehavior(typeof(MyCallContext))]
public class TestService : ITestService
{
	// now all operations can assert CallContext.Current != null
}

How do we achieve this?

Basically, you’ll need to do the following…

  1. Provide a way to access and create your custom context from your code
  2. initialize and dispose the context when required/appropriate
  3. allow yourself to apply the context (at various levels)

First thing you need is an interface for you context. Well, technically speaking, you don’t, but I decided to do so to make things more explicit and somewhat safer. I have a context interface (an abstract class, actually) that looks like this:

public abstract class CallContext
{
	[ThreadStatic] // who needs OperationContext? ;-)
	private static CallContext callContext;

	/// <summary>
	/// Gets the context of the current operation.
	/// </summary>

	public static CallContext Current
	{
		get { return callContext; }
		internal set { callContext = value;}
	}

	protected internal abstract void Initialize();
	protected internal abstract void Dispose();
}

The static Current property allows you to access the configured context from your service operation code (that’s the main reason I went for an abstract class opposed to an interface). You’ll only have to cast it to your exact type the moment you need to access it (more on this later).

If you want to create a custom context, all you have to do is inherit from CallContext and override its two methods. It goes without saying that the Initialize method is called right before your service call, and the Dispose method right after.

In case you’re wondering why the CallContext class is not IDisposable (while specifying a Dispose method), I did this so I could hide the method from the service implementation code: operations cannot call it. Besides, it didn’t add any value anyway.

The second part consists of an ICallContextInitializer that makes sure the context gets initialized and disposed when we want it, right before and right after our operation executes.

public class CallContextInitializer : ICallContextInitializer
{
	private readonly Type contextType;

	public CallContextInitializer(Type contextType)
	{
		this.contextType = contextType;
	}

	object ICallContextInitializer.BeforeInvoke(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.IClientChannel channel, Message message)
	{
		CallContext.Current = Activator.CreateInstance(contextType) as CallContext;

		if (CallContext.Current == null)
			throw new ConfigurationErrorsException(string.Format("The type '{0}' could not be initialized or is no CallContext derived type.", contextType));

		CallContext.Current.Initialize();

		return null; // we don't need no correlation
	}

	void ICallContextInitializer.AfterInvoke(object state)
	{
		// Dispose the context
		if (CallContext.Current != null)
			CallContext.Current.Dispose();
	}
}

Finally we need to apply the ICallContextInitializer to the service operations with a behavior. The following behavior implements IServiceBehavior, IEndpointBehavior and IOperationBehavior. This allows you to choose the granularity of applying the behavior.

public class CallContextServiceBehavior : Attribute, IServiceBehavior, IEndpointBehavior, IOperationBehavior
{
	private readonly Type contextType;

	public CallContextServiceBehavior(Type type)
	{
		contextType = type;
	}

	void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
	{
		foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
		foreach (var ed in cd.Endpoints)
		foreach (var operation in ed.DispatchRuntime.Operations)
		{
			operation.CallContextInitializers.Add(new CallContextInitializer(contextType));
		}
	}

	void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
	{
		foreach (var operation in endpointDispatcher.DispatchRuntime.Operations)
		{
			operation.CallContextInitializers.Add(new CallContextInitializer(contextType));
		}
	}

	void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
	{
		dispatchOperation.CallContextInitializers.Add(new CallContextInitializer(contextType));
	}

	// remainder of the interfaces have no implementation...
}

To enable applying the behavior through the system.ServiceModel configuration section (as in the first listing above), we need to create the following BehaviorExtensionElement.

public class CallContextBehaviorExtensionElement : BehaviorExtensionElement
{
	private ConfigurationPropertyCollection properties;

	public override Type BehaviorType
	{
		get { return typeof(CallContextBehavior); }
	}

	protected override object CreateBehavior()
	{
		Type type = Type.GetType(ContextType);

		if (type == null)
			throw new ConfigurationErrorsException(string.Format("The type '{0}' could not be initialized.", ContextType));

		return new CallContextBehavior(type);
	}

	protected override ConfigurationPropertyCollection Properties
	{
		get
		{
			if (properties == null)
			{
				properties = new ConfigurationPropertyCollection {new ConfigurationProperty("contextType", typeof (string))};
			}
			return properties;
		}
	}

	[ConfigurationProperty("contextType", IsRequired = true)]
	public string ContextType
	{
		get { return (string)base["contextType"]; }
		set { base["contextType"] = value; }
	}

}

Example

To illustrate how it all fits together: The following listing contains a completely self-contained unit test that illustrates the usage and working of the above solution.

[TestFixture]
public class CallContextBehaviorTests
{
	private const string address = "net.pipe://localhost/TestService";

	private ServiceHost host;
	private ITestService service;

	[TestFixtureSetUp]
	public void SetUp()
	{
		host = new ServiceHost(typeof(TestService));
		host.AddServiceEndpoint(typeof(ITestService), new NetNamedPipeBinding(), address);
		host.Open();

		var factory = new ChannelFactory<ITestService>(new NetNamedPipeBinding(), new EndpointAddress(address));
		service = factory.CreateChannel();
	}

	[TestFixtureTearDown]
	public void TearDown()
	{
		host.Close();
	}

	[Test]
	public void CallContext_is_null_by_default()
	{
		Assert.IsTrue(service.OperationWithoutContext());
	}

	[Test]
	public void CallContext_should_be_available_after_applying_service_attribute()
	{
		Assert.IsTrue(service.OperationWithContext());
	}
}

internal class MyCallContext : CallContext
{
	// any property we like

	public new static MyCallContext Current
	{
		get { return CallContext.Current as MyCallContext; }
	}

	protected override void Initialize() { }
	protected override void Dispose() { }
}

[ServiceContract]
internal interface ITestService
{
	[OperationContract]
	bool OperationWithContext();

	[OperationContract]
	bool OperationWithoutContext();
}

internal class TestService : ITestService
{
	[CallContextBehavior(typeof(MyCallContext))]
	public bool OperationWithContext()
	{
		return MyCallContext.Current != null;
	}

	public bool OperationWithoutContext()
	{
		return MyCallContext.Current == null;
	}
}
Follow

Get every new post delivered to your Inbox.