2016-07-11 2 views
2

람다 식의 매개 변수 유형을 한 유형에서 다른 유형으로 바꾸려고합니다.람다 식으로 매개 변수 유형 바꾸기

나는 stackoverflow 즉 this one에 대한 다른 답변을 발견했지만 그 (것)들과 운이 없었습니다.

도메인 객체와 도메인 객체를 검색 할 수있는 저장소가 있다고 상상해보십시오. 저장소는 도메인 개체 자체의 데이터 전송 객체를 처리하고지도를 반환해야하지만

:

ColourDto.cs을

public class DtoColour { 

    public DtoColour(string name) 
    { 
     Name = name; 
    } 

    public string Name { get; set; } 
} 

DomainColour.cs

public class DomainColour { 

    public DomainColour(string name) 
    { 
     Name = name; 
    } 

    public string Name { get; set; } 
} 

Repository.cs

public class ColourRepository { 
    ... 
    public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate) 
    { 
     // Context.Colours is of type ColourDto 
     return Context.Colours.Where(predicate).Map().ToList(); 
    } 
} 

도메인 모델에 대한 조건부이고 저장소의 Collection은 데이터 전송 개체의 모음이므로이 방법은 작동하지 않습니다. 마지막으로

테스트 시나리오

public class ColourRepository { 
    ... 
    public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate) 
    { 
     var visitor = new MyExpressionVisitor(); 
     var newPredicate = visitor.Visit(predicate) as Expression<Func<ColourDto, bool>>; 
     return Context.Colours.Where(newPredicate.Complie()).Map().ToList(); 
    } 
} 


public class MyExpressionVisitor : ExpressionVisitor 
{ 
    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     return Expression.Parameter(typeof(ColourDto), node.Name); 
    } 
} 

:

나는 예를 들면 슬로우이 작업을 수행하기 위해 ExpressionVisitor를 사용하려고했지만 그냥 예외없이 ParameterExpression의 유형을 변경하는 방법을 알아낼 수 없습니다 다음은 예외입니다.

System.ArgumentException : 'System.String Name'속성이이 아닙니다. 'ColourDto'유형에 대해 정의 된3210

누군가가 도움을 줄 수 있기를 바랍니다.

편집 : 여기에 여전히 dotnetfiddle

나던 작품입니다.

편집 : 여기에 각 유형에 대해 개별적으로 정의 된 작업 dotnetfiddle

감사 Eli Arbel

답변

3

당신은 작동이에 대한 몇 가지 작업을 수행해야합니다

  • Expression.Lambda에서 매개 변수 인스턴스를 모두 교체하고 어디서나 그들이 본문에 표시 - 모두에 대해 동일한 인스턴스를 사용합니다.
  • 람다의 대리자 형식을 변경하십시오.
  • 속성 식을 바꿉니다.

여기에 코드를 추가 제네릭,이다 :

public static Func<TTarget, bool> Convert<TSource, TTarget>(Expression<Func<TSource, bool>> root) 
{ 
    var visitor = new ParameterTypeVisitor<TSource, TTarget>(); 
    var expression = (Expression<Func<TTarget, bool>>)visitor.Visit(root); 
    return expression.Compile(); 
} 

public class ParameterTypeVisitor<TSource, TTarget> : ExpressionVisitor 
{ 
    private ReadOnlyCollection<ParameterExpression> _parameters; 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     return _parameters?.FirstOrDefault(p => p.Name == node.Name) ?? 
      (node.Type == typeof(TSource) ? Expression.Parameter(typeof(TTarget), node.Name) : node); 
    } 

    protected override Expression VisitLambda<T>(Expression<T> node) 
    { 
     _parameters = VisitAndConvert<ParameterExpression>(node.Parameters, "VisitLambda"); 
     return Expression.Lambda(Visit(node.Body), _parameters); 
    } 

    protected override Expression VisitMember(MemberExpression node) 
    { 
     if (node.Member.DeclaringType == typeof(TSource)) 
     { 
      return Expression.Property(Visit(node.Expression), node.Member.Name); 
     } 
     return base.VisitMember(node); 
    } 
} 
+0

이것이 작동하는 것 같습니다 : https://dotnetfiddle.net/qD3eHO –

+0

MakeMemberAccess를 사용하도록 VisitMember를 tweeked했습니다 : https://dotnetfiddle.net/HlZgPX –

0

속성입니다.

DomainColour에 의해 정의 된 속성 값을 ColourDto 값으로 가져올 수 없기 때문에이 오류가 발생합니다.

매개 변수를 사용하는 모든 MemberExpression을 방문하고 새 유형의 해당 속성을 사용하는 새 MemberExpression을 반환해야합니다.

+0

당신은 위의 시나리오에 대한 예를 가지고 있습니까? –