0

I have a generic query method defined for the Entity Framework as shown below. When I make my call to the API I'm getting back a list with tons of duplicate elements. Why does the .Distinct() appear to be doing nothing? I want the entire object to be looked at, not just a single property.

    internal IHttpActionResult GetCachedDataOrderedBy<TEntity, TResult>(Func<TEntity, TResult> selector, Func<TResult, string> orderby)
        where TEntity : class
    {
        using (var context = new DataModel.LabSOREntities())
            return Ok(context.Set<TEntity>()
                         .AsNoTracking()
                         .Select(selector)
                         .OrderBy(orderby)
                         .Distinct()
                         .ToArray());
    }

and I'm calling it like so:

   public IHttpActionResult GetCountries()
    {
        return GetCachedDataOrderedBy<vw_Location, Country>(r => new Country {
            CountryCode = r.Country_Code,
            CountryName = r.Country_Name,
            RegionCode = r.Region_Code
        }, r => r.CountryCode);
    }
dbc
  • 104,963
  • 20
  • 228
  • 340
Gargoyle
  • 9,590
  • 16
  • 80
  • 145
  • This doesn't seem to relate at all. I want the entire set of things to be distinct, not just comparing against a single key. – Gargoyle Mar 15 '18 at 23:12
  • 1
    If your entity is a reference type it will do the default comparison for referential equality. You're going to need to implement `IEqualityComparer` to pass into Distinct. – Jonathon Chase Mar 15 '18 at 23:13
  • That's exactly the problem, which is described in one of the answers. The default equality comparer compares by reference, so you have to implement your own – Camilo Terevinto Mar 15 '18 at 23:13
  • Here are some hints from our friend Jon Skeet https://stackoverflow.com/questions/1300088/distinct-with-lambda – Peter Kellner Mar 15 '18 at 23:33

1 Answers1

1

Your 'Country' class does not implement an appropriate Equals and GetHashCode method, so Distinct() is using reference equality and none of the objects are equal.

The bigger problem is that the use of a Func for your selector is materializing the query, so all the work is happening in the client. Your helper function needs to take expressions:

internal IHttpActionResult GetCachedDataOrderedBy<TEntity, TResult>(
    Expression<Func<TEntity, TResult>> selector, 
    Expression<Func<TResult, string>> orderby)

This should allow Entity Framework to compile the whole thing to a SQL expression and perform it on the SQL server. At that point the Equals and GetHashCode methods on Country are irrelevant because the SQL server is performing the work.

Jason
  • 369
  • 1
  • 4