EntityFramework-Plus
EntityFramework-Plus copied to clipboard
IncludeOptimized Multiple Levels
1. Description
Hello after i upgraded my project to .net 6.0 and ef core 6.0, i was facing issue on the includeoptimized so normally what we have written before was breaking. here is an example code that we use
efContext.Offers.IncludeOptimized(o => o.OfferItems.Select(
oi => oi.OfferItemScreenTenants.Select(oist => oist.ScreenTenant)
)).FirstOrDefaultAsync(o => o.GroupId == offerGroupId && o.Status != OfferStatus.Archived);
and exception we are getting is
System.ArgumentException: GenericArguments[2], 'System.Collections.Generic.List`1[System.Collections.Generic.List`1[Program+Gift]]', on 'Void PopulateCollection[TCollection,TElement,TRelatedEntity](Int32, Microsoft.EntityFrameworkCore.Query.QueryContext, System.Data.Common.DbDataReader, Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryResultCoordinator, 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.Collections.Generic.IReadOnlyList`1[Microsoft.EntityFrameworkCore.ChangeTracking.ValueComparer], System.Collections.Generic.IReadOnlyList`1[Microsoft.EntityFrameworkCore.ChangeTracking.ValueComparer], System.Collections.Generic.IReadOnlyList`1[Microsoft.EntityFrameworkCore.ChangeTracking.ValueComparer], System.Func`5[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,Microsoft.EntityFrameworkCore.Query.Internal.ResultContext,Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryResultCoordinator,TRelatedEntity])' violates the constraint of type 'TRelatedEntity'.
---> System.Security.VerificationException: Method Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor+ShaperProcessingExpressionVisitor.PopulateCollection: type argument 'System.Collections.Generic.List`1[System.Collections.Generic.List`1[Program+Gift]]' violates the constraint of type parameter 'TRelatedEntity'.
at System.RuntimeMethodHandle.GetStubIfNeeded(RuntimeMethodHandleInternal method, RuntimeType declaringType, RuntimeType[] methodInstantiation)
at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
--- End of inner exception stack trace ---
at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessShaper(Expression shaperExpression, RelationalCommandCache& relationalCommandCache, LambdaExpression& relatedDataLoaders, Int32& collectionId)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at .(IQueryable , Action`1 , RelationalQueryContext& , Object& )
at Z.EntityFramework.Extensions.EFPlusExtensions.EFPlusCreateCommand(IQueryable source, Action`1 action, RelationalQueryContext& queryContext, Object& compiledQuery)
at Z.EntityFramework.Plus.BaseQueryFuture.CreateExecutorAndGetCommand(RelationalQueryContext& queryContext)
at Z.EntityFramework.Plus.QueryFutureBatch.CreateCommandCombined()
at Z.EntityFramework.Plus.QueryFutureBatch.ExecuteQueries()
at Z.EntityFramework.Plus.QueryFutureEnumerable`1.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Z.EntityFramework.Plus.QueryIncludeOptimizedParentQueryable`1.CreateEnumerable()
at Z.EntityFramework.Plus.QueryIncludeOptimizedParentQueryable`1.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Program.Main()
3. Fiddle or Project
i reproduce the issue in dotnetfiddle and did some experiments, https://dotnetfiddle.net/22qD0x as i see it, if you use SelectMany() then do the Select() the problem is gone and i can use IncludeOptimized()
.IncludeOptimized(o => o.OfferItems.SelectMany(oi => oi.OfferItemScreenTenants).Select(oist=>oist.ScreenTenant)) this query works instead.
so the question is what is the best way to use IncludeOptimized for multilevel objects? Is this a correct usage? thanks for the assistance
Hello @cangunaydin ,
The best way has changed over time as some way that was working in EF Core 2 are no longer working in EF Core 3 and so on.
For example, another working way is the following LINQ:
var list = context.Orders
.IncludeOptimized(x => x.Items.SelectMany(o=>o.OrderItemDetails))
.IncludeOptimized(x => x.Items.SelectMany(o=>o.OrderItemDetails).SelectMany(y=>y.Gifts)).ToList();
Is it the best way? Look pretty much similar as the code you provided.
There is no "perfect" way, this is the same under the hood for our library. Everything depends on the EF Core version and what this version can support.
Best Regards,
Jon
So the suggested migration here is to just change all instances of Select
to SelectMany
?
Is this a project level factor?
It seems like if IncludeOptomized(x => x.Select(z => z.IsCollectionType))
is always going to throw an exception, it should just be enforced?