zzzprojects / EntityFramework-Plus

Entity Framework Plus extends your DbContext with must-haves features: Include Filter, Auditing, Caching, Query Future, Batch Delete, Batch Update, and more
https://entityframework-plus.net/
MIT License
2.27k stars 318 forks source link

InvalidCastException when doing a batch update on Npgsql-based context using a SelectListIterator to filter #804

Closed mennomacomi closed 4 months ago

mennomacomi commented 4 months ago

Here is what to include in your request to make sure we implement a solution as quickly as possible.

1. Description

When using an Npgsql-based database context, running a batch update query on a collection filtered through a SelectListIterator throws a System.InvalidCastException

Example code:

class Model 
{
  public int Id { get; set; }
  public string Foo { get; set; }
}

class DatabaseContext : DbContext
{
  public DbSet<Model> Models { get; set; }
  public DatabaseContext(DbContextOptions options) : base(options) { }
}

class Program 
{
  public static void Main(string[] args) 
  {
    using var context = new DbContext(new DbContextOptionsBuilder<DatabaseContext>().UseNpgsql("<ConnectionString>").Options);
    context.Models.Add(new Model() { Id = 1, Foo = "foo" });
    context.SaveChanges();

    var idList = new List<int>() { 1 }
    // Doesn't have a real purpose of course, but for example's sake. The point is that idsToUpdate is a SelectListIterator
    var idsToUpdate = idList.Select(x => x);
    // Throws InvalidCastException
    context.Models.Where(x => idsToUpdate.Contains(x.Id)).Update(x => new() { Foo = "bar" });
  }
}

When changing .Where(x => idsToUpdate.Contains(x.Id)) to .Where(x => idList.Contains(x.Id)) it does work When using another provider (tested with InMemory and Sqlite) it does work

2. Exception

If you are seeing an exception, include the full exception details (message and stack trace).

Exception message:
System.InvalidCastException : Unable to cast object of type 'SelectListIterator`2[System.Int32, System.Int32]' to type 'System.Collections.Generic.IList`1[System.Int32]'.
Stack trace:
 lambda_method469(Closure, IEnumerable`1)
 <>c__DisplayClass6_0`2.<SanitizeConverter>b__1(Object v)
 RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
 MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)

3. Fiddle or Project

See example code above

4. Any further technical details

This was working fine in version 6.100.0.5 but is broken in the latest version

Further technical details

JonathanMagnan commented 4 months ago

Hello @mennomacomi ,

Thank you for reporting.

We will look at it.

Best Regards,

Jon

JonathanMagnan commented 4 months ago

Hello @mennomacomi ,

The v8.103.1.0 has been released today.

Let us know if the new version has fixed your issue correctly.

Best Regards,

Jon

mennomacomi commented 4 months ago

Hi @JonathanMagnan

I can confirm the new version has fixed the issue. Thanks so much for the quick response and fix!

Kind regards,

Menno