C#, Generics, Reflection, Silverlight, Web Development

Map query string parameters to properties of an object, easily`

So, how many people have done this?


URL: http://.../something.aspx?PersonID=4&ChildID=16

void () {
// Assuming o = some object with properties PersonID and ChildID
PersonWithChild o = new PersonWithChild();
string _personID = Request.QueryString["PersonID"];
if (int.TryParse(_personID, out personID) {
o.PersonID = personID;
}
string _childID = Request.QueryString["ChildID"];
if (int.TryParse(_childID, out childID) {
o.ChildID = childID;
}
...
}

What a pain! This kind of plumbing is unnecessary, although frameworks like MVC have this taken care of.
Silverlight uses the concept of querystrings to pass data between pages of a navigation business application, so the code below can be used with Silverlight in addition to whatever .Net-based web framework of your choosing.

(I know code is cut off, copy and paste it and it will be fine)

Non-SL:

void init() { // This is where you initialization code may be
PersonWithChild o = new PersonWithChild();
MapQueryString(Request.QueryString, ref o, false);
// Properties are now set
}

Silverlight:

void init() { // This is where you initialization code may be
PersonWithChild o = new PersonWithChild();
// Silverlight querystring object is an IDictionary so use extension method ToQueryString()
MapQueryString(NavigationContext.QueryString.ToQueryString(), ref o, false);
// Properties are now set
}

Here are the functions: Put them into a static class in your project.

///

/// Transforms a dictionary of key/value pairs strongly typed as strings
/// into a querystring that can be parsed by MapQueryString of T.
///

///
///
public static string ToQueryString(this IDictionary kvps) {
string query = "";
foreach (var key in kvps) {
query += string.Format("{0}={1}&", key.Key, key.Value);
}
return query.TrimEnd('&');
}

///

Takes a query string and maps each key value pair to public properties of an object of type TEntity.
///

/// The class of the type of object.
/// A string containing key/value pairs.
/// The object whose properties will be populated.
/// If true, the property names and keys from the key-value pairs must match exactly.
/// An array of strings that separates each key-value pair. Passing null will default the list to {&, &}.
/// The string that separates each key and value in a key-value pair. By default this is "=".
/// If multiple parameters with the same key are specified, they are concatenated with this string, by default ",".
///
/// Since a query string can have multiple keys with the same name, a Dictionary generic was not used.
///
public static void MapQueryString(string queryString, ref TEntity o, bool caseSensitive = false, string[] separatorStrings = null, string keyValueSeparator = "=", string multipleValueJoinSeparator = ",") {
if (separatorStrings == null) { separatorStrings = new string[] { "&", "&" }; }
List kvps = GetKeyValuePairs(queryString, separatorStrings, keyValueSeparator);
Type type = o.GetType();
var properties = type.GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties) {
List kv;
// Get all matching values
if (caseSensitive) {
kv = kvps.Where(p => p.Key == property.Name).ToList();
} else {
kv = kvps.Where(p => p.Key.ToLower() == property.Name.ToLower()).ToList();
}
if (kv.Count > 0) {
// Concatenate the values
string value = "";
foreach (var kvp in kv) {
if (value.Length > 0) { value += multipleValueJoinSeparator; }
value += kvp.Value;
}
object target = null;
Type ptype = property.PropertyType;
if (ptype.FullName.Contains("Nullable")) {
ptype = ptype.GetProperty("Value").PropertyType;
}
if (ptype != typeof(string)) {
try {
target = ptype.InvokeMember("Parse", BindingFlags.InvokeMethod, null, target, new object[] { value });
property.SetValue(o, target, null);
} catch (Exception ex) { }
} else {
property.SetValue(o, value, null);
}
}
}
//return o;
}

///

Turns a query string into a map of key-value pairs.
///

///
///
///
///
public static List GetKeyValuePairs(string queryString, string[] separatorStrings, string keyValueSeparator) {
List output = new List();
string[] kvps = queryString.Split(separatorStrings, StringSplitOptions.RemoveEmptyEntries);
foreach (string kvp in kvps) {
string[] t = kvp.Split(new string[] { keyValueSeparator }, StringSplitOptions.RemoveEmptyEntries);
NonUniqueKeyValuePairStruct op = new NonUniqueKeyValuePairStruct();
if (t.Length == 1) {
op.Key = null;
op.Value = t[0];
} else {
op.Key = t[0];
op.Value = t[1];
}
output.Add(op);
}
return output;
}

///

Used to hold non-unique key-value pairs.
///

public struct NonUniqueKeyValuePairStruct
{
public string Key;
public string Value;
}

2 thoughts on “Map query string parameters to properties of an object, easily`

Leave a comment