Filling model properties using Reflection
In this post I'm going to show how to fill object properties using reflection no matter what types they are.
Let's take an example. Say, we have some input CSV file with header and values:
Manufacturer;Model;Color;OS;ReleaseDate;Rating
HTC;Titan;Black;Windows Phone 7.5;20111007;10.0
Samsung;Galaxy S3;White;Android 4.0.4;20120529;9.8
Nokia; Lumia 920;Yellow;Windows Phone 8;20121102;9.9
We are going to operate with these values in our application. First of all, we need to parse each line. It can be done manually or using some existing library. Parsed values we can store in a dictionary, for instance. Anyway, it's not a question of the topic.
So, we have parsed values and it would be more convenient to have a filled model with corresponding properties. Let's declare it:
using System;
using System.Drawing;
public class Device
{
public string Manufacturer { get; set; }
public string Model { get; set; }
public Color Color { get; set; }
public string OS { get; set; }
public DateTime ReleaseDate { get; set; }
public float Rating { get; set; }
}
As input data we have a dictionary (the key is a property name). We need to write code that accepts model type and input data. And then it should create, fill and return model instance. Note, that all input values are strings. We need to convert them to the appropriate type. And the ReleaseDate
has a custom format in the example, so we need to handle it separately.
Using reflection we can easily access each property: get its type, get or set value, get the type's converter to parse a string and create value. So, here is the code that does magic :)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
public static T CreateModel<T>(IDictionary<string, string> inputValues)
where T : new()
{
// Get the properties that should be filled. Take only public
// properties from the current type (not the base classes).
var properties = typeof(T).GetProperties(
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
// Create a model.
var model = new T();
// Fill each property of the model.
foreach (var property in properties)
{
// Get the string value from the input data dictionary.
var stringValue = inputValues[property.Name];
// Get the type of property that should be filled with
// value converted from the string value.
var type = property.PropertyType;
if (string.IsNullOrEmpty(stringValue))
{
// In this case we skip property so it
// will have default value for it's type.
continue;
}
// Create the value for the property.
object value;
// Convert DateTime separately because it has a custom
// format in our example.
if (type == typeof(DateTime))
{
// Format is hardcoded here but it can be retrieved,
// for instance, from the attribute attached to the property.
value = DateTime.ParseExact(
stringValue, "yyyyMMdd", CultureInfo.InvariantCulture);
}
else
{
// Need to convert string value to the type of property.
// First, we need to find converter for the type.
var converter = TypeDescriptor.GetConverter(type);
// Second, we use converter to get the proper value.
value = converter.ConvertFromString(stringValue);
}
// Set the property value.
property.SetValue(model, value, null);
}
return model;
}
And a short demo:
var dictinary = new Dictionary<string, string>
{
{ "Manufacturer", "HTC" },
{ "Model", "Titan" },
{ "Color", "Black" },
{ "OS", "Windows Phone 7.5" },
{ "ReleaseDate", "20111007" },
{ "Rating", "10.0" },
};
var device = CreateModel<Device>(dictinary);
It's quite easy, isn't it?