如何创建复制所有匹配属性的LambdaExpression

时间:2013-08-07 08:25:51

标签: c# .net lambda expression-trees

我已经做了基本的事情,但我仍然坚持创建实际的LambaExpresion:

任何人都有一些关于我想写的内容的指针? var COPYEXPRESSION = ...

public Expression<Func<TSource, TDestination>> GetOrCreateMapExpression<TSource, TDestination>()
{
    return (Expression<Func<TSource, TDestination>>)
        _expressionCache.GetOrAdd(new TypePair(typeof(TSource), typeof(TDestination)), tp =>
        {
            return CreateMapExpression(tp.SourceType, tp.DestinationType);
        });
}

private LambdaExpression CreateMapExpression(Type source, Type destination)
{
    ParameterExpression instanceParameter = Expression.Parameter(source);


   var sourceMembers =  source.GetProperties();
   var destMembers = destination.GetProperties();
   var matchingMembers = sourceMembers.Select(s =>
       new
       {
           Source = s,
           Dest = destMembers.FirstOrDefault(d =>
               d.Name.Equals(s.Name) && d.PropertyType == s.PropertyType)
       }).Where(map => map.Dest != null).ToArray();


  var COPYEXPRESSION = ...

    return Expression.Lambda(COPYEXPRESSION , instanceParameter);
}

更新

我得到了正确的返回类型,但是在单元测试时,映射的类在属性上为null。

private LambdaExpression CreateMapExpression(Type source, Type destination)
{
    ParameterExpression instanceParameter = Expression.Parameter(source);
    var instance2Parameter = Expression.New(destination);
    LabelTarget returnTarget = Expression.Label(destination);

    var sourceMembers = source.GetProperties().Where(p => p.GetMethod.IsPublic);
    var destMembers = destination.GetProperties().Where(p => p.SetMethod.IsPublic);
    var matchingMembers = sourceMembers.Select(s =>
        new
        {
            Source = s,
            Dest = destMembers.FirstOrDefault(d =>
                d.Name.Equals(s.Name) && d.PropertyType == s.PropertyType)
        }).Where(map => map.Dest != null).ToArray();

    var block = Expression.Block(Expression.Block(
        matchingMembers.Select(p =>
            Expression.Assign(
                Expression.Property(instance2Parameter, p.Dest),
                Expression.Property(instanceParameter, p.Source)))),
                 Expression.Label(returnTarget, instance2Parameter));


    return Expression.Lambda(block, instanceParameter);
}

解决方案

这对我有用:

    return Expression.Lambda( Expression.MemberInit(Expression.New(destination),
        matchingMembers.Select(p =>
            Expression.Bind(p.Dest, Expression.Property(instanceParameter, p.Source)))),
            instanceParameter);

1 个答案:

答案 0 :(得分:1)

鉴于

ParameterExpression instanceParameter = Expression.Parameter(source);
ParameterExpression instance2Parameter = Expression.Parameter(destination);

你需要两个参数,一个用于源,一个用于目的地......

除非您正在构建新目的地,否则您需要Expression.Variable instance2Parameter,其中Expression.New

var block = Expression.Block(
                matchingMembers.Select(p =>       
                    Expression.Assign(
                        Expression.Property(instance2Parameter, p.Dest),
                        Expression.Property(instanceParameter, p.Source)))

这是一个包含所有Expression.Assign

的块

请注意,您应该检查Dest中是否存在setter,以及Source中是否存在getter(但最好在sourceMembers.Select中执行此操作。)