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

WhereBulkContains not compatible with multiple inheritance entity type (TPT)

Open casra-developers opened this issue 1 year ago • 1 comments

Description

If you have entities which inherit from each other, WhereBulkContains throws an exception.

Exception

Exception message: Oops! Untyped list is not compatible with multiple inheritance entity type (TPC, TPH, TPT).]
Stack Trace:

[System.Exception: Oops! Untyped list is not compatible with multiple inheritance entity type (TPC, TPH, TPT).]
   at .BulkInsert[T](DbContext this, BulkOperation`1 bulkOperation, IEnumerable`1 entities2, List`1 entitiesToUpdate)
   at .BulkInsert[T](BulkOperation`1 this, DbContext context, IEnumerable`1 entities, List`1 entitiesToUpdate)
   at DbContextExtensions.BulkInsert[T](DbContext this, IEnumerable`1 entities, Action`1 bulkOperationFactory)
   at Z.EntityFramework.Plus.WhereBulkContainsBuilder`1.BulkCopy(DbContext context, List`1 keyNames)
   at Z.EntityFramework.Plus.WhereBulkContainsBuilder`1.WhereBulkContains(DbContext context, String commandText, List`1 commandTableBuilders, Boolean isNotCTE)
   at Z.EntityFramework.Plus.QueryHookCommandInterceptor.ApplyHook(DbCommand command, DbContext context, String hook, Boolean isNotCTE)
   at Z.EntityFramework.Plus.QueryHookCommandInterceptor.ReaderExecuting(DbCommand command, DbCommandInterceptionContext`1 interceptionContext)
   at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<Reader>b__d(IDbCommandInterceptor i, DbCommand t, DbCommandInterceptionContext`1 c)
   at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext)
   at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)

[System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details.]
   at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)
   at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType](ObjectContext context, ObjectParameterCollection parameterValues)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
   at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__0[TResult](IEnumerable`1 sequence)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[TResult](Expression expression)
   at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.First[TSource](IQueryable`1 source)
   at Program.Main() :line 28

Fiddle or Project (Optional)

(https://dotnetfiddle.net/yMODPI)

Further technical details

  • EF version: 6.4.4
  • EF Extensions version: 6.14.3
  • Database Provider: SQL server 2019

casra-developers avatar Aug 10 '22 14:08 casra-developers

Hello @casra-developers ,

Yes, unfortunately, that's currently a limitation of our library.

At this moment, there is no short-term plan to support it either.

Best Regards,

Jon

JonathanMagnan avatar Aug 10 '22 15:08 JonathanMagnan

I also just hit this issue and was going to ask about it. I don't quite understand what the limitation is? The exception doesn't make a lot of sense to me. What is the "untyped list" it's complaining about? The list of long values representing the primary key of the entity?

jzabroski avatar Aug 18 '22 03:08 jzabroski

@casra-developers My typical workaround for something like this is to use System.Interactive Buffer() method and union the results of batches of 2,000 sorted ids at a time. It's important to sort the ids to improve the locality of reference of key range scans, to reduce acquisition costs of repeated attempts to acquire a Shared-Range Share lock on db table pages. It sucks because it adds many round trips and makes performance harder to reason about, and most junior developers have no idea what System.Interactive is or what Buffer() does, and it is stupid to take on a whole dll dependency to fix one workaround.

jzabroski avatar Aug 18 '22 03:08 jzabroski

Also, BulkSaveChanges does work cf https://dotnetfiddle.net/WDKsdz , which is a huge relief. I don't quite understand why BulkSaveChanges would work and this would not work?

jzabroski avatar Aug 18 '22 03:08 jzabroski

Hello @jzabroski ,

Both methods are completely different. We can surely find a way to make it works with WhereBulkContains, but that's currently not a priority as we have other requests that we need to complete. So unfortunately for now, that's currently a limitation of our library.

I agree that the exception currently doesn't make sense. This is due instead of verifying if we support the current WhereBulkContains, we just keep executing the code until this error happens. In this scenario, under the hood, an anonymous type is created (that we call untyped type).

JonathanMagnan avatar Aug 18 '22 14:08 JonathanMagnan

It makes sense that BulkSaveChanges would work now that I think about it - sorry - I really shouldn't post comments at midnight when my brain is half asleep.

jzabroski avatar Aug 18 '22 14:08 jzabroski

No worries!

Don't hesitate to contact us if you need further information.

Best regards,

Jon

JonathanMagnan avatar Aug 18 '22 14:08 JonathanMagnan