EntityFramework-Plus icon indicating copy to clipboard operation
EntityFramework-Plus copied to clipboard

IncludeOptimized Multiple Levels

Open cangunaydin opened this issue 2 years ago • 2 comments

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

cangunaydin avatar Jan 23 '22 18:01 cangunaydin

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

JonathanMagnan avatar Jan 28 '22 00:01 JonathanMagnan

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?

Prinsn avatar Jan 28 '22 16:01 Prinsn