Click here to Skip to main content
15,884,388 members
Articles / Programming Languages / C#

URL Auto Mapping

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
17 Jan 2010Apache4 min read 16K   210   8  
A URL query string to object mapper.

Introduction

Recently, I came across a need to pass data between services in an application in a generic way. I didn't want to use any solution that involved serialization because it would require for services which merely pass some data to others to have references to classes which they don't need for any business logic, thus unnecessary increasing coupling. For example, something like in the diagram below. The diagram assumes some kind of messaging being used to communicate between the services.

Service_Example.png

Of course, it's possible to notify the sender (consumer in the diagram) instead of the dedicated service, but imagine the situation where you have a lot of senders and the action to be executed is fairly simple (like inserting a history record to your data store) and can be easily generalised. In that situation, the sender will need to pass some data to the post action service which is not a concern for the email service. One of the easiest ways is pass that data as a URI string. Another option is JSON, but you would need to de-serialize in order to add any new bit of information. The URI is free of such limitations, it's very easy to add a new parameter. Coupled with Dependency Injection and ORM frameworks, the service can be very flexible. In that case, you would use the authority part as some lookup value for the DI, and the query will contain your data. As a side effect, the Email Service may add its own data by just appending the query string.

For that scenario, it would be nice to have some way of automatically mapping parameters from the query string to the properties of a class. Also, it will help a common task in any web application to get or put data to URL strings.

Background

The idea of the URI mapper is to match parameter names in the URI query string to properties in a target class using Reflection. It looks up only for properties (doesn't check fields), and can match protected, private, as well as public properties. The target property must have a getter and setter.

The mapper decodes/encodes strings on the value level and it doesn't expect the query syntax characters to be encoded (like ?, #, &, =, and the comma for collections). If you have a string with those characters encoded, you will need to decode them.

The mapper doesn't match object graphs; however, it's capable of populating collection properties as long as they support either the ICollection or ICollection<> interface. For collections, the separator is a comma. If you have a string collection where the items contain commas, you will need to encode these nested commas by %2C. For example, like: "CollectionParam=Hello%2C World!,Another collection item".

If there is more than one parameter with the same name in the query, the last value wins. I couldn't find any guidance for such cases. Microsoft's implementation of URL parsing adds all values with the same key together. There are several problems here - it uses the valid character (comma) to separate values (which is, in my opinion, absolutely unacceptable for such a low level utility), and it would present inconsistent behaviour for the mapping; for collections, it would be fine, but for any numeric value, there is no way to tell which value should be used. So, I decided that the approach "the last value wins" is correct in this case, and it also simplifies overriding previous values as the URI travels through a chain of services.

The mapping process can be influenced in two ways: by providing a supplied class and property level attributes, like skip, required, etc., as well as overriding the default configuration.

The default behaviour of the mapper is:

  • To match query parameters to property names in a case insensitive manner
  • All properties are optional, meaning that they may not be present in a query string
  • All parameters are optional, meaning that the matching property can be absent

Using the Code

Using the mapper is extremely straightforward. You can use either the statically created class's instance or instantiate your own. The class has several overload methods, but the most common would be like:

C#
TestClass test = UriMapper.DefaultMapper.Map<TestClass>( 
    new Uri("test://host/path?Property1=23,53,34"));

If you want, you can change the default behaviour by reconfiguring. Or, if your URI string doesn't have parameters whose names match properties exactly, you can try configuring the mapper to use the fuzzy name resolver, which is off by default. It tries to match names by looking for sub strings.

C#
UriMapper.DefaultMapper.Configure(new UriMappingConfiguration().Configure(
          new TryAddToCollectionAttribute()));  

UriMapper mapper = new UriMapper();
mapper.Configure(new UriMappingConfiguration().Configure(
       new DefaultFuzzyNameResolver()));

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Architect
Ireland Ireland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --