实体框架核心DbContext.RemoveRange和类型约束

时间:2019-02-12 20:42:51

标签: c# generics entity-framework-core

此代码引发异常

  

System.InvalidOperationException:找不到实体类型“列表<..>”。确保已将实体类型添加到模型中。

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

但是,如果添加类型约束where T: class,则不会引发异常。为什么是这样?我给人的印象是C#类型约束不影响这样的运行时行为。两种版本都可以正常编译。

1 个答案:

答案 0 :(得分:6)

这不是运行时行为,而是此处的编译时方法重载解析和协方差:

context.RemoveRange(existing);

RemoveRange方法有两个重载:

RemoveRange(IEnumerable<object> entities)

RemoveRange(params object[] entities)

具有类约束,C#编译器可以选择IEnumerable<object>来重载-因为ICollection<T>IEnumerable<T>,而引用类型IEnumerable<T>的{​​{1}}是协变的,因此是T

没有类约束,唯一可用的选项是带有IEnumerable<object>参数的方法。这是params object[]构造的缺点/副作用/陷阱之一-类型为params object[]的每个单个参数arg被视为object[]并隐式传递为{ {1}}。

所以,在前一种情况下,实际的呼叫是

object

在后一种情况下是

new object[] { arg }

换句话说,列表作为对象传递,这导致了相关的运行时异常。

context.RemoveRange((IEnumerable<object>)existing); 类的所有其他context.RemoveRange(new object[] { existing }); 方法-RangeDbContextAddRange也是如此。