Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IEnumerable and ISet in queries with Contains #19636

Closed
IgorMenshikov opened this issue Jan 19, 2020 · 3 comments
Closed

IEnumerable and ISet in queries with Contains #19636

IgorMenshikov opened this issue Jan 19, 2020 · 3 comments

Comments

@IgorMenshikov
Copy link

IgorMenshikov commented Jan 19, 2020

I have a query that filters by a column by a set of values. If I use HashSet variable with set of values, everything works. If I pass IEnumerable or ISet variable of instance of HashSet, I get an exception "could not be translated"

Steps to reproduce

The query I use requests "operation" objects by some filters and create a list of custom class instances using received data:

// list of possible operation types
public enum OperationType { ... }

// set of types to filter with the query
private readonly static HashSet<OperationType> typeList = new HashSet<OperationType>() { ... }

// my query:
var list = (from i in context.DbSet<Operation>()
               where typeList.Contains(i.Type) &&
                      i.DeleteDate == null &&
                      i.PeriodID == parameters.PeriodID
              orderby i.PlanDate descending
              select new L.Item()
              {
                     Operation = i
              }).ToList();

It works well but if I change typeList to

private readonly static IEnumerable<OperationType> typeList = new HashSet<OperationType.>() { ... }

I get an exception:

System.InvalidOperationException: 'The LINQ expression 'DbSet<Operation>
    .Where(o => (IEnumerable<Code>)HashSet<Code> { ... }
        .Contains(o.Type) && o.DeleteDate == null && o.PeriodID == __parameters_PeriodID_0)' 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().

Further technical details

EF Core version: 3.1.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: NET Core 3.1.1
Operating system: Windows
IDE: Visual Studio 2019 16.4 latest

@ajcvickers
Copy link
Contributor

@IgorMenshikov As a workaround, try making the field not readonly.

Note for team: this looks like a duplicate of something I thought we fixed, but I can't find the duplicate and this still fails on master.

public class Operation
{
    public int Id { get; set; }
    public OperationType Type { get; set; }
    
    public DateTime PlanDate { get; set; }
    public DateTime? DeleteDate { get; set; }
    
    public int PeriodID  { get; set; }
}

public class Item
{
    public Operation Operation { get; set; }
}

public class Parameters
{
    public int PeriodID  { get; set; }
}

public enum OperationType
{
    One,
    Two
}

public class BloggingContext : DbContext
{
    private readonly ILoggerFactory Logger 
        = LoggerFactory.Create(c => c.AddConsole());//.SetMinimumLevel(LogLevel.Debug));

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseLoggerFactory(Logger)
            .EnableSensitiveDataLogging()
            .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Operation>();    
    }
}

public class Program
{
    private readonly static IEnumerable<OperationType> typeList = new HashSet<OperationType>
    {
        OperationType.One,
        OperationType.Two
    };
    
    public static async Task Main()
    {
        using (var context = new BloggingContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
        }

        using (var context = new BloggingContext())
        {
            var parameters = new Parameters();
            
            var list = (from i in context.Set<Operation>()
                where typeList.Contains(i.Type) &&
                      i.DeleteDate == null &&
                      i.PeriodID == parameters.PeriodID
                orderby i.PlanDate descending
                select new Item()
                {
                    Operation = i
                }).ToList();
        }
    }
}
Unhandled exception. System.InvalidOperationException: The LINQ expression 'DbSet<Operation>
    .Where(o => (IEnumerable<OperationType>)HashSet<OperationType> { One, Two, }
        .Contains(o.Type) && o.DeleteDate == null && o.PeriodID == __parameters_PeriodID_0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to cl
ient evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more informati
on.
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|8_0(ShapedQueryExpression translated, <>c__DisplayClass8_0& )
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)

@IgorMenshikov
Copy link
Author

I try to avoid accidental changes of variables. That is why I declare them "readonly" with minimal interface if possible.

One day I have create an issue #17877, so HashSet.Contains can be used for queries. Comments there tell it was implemented for generic ICollection.Contains. Maybe they are relative but does not look like if removal of "readonly" helps.

@smitpatel
Copy link
Contributor

Duplicate of #18658

@smitpatel smitpatel marked this as a duplicate of #18658 Jan 28, 2020
@smitpatel smitpatel removed their assignment Feb 5, 2020
@smitpatel smitpatel removed this from the 5.0.0 milestone Feb 5, 2020
@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants