Introduction
When working in C#, we may want to map one Model/Entity to another. Reflection is the key which can help us at this point.
So let’s see how to make a simple model or Entity mapper using reflections in C#.
Background
Let’s say we have some models like:
IStudent Interface
interface IStudent
{
long Id { get; set; }
string Name { get; set; }
}
Student Class
class Student : IStudent
{
public long Id { get; set; }
public string Name { get; set; }
}
StudentLog Class
class StudentLog : IStudent
{
public long LogId { get; set; }
public long Id { get; set; }
public string Name { get; set; }
}
Where Student
and StudentLog
, both have some common properties (name and type is the same).
MapperUtility
Here is the utility class which we are going to use for mapping:
TTarget MapTo<TSource, TTarget> (this TSource aSource, TTarget aTarget)
- maps properties of aSource
to given aTarget
, and returns the aTarget
TTarget CreateMapped<TSource, TTarget> (this TSource aSource)
- maps properties of aSource
to a newly created object of type TTarget
and returns it
Mapping would work for the properties where name
and type
are the same for TSource
and TTarget
.
public static class MapperUtility
{
public static TTarget MapTo<TSource, TTarget>(this TSource aSource, TTarget aTarget)
{
const BindingFlags flags = BindingFlags.Public |
BindingFlags.Instance | BindingFlags.NonPublic;
var srcFields = (from PropertyInfo aProp in typeof(TSource).GetProperties(flags)
where aProp.CanRead
select new
{
Name = aProp.Name,
Type = Nullable.GetUnderlyingType(aProp.PropertyType) ??
aProp.PropertyType
}).ToList();
var trgFields = (from PropertyInfo aProp in aTarget.GetType().GetProperties(flags)
where aProp.CanWrite
select new
{
Name = aProp.Name,
Type = Nullable.GetUnderlyingType(aProp.PropertyType) ??
aProp.PropertyType
}).ToList();
var commonFields = srcFields.Intersect(trgFields).ToList();
foreach (var aField in commonFields)
{
var value = aSource.GetType().GetProperty(aField.Name).GetValue(aSource, null);
PropertyInfo propertyInfos = aTarget.GetType().GetProperty(aField.Name);
propertyInfos.SetValue(aTarget, value, null);
}
return aTarget;
}
public static TTarget CreateMapped<TSource, TTarget>(this TSource aSource) where TTarget : new()
{
return aSource.MapTo(new TTarget());
}
}
Create New Mapped
Would create new mapped object of StudentLog
from object source of type Student
:
Student source = new Student() { Id = 1, Name = "Smith" };
StudentLog newMapped = source.CreateMapped<Student, StudentLog>();
Map to Existing One
Would map from target of type Student
to an existing aSourceLog
of type StudentLog
:
StudentLog aSourceLog = new StudentLog();
Student target = new Student();
target.Id = 2;
target.Name = "Tom";
aSourceLog = target.MapTo(aSourceLog);
target.MapTo(aSourceLog);
Limitations
- I have used framework 4.
- This mapper only works for classes. If you try with
struct
, it wouldn’t be able to map, or if you try to use List<Student>
, it would throw an error. - I only needed that much so I created it, if you need to do some more things, check out AutoMapper.
You can find the solution of VS2010 in the attachment.