简体   繁体   中英

set nested properties of object while mapping

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));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM