【问题标题】:Custom LINQ extension method throws VerificationException: type argument IEnumerable`1[entity]' violates constraint of type parameter 'TCollection'自定义 LINQ 扩展方法抛出 VerificationException:类型参数 IEnumerable`1[entity]' 违反类型参数“TCollection”的约束
【发布时间】:2020-10-16 18:42:55
【问题描述】:

我有一些看起来像这样的实体:

public class MyEvent 
{
    // ...
    public IEnumerable<MyEncounter> Encounters { get; set; } // Navigation property
    // ...
}

public class MyEncounter
{
    public Guid MyEventID { get; set; }
    public MyEvent MyEvent { get; set; }  // Navigation property

    public Guid? MySpecialProperty { get; set; }

    public int Status { get; set; }
    //...
}

我想添加一个扩展方法来对 MyEvent 上的 Encounters 集合进行一些过滤,并为其中一个 MyEncounter 实例(特别是 status = 1 的实例)返回 MySpecialProperty 的值。

所以,我写了我的扩展方法:

public static class MyExtensions
{
    public static Guid? GetMySpecialProperty(this IEnumerable<MyEncounter> encounters)
    {
        return encounters.Where(e => e.Status == 1)
                         .Select(e => e.MySpecialProperty)
                         .FirstOrDefault();
    }
}

现在,我准备好使用我的扩展方法了。我已经大大简化了这一点,以删除不相关的内容:

var qry = MyEventsContext.
    .Where(<some logic>)
    .Select(x => new MyViewModel
    {
        SpecialPropValue = x.Encounters.GetMySpecialProperty()   
    });

这会按预期编译,我已准备好运行查询:

IList<MyViewModel> items = await qry.ToListAsync();

这就是问题所在。只要.ToListAsync() 被执行,我就会得到这个异常:

VerificationException: Method Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor+CustomShaperCompilingExpressionVisitor.PopulateCollection: type argument 'System.Collections.Generic.IEnumerable`1[<fully qualified name of MyEncounter>]' violates the constraint of type parameter 'TCollection'.

内部异常只是稍微详细一点:

ArgumentException: GenericArguments[0], 'System.Collections.Generic.IEnumerable`1[<fully qualified name of MyEncounter>]', on 'Void PopulateCollection[TCollection,TElement,TRelatedEntity](Int32, Microsoft.EntityFrameworkCore.Query.QueryContext, System.Data.Common.DbDataReader, Microsoft.EntityFrameworkCore.Query.Internal.ResultCoordinator, System.Func`3[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,System.Object[]], System.Func`3[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,System.Object[]], System.Func`3[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,System.Object[]], System.Func`5[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,Microsoft.EntityFrameworkCore.Query.Internal.ResultContext,Microsoft.EntityFrameworkCore.Query.Internal.ResultCoordinator,TRelatedEntity])' violates the constraint of type 'TCollection'.

这个异常告诉我什么,有没有办法解决它?据我所知,我已经正确地创建了我的扩展方法。

有趣的是,如果我创建一个扩展方法来获取与条件匹配的 MyEncounters 并在查询中执行 Select/FirstOrDefault,一切都会按预期工作:
// Extension method
public static IEnumerable<MyEncounter> GetMySpecialEncounters(this IEnumerable<MyEncounter> encounters)
{
    return encounters.Where(e => e.Status == 1)
}

// Query
var qry = MyEventsContext.
    .Where(<some logic>)
    .Select(x => new MyViewModel
    {
        SpecialPropValue = x.Encounters.GetMySpecialEncounters()
                                       .Select(x => x.MySpecialProperty)
                                       .FirstOrDefault()   
    });
罢工>
我对上述内容有误。在查询中使用该扩展方法会导致此异常:
When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type

起作用的是在查询中执行所有逻辑,根本没有扩展方法:

// Query
var qry = MyEventsContext.
    .Where(<some logic>)
    .Select(x => new MyViewModel
    {
        SpecialPropValue = x.Encounters.Where(e => e.Status == 1)
                                       .Select(x => x.MySpecialProperty)
                                       .FirstOrDefault()   
    });

【问题讨论】:

  • 您的代码无法编译。
  • @TanveerBadar 它并不是真的要编译。我已经从一堆不同的文件中包含了相关内容的 sn-ps。
  • 我应该更清楚。 MyEvent 的导航道具缺少名称,因此有点难以理解。不管能不能编译,“心理”都很难编译。
  • 另外,看看使用ToList() 是否可以解决问题。我自己也注意到异步方法有一些奇怪的行为。
  • @TanveerBadar 啊,我明白了!我已经修正了那个错字。我也尝试在执行查询时使用.ToList(),但它没有改变任何东西。

标签: c# entity-framework-core asp.net-core-mvc


【解决方案1】:

x.Encounters 上调用 ToList 在 3.1 EF Core 中对我有效(尽管 AsEnumerable()ToArray() 不适用):

.Select(x => new MyViewModel
{
    SpecialPropValue = x.Encounters.ToList().GetMySpecialProperty()
});

这种行为的原因是键入问题 - CustomShaperCompilingExpressionVisitor.VisitExtension 方法尝试处理填充集合并尝试构建表达式树以调用 PopulateCollection 方法,该方法具有集合类型的通用约束为 ICollection&lt;TElement&gt; 但确定 IEnumerable&lt;TElement&gt;并将其传递给MakeGenericMethod 调用失败,因为IEnumerable 不是ICollection(实际上反之亦然)。

【讨论】:

  • 将模型中的导航属性集合从IEnumerable 更改为ICollection 解决了这个问题。谢谢。
猜你喜欢
  • 2016-11-16
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 1970-01-01
  • 1970-01-01
  • 2016-09-30
  • 1970-01-01
  • 2011-06-23
相关资源
最近更新 更多