C# Aggregate for property Expression returns AmbiguousMatchException

Viewed 475

I have following (simplified) classes:

public abstract class BaseSite
{
    public int SiteId { get; set; }
    public string Name { get; set; }
}

public class OptionalSite : BaseSite
{
    public new int? SiteId { get; set; }
}

And the following method:

    public static Expression<Func<T, bool>> PredicateExtension<T>(this IQueryable<T> source, string member, object value, string expression)
    {
        ParameterExpression item = Expression.Parameter(typeof(T), "item");
        Expression memberValue = member.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
        Type memberType = memberValue.Type;
        if (value != null && value.GetType() != memberType)
            value = Convert.ChangeType(value, memberType);
        Expression condition = null;
        switch (expression)
        {
            case "==":
                condition = Expression.Equal(memberValue, Expression.Constant(value, memberType));
                break;
            case "!=":
                condition = Expression.NotEqual(memberValue, Expression.Constant(value, memberType));
                break;
            case "<":
                condition = Expression.LessThan(memberValue, Expression.Constant(value, memberType));
                break;
            case ">":
                condition = Expression.GreaterThan(memberValue, Expression.Constant(value, memberType));
                break;
            case "<=":
                condition = Expression.LessThanOrEqual(memberValue, Expression.Constant(value, memberType));
                break;
            case ">=":
                condition = Expression.GreaterThanOrEqual(memberValue, Expression.Constant(value, memberType));
                break;
            default:
                break;
        }
        if (condition == null)
            condition = Expression.Equal(memberValue, Expression.Constant(value, memberType));
        var predicate = Expression.Lambda<Func<T, bool>>(condition, item);

        return predicate;
    }

Now when calling the method with the following parameters:

LinqExtentions.PredicateExtension<OptionalSite>(SiteDbSet, "SiteId", 1, "==");

I have the following issue: On the second line of the method there is a Aggregate call but this gives me the AmbiguousMatchException. The reason is that the property SiteIdis defined both in the base class and in the OptionalSite class (public new ...) ...

So the question here is: how can I get the correct Expression using this (or another) method? I will probably need to get the same Expression result but using a different way of getting it so that when properties are found in the base class and the implemented class that I can choose for the property on the class that implements the base class.

EDIT:

The type of SiteId changes from int to int?. Other classes that implement this base class need to have it as a required property (EF) but this class needs it an optional property. Because of this I cannot use the virtual keyword in my base class.

1 Answers

For information on why you get the AmbiguousMatchException and how to solve it, you could look at this answer.

You would have to use a more advanced function:

Expression memberValue = member.Split('.').Aggregate((Expression)item, (expr, name) =>
{
    // get all properties with matching name
    var properties = expr.Type.GetProperties().Where(p => p.Name == name);
    // if only one found, use that, else use the one that is declared in the derived type
    var property = properties.Count() == 1 ? properties.First() : properties.Single(p => p.DeclaringType == expr.Type);
    // make expression from this PropertyInfo
    return Expression.Property(expr, property);
});

Note that is just a basic approach. It doesn't take fields into account (shouldn't be an issue with EF) and there could be several levels of inheritance with the property declared anywhere in-between. But you get the idea.

Related