Entity Framework Core DbContext.RemoveRange and type constraints

c# entity-framework-core generics

Question

This code throws an exception

System.InvalidOperationException: The entity type 'List<..>' was not found. Ensure that the entity type has been added to the model.

private static void Update<T>(DbContext context, ICollection<T> existing, ICollection<T> updated)  // where T: class
{
      context.RemoveRange(existing); 
      updated.ToList().ForEach(existing.Add);
}

However, if you add the type constraint where T: class no exception is thrown. Why is this? I was under the impression C# type constraints didn't affect run time behavior like this. Both versions compile fine.

1
4
2/12/2019 9:44:39 PM

Accepted Answer

It's not the runtime behavior, but the compile time method overload resolution and covariance here:

context.RemoveRange(existing);

RemoveRange method has two overloads:

RemoveRange(IEnumerable<object> entities)

and

RemoveRange(params object[] entities)

Having class constraint allows C# compiler to pick the overload with IEnumerable<object> - because ICollection<T> is IEnumerable<T>, and IEnumerable<T> for reference type T is covariant, hence is IEnumerable<object>.

Without class constraint, the only available options is the method with params object[] argument. And here comes one of the drawbacks / side effects / traps of params object[] construct - every single argument arg with type other than object[] is treated as object and passed implicitly as new object[] { arg }.

So, in the former case the actual call is

context.RemoveRange((IEnumerable<object>)existing);

while in the later case it is

context.RemoveRange(new object[] { existing });

In other words, the list is passed as object, which leads to the runtime exception in question.

The same applies to all other Range methods of the DbContext class - AddRange, UpdateRange and AttachRange.

7
2/13/2019 12:07:46 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow