Übersetzen des Ausdrucksbaums von einem Typ in einen anderen Typ mit komplexen Zuordnungen

inspiriert vondiese Antwort Ich versuche, eine Eigenschaft einer Modellklasse einem Ausdruck zuzuordnen, der auf der tatsächlichen Entität basiert. Dies sind die beiden beteiligten Klassen:

<code>public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Id { get; set; }
    public DateTime? BirthDate { get; set; }
    public int CustomerTypeId { get; set; }
}

public class CustomerModel
{
    ...
    public bool HasEvenId { get; set; }
}
</code>

Ein Beispiel für einen möglichen Ausdruck, den ich konvertieren möchte, ist:

<code>Expression<Func<CustomerModel, bool>> from = model => model.HasEvenId;
Expression<Func<Customer, bool>> to = entity => ((entity.Id % 2) == 0);
</code>

Das Problem ist, dass ich einen OData-Endpunkt über ASP.NET WebAPI verfügbar machen muss, aber einige Vorgänge an den Entitäten ausführen muss, bevor ich sie ausführen kann, daher die Notwendigkeit einer Modellklasse und die Notwendigkeit, den Ausdruck basierend auf dem Modell zu übersetzen Ich könnte als OData-Abfrage einen Ausdruck erhalten, der auf der Entität basiert und den ich zum Abfragen von EF4 verwenden würde.

Dies ist, wo ich so weit gekommen bin:

<code>private static readonly Dictionary<Expression, Expression> Mappings = GetMappings();

private static Dictionary<Expression, Expression> GetMappings()
{
    var mappings = new Dictionary<Expression, Expression>();

    var mapping = GetMappingFor((CustomerModel model) => model.HasEvenId, (Customer customer) => (customer.Id%2) == 0);
    mappings.Add(mapping.Item1, mapping.Item2);

    return mappings;
}

private static Tuple<Expression, Expression> GetMappingFor<TFrom, TTo, TValue>(Expression<Func<TFrom, TValue>> fromExpression, Expression<Func<TTo, TValue>> toExpression)
{
    MemberExpression fromMemberExpression = (MemberExpression) fromExpression.Body;
    return Tuple.Create<Expression, Expression>(fromMemberExpression, toExpression);
}

public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression, Dictionary<Expression, Expression> mappings = null)
{
    if (expression == null)
        return null;

    string parameterName = expression.Parameters[0].Name;

    parameterName = string.IsNullOrWhiteSpace(parameterName) ? "p" : parameterName;

    var param = Expression.Parameter(typeof(TTo), parameterName);
    var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } };
    ParameterChangeVisitor parameterChange = new ParameterChangeVisitor(parameterName);
    if (mappings != null)
        foreach (var mapp in mappings)
            subst.Add(mapp.Key, parameterChange.Visit(mapp.Value));

    var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst);
    return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param);
}

public IQueryable<CustomerModel> Get()
{
    var filterExtractor = new ODataFilterExtractor<CustomerModel>();
    Expression<Func<CustomerModel, bool>> expression = filterExtractor.Extract(Request);
    Expression<Func<Customer, bool>> translatedExpression = Translate<CustomerModel, Customer>(expression, Mappings);

    IQueryable<Customer> query = _context.Customers;

    if (translatedExpression != null)
        query = query.Where(translatedExpression);

    var finalQuery = from item in query.AsEnumerable() 
                     select new CustomerModel()
                        {
                            FirstName = item.FirstName, 
                            LastName = item.LastName, 
                            Id = item.Id, 
                            BirthDate = item.BirthDate, 
                            CustomerTypeId = item.CustomerTypeId,
                            HasEvenId = (item.Id % 2 ) == 0
                        };

    return finalQuery.AsQueryable();
}
</code>

woher:

ODataFilterExtractor ist eine Klasse, die den $ filter-Ausdruck aus der erhaltenen RequestMessage extrahiert.ParameterChangeVisitor ändert nur den gesamten ParameterExpression in einen neuen mit der angegebenen Zeichenfolge als Parameternamen.

Außerdem habe ich die VisitMember-Methode der oben verlinkten Antwort folgendermaßen geändert:

<code>protected override Expression VisitMember(MemberExpression node)
{
    // if we see x.Name on the old type, substitute for new type
    if (node.Member.DeclaringType == _from)
    {
        MemberInfo toMember = _to.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SingleOrDefault();
        if (toMember != null)
        {
            return Expression.MakeMemberAccess(Visit(node.Expression), toMember);
        }
        else
        {
            if (_substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Any(me => me.Member.Equals(node.Member)))
            {
                MemberExpression key = _substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Single(me => me.Member.Equals(node.Member));
                Expression value = _substitutions[key];

                // What to return here?
                return Expression.Invoke(value);
            }
        }
    }
    return base.VisitMember(node);
}
</code>

Danke für deine Hilfe.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage