首页 > 解决方案 > 映射时设置对象的嵌套属性

问题描述

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

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

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

更新:

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

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

标签: c#.net.net-coreentity-framework-coreexpression-trees

解决方案


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

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

推荐阅读