I've got some entities that looks like this:
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; }
//...
}
I want to add an extension method to do some filtering of the Encounters collection on MyEvent and return the value of MySpecialProperty for one of the MyEncounter instances (specifically, the instance where status = 1).
So, I wrote my extension method:
public static class MyExtensions
{
public static Guid? GetMySpecialProperty(this IEnumerable<MyEncounter> encounters)
{
return encounters.Where(e => e.Status == 1)
.Select(e => e.MySpecialProperty)
.FirstOrDefault();
}
}
Now, I'm ready to use my extension method. I've simplified this dramatically to remove stuff that should be irrelevant:
var qry = MyEventsContext.
.Where(<some logic>)
.Select(x => new MyViewModel
{
SpecialPropValue = x.Encounters.GetMySpecialProperty()
});
This compiles as expected, and I'm ready to run the query:
IList<MyViewModel> items = await qry.ToListAsync();
This is where things go wrong. As soon as .ToListAsync() is executed, I get this exception:
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'.
The inner exception is only slightly more detailed:
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'.
What is this exception telling me, and is there a way to resolve it? As far as I can tell, I've created my extension method correctly.
// 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()
});
I was mistaken about the above. Using that extension method in the query results in this exception:
When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type
What does work is doing all the logic inside the query, with no extension method at all:
// Query
var qry = MyEventsContext.
.Where(<some logic>)
.Select(x => new MyViewModel
{
SpecialPropValue = x.Encounters.Where(e => e.Status == 1)
.Select(x => x.MySpecialProperty)
.FirstOrDefault()
});