10

ラムダ式のパラメーターの型をある型から別の型に置き換えようとしています。

私はstackoverflowで他の答え、つまりこれを見つけましたが、私はそれらに運がありませんでした。

ドメイン オブジェクトと、ドメイン オブジェクトを取得できるリポジトリがあると想像してみてください。

ただし、リポジトリは独自のデータ転送オブジェクトを処理し、ドメイン オブジェクトをマップして返す必要があります。

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

リポジトリ.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();
    }
}

ご覧のとおり、述語はドメイン モデル用であり、リポジトリ内のコレクションはデータ転送オブジェクトのコレクションであるため、これは機能しません。

これを行うために を使用しようとしましたが、たとえば、例外がスローされずにExpressionVisitorのタイプを変更する方法がわかりません。ParameterExpression

テストシナリオ

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

最後に、例外は次のとおりです。

System.ArgumentException : プロパティ 'System.String Name' は型 'ColourDto' に対して定義されていません

誰かが助けてくれることを願っています。

編集:ここにdotnetfiddleがあります

まだ動作しません。

編集:ここに作業中のdotnetfiddleがあります

ありがとうエリ・アーベル

4

2 に答える 2

13

これを機能させるには、いくつかのことを行う必要があります。

  • 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);
    }
}
于 2016-07-13T07:47:10.540 に答える
1

プロパティは、タイプごとに個別に定義されます。

DomainColourこのエラーは、 type の値からによって定義されたプロパティの値を取得できないために発生しますColourDto

パラメータを使用するすべてにアクセスし、新しい型からそのプロパティを使用MemberExpressionする新しいを返す必要があります。MemberExpression

于 2016-07-11T21:41:19.117 に答える