0

I'm currently working on an EF website where I have this database object:

public partial class Association
{
    public Association()
    {
        this.Users = new HashSet<User>();
    }

    public Guid Id { get; set; }
    public Guid ItemId { get; set; }

    [ForeignKey("ItemId")]
    public virtual Item Item { get; set; }

    [InverseProperty("Association")]
    public virtual ICollection<User> Users { get; set; }
}

An Item class:

public partial class Item
{
    public Item()
    {
        this.Associations = new HashSet<Association>();
    }

    public Guid Id { get; set; }
    public ItemTypeEnum Type { get; set; }

    [InverseProperty("Item")]
    public virtual ICollection<Association> Associations { get; set; }
}

And this dictionary:

public static Dictionary<ItemTypeEnum, int> MaxOccupationRange = new Dictionary<ItemTypeEnum, int>()
    {
        { ItemTypeEnum.Type1, 1 },
        { ItemTypeEnum.Type2, 2 },
        { ItemTypeEnum.Type3, 5 },
        { ItemTypeEnum.Type4, 10 },
    };

I would need an Expression<Func<Association, bool>> that would return true if association.Users.Count < MaxOccupationRange[association.Item.Type], and else false.
I think this should roughly look like the following method but unfortunately, I'm not familiar enough with the Expression concept to achieve this kind of thing.

When debugging the following result, it seems like it computes (an not optimized but) correct expression.

public static Expression<Func<TSource, bool>> MyExprMethod<TSource, TKey, TKey2, TValue>(Expression<Func<TSource, TKey>> source, Expression<Func<TSource, TKey2>> source2, IReadOnlyDictionary<TKey, TValue> dict)
    {
        var body = dict
            .Aggregate((Expression)null, (next, dicEntry) => next == null
                ? Expression.Condition
                    (
                        Expression.LessThan(source2.Body, Expression.Constant(dicEntry.Value)),
                        Expression.Constant(true),
                        Expression.Constant(false)
                    )
                : Expression.Condition
                    (
                        Expression.Equal(source.Body, Expression.Constant(dicEntry.Key)),
                        Expression.Condition
                        (
                            Expression.LessThan(source2.Body, Expression.Constant(dicEntry.Value)),
                            Expression.Constant(true),
                            next
                        ),
                        next
                    )
                );

        return Expression.Lambda<Func<TSource, bool>>(body, source.Parameters[0]);
    }

I try to call it this way:

var expr = MyHelper.MyExprMethod((Association x) => x.Item.Type, (Association b) => b.Users.Count, Item.MaxOccupationRange);

But obviously it complains about the b parameter to be unassigned since it does not appear in the result lambda.
I tried many things (anonymous type instead of two sources etc.) but it seems that I'm too unfamiliar with the concept, I could not find a way to generate such an expression.

Would anybody please show me ?

Thanks a lot.

EDIT

Here's the call context:

var filter = PredicateUtils.True<Association>();

var expr = MyHelper.MyExprMethod((Association x) => x.Item.Type, (Association b) => b.Users.Count, Item.MaxOccupationRange);
filter = filter.And(expr);

var associations = entities.Associations.Where(filter);
return associations .OrderBy(a => a.Id).ToPagedList(pageNb, 2);

And here's the message, shown on the last line (ToPagedList): The parameter 'b' was not bound in the specified LINQ to Entities query expression

Flash_Back
  • 565
  • 3
  • 8
  • 31
  • Where does it complain about b? The above code compiles and runs, *besides* `EF`. –  Feb 04 '17 at 13:59
  • Thank for your answer, I edited the first post hoping this helps – Flash_Back Feb 04 '17 at 14:09
  • you want `Association x` and `Association b` to be the *same* parameter: have a look at my answer where I'm showing how to do this.. –  Feb 04 '17 at 15:34

1 Answers1

1

I guess you need to re-parametrize the second expression

    public static Expression<Func<TSource, bool>> MyExprMethod<TSource, TKey, TKey2, TValue>(Expression<Func<TSource, TKey>> source, Expression<Func<TSource, TKey2>> source2, IReadOnlyDictionary<TKey, TValue> dict)
    {
        source2 = PredicateRewriter.Rewrite(source2, source.Parameters[0]);
        var body = dict

through a helper

    public class PredicateRewriter
    {
        public static Expression<Func<T, U>> Rewrite<T,U>(Expression<Func<T, U>> exp, //string newParamName
            ParameterExpression param)
        {
            //var param = Expression.Parameter(exp.Parameters[0].Type, newParamName);
            var newExpression = new PredicateRewriterVisitor(param).Visit(exp);

            return (Expression<Func<T, U>>)newExpression;
        }

with a Visitor pattern

        private class PredicateRewriterVisitor : ExpressionVisitor
        {
            private readonly ParameterExpression _parameterExpression;

            public PredicateRewriterVisitor(ParameterExpression parameterExpression)
            {
                _parameterExpression = parameterExpression;
            }

            protected override Expression VisitParameter(ParameterExpression node)
            {
                return _parameterExpression;
            }
        }
    }
Community
  • 1
  • 1