Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core 3 x.Contains() in expression where x is ICollection

I've got the following data layer setup:

public class Repository : IRepository {

    private readonly MyDbContext _dbContext;

        public List<Meter> Search(Expression<Func<Meter,bool>> criteria)
            IQueryable<Meter> results = _dbContext.Meters;
            return results.Where(criteria).ToList();
        }
    }
}

... from a client class:

IRepository _repository;

public void ClientMethod () {

    ICollection<int> ids = new List<int>() {1, 2, 3);
    var results = _repository.Search(c=> ids.Contains(c.Id)); // This throws exception

}

This results in the exception:

expression Where(source: DbSet, predicate: (m) => (Unhandled parameter: __ids_0).Contains(m.Id))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()

But if I change the collection reference to IEnumerable or List, it works:

public void ClientMethod () {

    // This works
    List<int> ids = new List<int>() {1, 2, 3);
    var results = _repository.Search(c=> ids.Contains(c.Id)); 

    // This works
    IEnumerable<int> ids = new List<int>() {1, 2, 3);
    var results = _repository.Search(c=> ids.Contains(c.Id)); 
}

Why is it not working for ICollection, but it does for IEnumerable and List? Many of my client methods take an ICollection as a parameter.

I'm using EF Core 3.0 but I believe I had the same problem in 2.1, it just didn't throw as it evaluated it on the client instead.

like image 727
zola25 Avatar asked Sep 29 '19 15:09

zola25


3 Answers

This is a known bug fixed in 3.1. 3.0 is almost unusable due to all regressions after the query pipeline rewrite.

I recommend following the issue tracker on github for ef core.

https://github.com/dotnet/efcore/pull/17599

like image 200
joakimriedel Avatar answered Nov 12 '22 12:11

joakimriedel


When you cast to IEnumerable() or List() EFCore forces Client side evaluation, which is why it works. (but works at a performance cost)

  • "Prior to version 3.0, Entity Framework Core supported client evaluation anywhere in the query" Reference
  • Version of EFCore prior to 3.0 also allowed you to add a warning if you mixed Server and Client evaluation.
  • Then EF changed policy to not allow you to mix Server and Client evaluation, so accidental performance issues don't happen. It's a little more complicated than that though, so here is a link to EFCore 3.1 breaking changes *All other versions can be found here as well in the left menu navigation, including 3.0.
like image 35
CANDIMAN Avatar answered Nov 12 '22 12:11

CANDIMAN


I've run into the same issue, when migrating to EF Core 3.0 from 2.2. I've had ef configured before to throw error on client side evaluation, and it was working fine. So I'm sure it's a new issue with 3.0, introduced when they've rewritten the linq engine.

Casting it to either IEnumerable or List works for me as well, thanks for the tip!

like image 8
Gergo Drazsdik Avatar answered Nov 12 '22 14:11

Gergo Drazsdik