I am using an expression tree as an alternative to automapper to map source properties to target properties using below code
What i am doing is, I have created static method inside static class for mapping and assigning inner child object property to outer object property
public static class PropertyMapper<TSource, TDest>
{
private static Expression<Func<TSource, Dictionary<string, MasterSection>, TDest>> _mappingExpression;
private static Func<TSource, Dictionary<string, MasterSection>, TDest> _mapper;
static PropertyMapper()
{
_mappingExpression = ProjectionMap();
_mapper = _mappingExpression.Compile();
}
public static Func<TSource, Dictionary<string, MasterSection>, TDest> Mapper => _mapper;
public static Expression<Func<TSource, Dictionary<string, MasterSection>, TDest>> ProjectionMap()
{
var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
var targetProperties= typeof(TDest).GetProperties().Where(p => p.CanWrite);
var propertyMap =
from d in targetProperties
join s in sourceProperties on new { d.Name, d.PropertyType } equals new { s.Name, s.PropertyType }
where d.Name != "SourceOfDataId" && d.Name!= "SourceOfData"
select new { Source = s, Dest = d };
var itemParam = Expression.Parameter(typeof(TSource), "item");
var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest, Expression.Property(itemParam, p.Source))).ToList();
var sourceOfDataIdProp = targetProperties.FirstOrDefault(s => s.Name == "SourceOfDataId");
if (sourceOfDataIdProp != null)
{
memberBindings.Add(Expression.Bind(sourceOfDataIdProp,Expression.Convert(Expression.Property(Expression.Property(itemParam, "SourceOfData"),"Id"),typeof(Guid?))));
}
var sourceOfDataProp = targetProperties.FirstOrDefault(s => s.Name == "SourceOfData");
if(sourceOfDataProp != null)
{
// here i would like to update `sourceOfData` object property "isApproved"
}
var newExpression = Expression.New(typeof(TDest));
var memberInitExpression = Expression.MemberInit(newExpression, memberBindings);
var projection = Expression.Lambda<Func<TSource, Dictionary<string, MasterSection>, TDest>>(memberInitExpression, itemParam, dictParam);
return projection;
}
}
and i am using the above method here in below to map the source properties to target properties
AirflowsLab = sourceMechanicalData.AirflowsLab.Select(a => PropertyMapper<LibraryLabAirflow, LibraryLabAirflow>.Mapper(a, masterSectionMappedLibrary)).ToList();
and the structure for LibraryLabAirflow is looks like as below
public class LibraryLabAirflow
{
[ForeignKey("SourceOfData")]
public Guid? SourceOfDataId { get; set; }
public virtual CodeStandardGuideline SourceOfData { get; set; }
}
Above mapping is working fine, what i am trying is now that i need to access the sourceOfData
child object of target and update the property for sourceOfData
and map that updated child object to source child object sourceOfData
.
below is the sourceOfData object details
"SourceOfData":{
"Id": "c5bf3585-50b1-4894-8fad-0ac884343935",
"IsApproved": null, // trying to set this to true instead of null inside target object
"MasterSection": null
},
I am not sure how to access the child object property using an expression tree in this scenario and i cannot use automapper library. Could any one please let me know how to access the child object property and update and assign back to target.
What i am trying to generate an expression looks like this source.SourceOfData = target.SourceOfData
but before this i need to update one of the property of target.SourceOfData
Many thanks in advance
Desired expression :
AirflowsLab = sourceMechanicalData.AirflowsLab.Where(a => a != null).Select(item => new LibraryLabAirflow()
{
SourceOfData = new CodeStandardGuideline()
{
IsApproved = true,// trying to set this through expression tree
City = item.SourceOfData.City
......
.......
}
}).ToList(),
trying like this is not working as well, 1
var sourceOfDataProp = targetProperties.FirstOrDefault(s => s.Name == "SourceOfData");
if(sourceOfDataProp != null)
{
// here need to get the sourceofdata properties
var sourceOfDataProperty = Expression.Property(Expression.Constant(sourceOfDataProp), "IsApproved");
}
Update:
i have implemented the logic inside if block of sourceOfDataProp != null
but getting an error
if (sourceOfDataProp != null)
{
var targetitemParam = Expression.Parameter(typeof(TTarget), "item");
var sourceOfDataPropertiesFilter = new List<string>()
{
"IsApproved"
};
var sourceItem = Expression.Property(itemParam, typeof(TSource).GetProperty("SourceOfData"));
var sourcePropertyInfo = sourceItem.Type.GetProperties().Where(p => p.CanRead);
var targetItem = Expression.Property(targetitemParam, typeof(TTarget).GetProperty("SourceOfData"));
var targetPropertyInfo = targetItem.Type.GetProperties().Where(p => p.CanWrite);
var sourceOfDataPropertyMap = from tp in targetPropertyInfo
join sp in sourcePropertyInfo
on new { tp.Name, tp.PropertyType } equals new { sp.Name, sp.PropertyType }
where !sourceOfDataPropertiesFilter.Contains(tp.Name)
select new { Source = sp, Target = tp };
// getting error at below line type of arguments does not match
var sourceOfDataMemberBindings = sourceOfDataPropertyMap.Select(p => Expression.Bind(p.Target, Expression.PropertyOrField(targetitemParam, "SourceOfData"))).ToList();
}
I have solved this problem like as below
if (sourceOfDataProp != null)
{
var targetItemParam = Expression.Parameter(typeof(TTarget), "item");
var sourceOfDataPropertiesFilter = new List<string>()
{
"IsApproved"
};
var sourceItem = Expression.Property(itemParam, typeof(TSource).GetProperty("SourceOfData"));
var sourceOfDataSourceProperties = sourceItem.Type.GetProperties().Where(p => p.CanRead);
var targetItem = Expression.Property(targetItemParam, typeof(TTarget).GetProperty("SourceOfData"));
var sourceOfDataTargetProperties = targetItem.Type.GetProperties().Where(p => p.CanWrite);
var sourceOfDataPropertyMap = sourceOfDataTargetProperties.Join(sourceOfDataSourceProperties,
t => new { t.Name, t.PropertyType },
s => new { s.Name, s.PropertyType },
(t, s) => new { Source = s, Target = t })
.Where(t => !sourceOfDataPropertiesFilter.Contains(t.Target.Name));
var sourceOfDataMemberBindings = sourceOfDataPropertyMap.Select(p => Expression.Bind(p.Target, Expression.Property(sourceItem, p.Source))).ToList();
var sourceOfDataIsApprovedProp = sourceOfDataTargetProperties.FirstOrDefault(s => s.Name == "IsApproved");
if (sourceOfDataIsApprovedProp != null)
{
sourceOfDataMemberBindings.Add(Expression.Bind(sourceOfDataIsApprovedProp, Expression.Constant(true, typeof(bool?))));
}
var sourceOfDataExpression = Expression.New(typeof(DesignHub.Entities.CodeStandardGuideline));
var sourceOfDataMemberInitExpression = Expression.MemberInit(sourceOfDataExpression, sourceOfDataMemberBindings);
memberBindings.Add(Expression.Bind(sourceOfDataProp, sourceOfDataMemberInitExpression));
}