Convert From Type to Type using System Reflection
Convert From Type to Type using System reflection
Introduction
Using Models view controller often requires converting the model from one type to another.
This is often done by assigning and manually casting type to another type.
In this article, we will learn how to generically and dynamically convert the model to another model using System.Reflection
and Attributes
.
Note: This code is writen on the spot, so there is no testing and the code has not been run.
Packages
In this article, we will use only one package and it's really optional: FastDeepCloner.
Using the Code
These are the test classes we will use in this example.
public class User
{
public string UserName { get; set; }
public string Email { get; set; }
public Person Person { get; set; } = new Person();
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
/// <summary>
/// This is the modelView that will contain data from both User and Person
/// </summary>
public class UserModelView
{
[PropertyCordinator("Person.FirstName$ $Person.LastName")]
public string Name { get; set; }
[PropertyCordinator("Email")]
public string UserEmail { get; set; }
public string UserName{ get; set; }
}
/// <summary>
/// This attribute is to map a property from One type to another type
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class PropertyCordinator : Attribute
{
public readonly string PropertyPath;
public PropertyCordinator(string propertyPath)
{
PropertyPath = propertyPath;
}
}
Normally, converting class User
to UserModelView
will be as follows:
var user = new User()
{
UserName = "TAlen",
Email = "xxx@gmail.com",
Person = new Person()
{
FirstName = "Alen",
LastName = "Toma"
}
};
// now converting this to UserModelView will usually be like this
var userModel = new UserModelView()
{
UserEmail = user.Email,
Name = $"{user.Person.FirstName} {user.Person.LastName}"
};
// we will learn to make this much easier by using System.Reflection
// like user.ToType<UserModelView>();
Now let's create the converter.
/// <summary>
/// Convert type of class to another type
/// this method cannot handle IList yet, you need to further develop it to use IList
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
/// <returns></returns>
public static T ToType<T>(this object item)
{
T model = (T)typeof(T).CreateInstance();
// using FastDeepcloner is much faster then
// item.GetType().GetProperties(), because it has caching mechanism.
List<IFastDeepClonerProperty> props = FastDeepCloner.DeepCloner.
GetFastDeepClonerProperties(model.GetType());
foreach (var p in props)
{
var propertyCordinate = p.GetCustomAttribute<PropertyCordinator>();
// if we have PropertyCordinator, then take it or assume the name property itself
string path = propertyCordinate?.PropertyPath ?? p.Name;
var value = GetPropertyValue(item, path);
if (value != null)
{
// There is no type converting in this case,
// for further developing where different propertytypes
// exist, we may need to convert the value
p.SetValue(model, value);
}
}
return model;
}
/// <summary>
/// Get the value by its path
/// </summary>
/// <param name="item"></param>
/// <param name="propertyName"></param>
/// <param name="properties"></param>
/// <returns></returns>
private static object GetPropertyValue(object item, string propertyName)
{
// try to split Person.FirstName$ $Person.LastName
var names = propertyName.Split('$');
object result = null;
foreach (var name in names)
{
if (name == " ") // add the space
{
if (result == null)
result = " ";
else result = result + " ";
continue;
}
object rItem = item;
/// try to split Person.FirstName
var subNames = name.Split('.');
foreach (var sn in subNames)
{
var p = FastDeepCloner.DeepCloner.GetFastDeepClonerProperties(rItem.GetType())
.FirstOrDefault(x =>
string.Equals(x.Name, sn, StringComparison.CurrentCultureIgnoreCase));
if (!p.IsInternalType) // e.g., Is class. Is Person Object or string FirstName
{
// We need to prepare when cases like Item is an Ilist,
// but we will skip this for this article.
rItem = p.GetValue(rItem);
}
else
{
var r = p.GetValue(rItem);
if (r == null)
continue;
if (r.GetType() == typeof(string))
{
if (result == null)
result = r;
else result = result.ToString() + r.ToString();
}
else
{
result = r; /// other types like Int, DateTime and so on.
}
}
}
}
return result;
}
UserModelView model = user.ToType<UserModelView>()
Points of Interest
Using System.Reflection
makes programing more fun and also much less code to write.
I hope I was able to show that to you in this article.
Those kind of methods are often used by a big libraries like JsonParser
or even EntityFramework
.
History
- 25th October, 2018: Initial version