Introduction
PropertyMapper<T>
is a helper class that allows to map object properties to a certain string
keys. It also allows to access values of mapped properties based on those string
keys.
I first needed such functionality while working with EntityFramework
Code-First. I had a few table structures with many columns having uppercase names and containing underscores, which I didn't want to bring into my POCO object definitions. So I decided to create a helper class that would map my POCO object properties to the string
keys ones, and would let me extract values from POCO instances and quickly map them back to original string
keys, forming a dictionary with key/value pairs.
Background
Mapping functionality by itself is pretty trivial, it is based on two instances of Dictionary<string, string>
for quick lookup, and does not require special attention. What's more interesting here is the GetPropertyName
method, which accepts Expression<Func<T, TReturn>>
as an argument. This trick allows to use MapProperty
method as follows:
public sealed class Test
{
public string Foo { get; set; }
public string Bar { get; set; }
}
var mapper = new PropertyMapper<Test>();
mapper.MapProperty(x => x.Foo, "PROP-FOO");
mapper.MapProperty(x => x.Bar, "PROP-BAR");
As opposed to passing hard-coded string
s, that make code refactoring impossible:
mapper.MapProperty("Foo", "PROP-FOO");
mapper.MapProperty("Bar", "PROP-BAR");
Source Code
public sealed class PropertyMapper<T>
{
private readonly Dictionary<string, string> m_maps = new Dictionary<string, string>();
private readonly Dictionary<string, string> m_props = new Dictionary<string, string>();
private static string GetPropertyName<TReturn>(Expression<Func<T, TReturn>> property)
{
var expr = property.Body as MemberExpression;
if (expr == null)
throw new InvalidOperationException("Invalid property type");
return expr.Member.Name;
}
public void MapProperty<TReturn>(Expression<Func<T, TReturn>> property, string mapTo)
{
var propName = GetPropertyName(property);
m_maps[mapTo] = propName;
m_props[propName] = mapTo;
}
public string GetPropertyMap<TReturn>(Expression<Func<T, TReturn>> property)
{
return GetPropertyMap(GetPropertyName(property));
}
public string GetPropertyMap(string property)
{
string result;
return m_props.TryGetValue(property, out result) ? result : null;
}
public object GetPropertyValue(T instance, string property)
{
string result;
if (!m_maps.TryGetValue(property, out result))
return null;
return typeof (T).GetProperty(result).GetValue(instance, null);
}
public IDictionary<string, object> GetPropertyValues(T instance)
{
return m_maps
.Select(x => new {Name = x.Key, Value = typeof (T).GetProperty(x.Value).GetValue(instance, null)})
.Where(x => x.Name != null && x.Value != null)
.ToDictionary(x => x.Name, x => x.Value);
}
}
Using the Code
Given the following class:
public sealed class Test
{
public string Foo { get; set; }
public string Bar { get; set; }
}
We can use PropertyMapper<T>
as follows:
var mapper = new PropertyMapper<Test>();
mapper.MapProperty(x => x.Foo, "PROP-FOO");
mapper.MapProperty(x => x.Bar, "PROP-BAR");
var t = new Test {Foo = "Value-Foo", Bar = "Value-Bar"};
Console.WriteLine("Single property:");
var propMap = mapper.GetPropertyMap(x => x.Foo);
Console.WriteLine("{0}={1}", propMap, mapper.GetPropertyValue(t, propMap));
Console.WriteLine();
Console.WriteLine("All properties:");
foreach (var item in mapper.GetPropertyValues(t))
Console.WriteLine("{0}={1}", item.Key, item.Value);
Output results are:
Single property:
PROP-FOO=Value-Foo
All properties:
PROP-FOO=Value-Foo
PROP-BAR=Value-Bar