Pete_ch
Pete_ch

Reputation: 1321

how should I make the following linq generic method returns distinct records?

when using the method below for an OR operation, I am getting back duplicate records. do I have to specify a custom IEqualityComparer? a simple distinct() does not work

internal static IQueryable<T> FilterEntity<T>(filters filters, IQueryable<T> entities)
    {
        if (filters.groupOp == "AND")
            foreach (var rule in filters.rules)
                entities = entities.Where<T>(
                    rule.field, rule.data,
                    (WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op)
                    );
        else
        {
            //Or
            IQueryable<T> temp = (new List<T>()).AsQueryable();
            foreach (var rule in filters.rules)
            {
                var t = entities.Where<T>(
                    rule.field, rule.data,
                    (WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op)
                    );

                    temp = temp.Concat<T>(t).AsQueryable();
            }
            entities = temp;
        }
        return entities;
    }

EDITED AFTER SUGGESTION FROM @usr below - This gives me the correct query in sql profiler (with a distinct) but this starts to look too convoluted - I wd like a cleaner solution

    internal static IQueryable<T> FilterEntity<T>(filters filters, IQueryable<T> entities)
    {
        if (filters.groupOp == "AND")
            foreach (var rule in filters.rules)
                entities = entities.Where<T>(
                    rule.field, rule.data,
                    (WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op)
                    );
        else
        {
            //Or
            var t1 = entities.Where<T>(filters.rules[0].field,filters.rules[0].data,
                (WhereOperation)StringEnum.Parse(typeof(WhereOperation),filters.rules[0].op)
                );
            for (int i = 1; i<filters.rules.Count(); i++)
            {

                var t = t1.Where<T>(filters.rules[i].field, filters.rules[i].data,
                (WhereOperation)StringEnum.Parse(typeof(WhereOperation), filters.rules[i].op)
                );
              t1.Concat<T>(t).AsQueryable();
            }
           entities  = t1;
        }
        return entities.Distinct<T>();
    }

Upvotes: 0

Views: 231

Answers (2)

Gert Arnold
Gert Arnold

Reputation: 109080

This is not related to Distinct() or IEqualityComparer. It is the modified closure gotcha, that is: the loop variable rule must be copied in the loop body:

foreach (var rule in filters.rules)
{
    var rule1 = rule;
    // work with rule1 only.

You can follow Usr's advice by doing this:

IQueryable<T> temp = null;
    ....
    foreach (var rule in filters.rules)
    {
        var rule1 = rule;
        var t = entities.Where<T>(rule1.field, rule1.data,
            (WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule1.op));

            if (temp == null)
                temp = t;
            else
                temp = temp.Union(t); // Union!!
    }
}
return temp;

I wonder if it solves your issue. Note the use of Union (which is an implicit Distinct). If it does not solve your issue I think there is some code invisible to us (e.g. in your WhereOperation) that interferes.

Upvotes: 1

usr
usr

Reputation: 171178

Don't start with a List<T> to start your query. Start with the first "t". That way you will get only one query which is executing on the server. Use Union instead of Concat to get distinct results.

Upvotes: 1

Related Questions