4

My simple line of code Db.CoverageParts.Add(newPart); is blowing the following exception from EF Core 5.0.6 / Castle 4.4.1 with Lazy Loading enabled:

System.NotImplementedException
  HResult=0x80004001
  Message=This is a DynamicProxy2 error: The interceptor attempted to 'Proceed' for method 'Microsoft.EntityFrameworkCore.Infrastructure.ILazyLoader get_LazyLoader()' which has no target. When calling method without target there is no implementation to 'proceed' to and it is the responsibility of the interceptor to mimic the implementation (set return value, out arguments etc)
  Source=Castle.Core
  StackTrace:
   at Castle.DynamicProxy.AbstractInvocation.ThrowOnNoTarget()
   at Castle.DynamicProxy.Internal.CompositionInvocation.EnsureValidTarget()
   at Castle.Proxies.Invocations.IProxyLazyLoader_get_LazyLoader.InvokeMethodOnTarget()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.DynamicProxy.StandardInterceptor.PerformProceed(IInvocation invocation)
   at Castle.DynamicProxy.StandardInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.MLCoveragePartProxy.get_LazyLoader()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrPropertyGetter`2.GetClrValue(Object entity)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetProperty(IPropertyBase propertyBase, Object value, Boolean isMaterialization, Boolean setModified, Boolean isCascadeDelete, CurrentValueType valueType)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetProperty(IPropertyBase propertyBase, Object value, Boolean isMaterialization, Boolean setModified, Boolean isCascadeDelete)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetServiceProperties(EntityState oldState, EntityState newState)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.SetEntityState(InternalEntityEntry entry, EntityState entityState)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.Add(TEntity entity)
   at --- SNIP THIS IS WHERE MY CODE APPEARS IN THE CALL STACK ---

This is a large project in the process of being upgraded from .NET Framework 4.8 to .NET Core 5. Lazy loading is enabled in EF Core. So far 97% of the code in this project is upgraded, tested, and verified working without any issues.

I am working to reproduce this issue with simplified code, but there is a lot to unwind to produce this issue. I've so far verified this issue occurs in a simple single-threaded version of my code with almost no use of async/await. Similar code throughout the app works without problems.

All libraries have been checked to be the latest RTM versions of .NET, EF Core, and Castle (though the latter is just a dependency of EF Core Proxies).

Can anyone offer any pointers that might give me an avenue of attack?

pbristow
  • 1,997
  • 4
  • 26
  • 46
  • 1
    Can you show your context, related entities, as well as actual code used when adding the entity to the context? – ESG May 30 '21 at 17:41
  • 1
    Great question @ESG. No, it's a very complex batch job in a fairly large app with quite a large context. That's why I said "I am working to reproduce this issue with simplified code." Sorry I'm not readily able to do that quickly, but I am working towards that end. – pbristow May 30 '21 at 18:50

1 Answers1

2

It dawned on me that it made no sense that anything would try, much less fail, to access the LazyLoader property on an entity that was just created. Naturally it's not yet bound to a context and the LazyLoader should be null, right??

In fact the real issue is that the new type is a dynamic proxy, not the concrete POCO. The problem stems from how the new entity is created. Because it is a derived type cloned from an instance of an existing entity of the same type, my code does the following:

var newCoveragePart = (CoveragePart)Activator.CreateInstance(oldCoveragePart.GetType());

The problem of course is that this clones a proxy, not the base type, which I assume throws big monkey wrenches in everything. This practice worked fine in EF for .NET Framework, so it's a bit shocking to realize it doesn't work in EF Core. Here's what's needed to fix the issue:

var cpType = (oldCoveragePart is IProxyLazyLoader ? oldCoveragePart.GetType().BaseType : oldCoveragePart.GetType()); 
var newCoveragePart = (CoveragePart)Activator.CreateInstance(cpType);

Side note: I also shallow-clone entities with AutoMapper and add them to the context after doing so. I'm going to have to review that code to make sure it doesn't have the same or similar problem.

pbristow
  • 1,997
  • 4
  • 26
  • 46
  • HI do you have an example on how to use AutoMapper to clone the entity and add them to the context ? thank you – Cristian Sala Jun 30 '21 at 07:02
  • 1
    @CristianSala Check out https://stackoverflow.com/a/5713636/358578. To add it to the context, you add it like you would any other object `_context.MyCollection.Add(myClonedEntity)` – pbristow Jun 30 '21 at 13:23