映射时设置对象的嵌套属性

set nested properties of object while mapping

我使用表达式树作为自动映射器的替代方法,使用以下代码将源属性映射到目标属性

我正在做的是,我在 static class 中创建了静态方法,用于将内部子对象 属性 映射和分配给外部对象 属性

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

我在下面使用上述方法将源属性映射到目标属性

AirflowsLab = sourceMechanicalData.AirflowsLab.Select(a => PropertyMapper<LibraryLabAirflow, LibraryLabAirflow>.Mapper(a, masterSectionMappedLibrary)).ToList();

LibraryLabAirflow 的结构如下所示

public class LibraryLabAirflow
{
    [ForeignKey("SourceOfData")]
    public Guid? SourceOfDataId { get; set; }
    public virtual CodeStandardGuideline SourceOfData { get; set; }
}

上面的映射工作正常,我正在尝试的是现在我需要访问目标的 sourceOfData 子对象并更新 sourceOfData 的 属性 和更新的映射子对象到源子对象 sourceOfData.

下面是 sourceOfData 对象的详细信息

"SourceOfData":{
                "Id": "c5bf3585-50b1-4894-8fad-0ac884343935",
                "IsApproved": null, // trying to set this to true instead of null inside target object
                "MasterSection": null
              },

我不确定在这种情况下如何使用表达式树访问子对象 属性,而且我不能使用 automapper 库。谁能告诉我如何访问子对象 属性 并更新并分配回目标。

我试图生成的表达式看起来像这样 source.SourceOfData = target.SourceOfData 但在此之前我需要更新 target.SourceOfData

的 属性 之一

非常感谢

想要表达 :

   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(),

这样尝试也不行,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");                    
 }

更新:

我已经在 sourceOfDataProp != null 的 if 块中实现了逻辑,但出现错误

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

我已经解决了这个问题,如下所示

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