Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data access within ChangeTracker.Tracked event throws InvalidOperationException #12615

Open
Tracked by #22954
optiks opened this issue Jul 10, 2018 · 5 comments
Open
Tracked by #22954

Comments

@optiks
Copy link

optiks commented Jul 10, 2018

Performing a data access operation within the ChangeTracker.Tracked event results in an InvalidOperationException being thrown due to the EF Core code being within a critical section.

We were really hoping to use this event to get around the lack of lifecycle hooks (#626).

In our specific case, we are porting a codebase from LINQ to SQL to EF Core. This codebase extensively used the LINQ to SQL OnLoaded() partial implementations.

Is there any chance that the Tracked event could be modified to allow data access to occur within the event delegate?

Exception message: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Stack trace:
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Load[TSource](IQueryable`1 source)
   at Microsoft.EntityFrameworkCore.Internal.EntityFinder`1.Load(INavigation navigation, InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.Load()
   at Microsoft.EntityFrameworkCore.ChangeTracking.CollectionEntry.Load()
   at Microsoft.EntityFrameworkCore.Internal.LazyLoader.Load(Object entity, String navigationName)
   at Microsoft.EntityFrameworkCore.Proxies.Internal.LazyLoadingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.EmployeeProxy.get_Devices()
   at EFCore_TrackedEventCriticalSection.TestDataContext.ChangeTracker_Tracked(Object sender, EntityTrackedEventArgs e) in C:\Users\Michael\Code\EFCore-TrackedEventCriticalSection\DataContext.cs:line 18
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.OnTracked(InternalEntityEntry internalEntityEntry, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.MarkUnchangedFromQuery(ISet`1 handledForeignKeys)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTrackingFromQuery(IEntityType baseEntityType, Object entity, ValueBuffer& valueBuffer, ISet`1 handledForeignKeys)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityTrackingInfo.StartTracking(IStateManager stateManager, Object entity, ValueBuffer& valueBuffer)
   at Microsoft.EntityFrameworkCore.Query.QueryContext.StartTracking(Object entity, EntityTrackingInfo entityTrackingInfo)
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.<_TrackEntities>d__17`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at EFCore_TrackedEventCriticalSection.Program.Main(String[] args) in C:\Users\Michael\Code\EFCore-TrackedEventCriticalSection\Program.cs:line 21</StackTrace><ExceptionString>System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
   at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Load[TSource](IQueryable`1 source)
   at Microsoft.EntityFrameworkCore.Internal.EntityFinder`1.Load(INavigation navigation, InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.Load()
   at Microsoft.EntityFrameworkCore.ChangeTracking.CollectionEntry.Load()
   at Microsoft.EntityFrameworkCore.Internal.LazyLoader.Load(Object entity, String navigationName)
   at Microsoft.EntityFrameworkCore.Proxies.Internal.LazyLoadingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.EmployeeProxy.get_Devices()
   at EFCore_TrackedEventCriticalSection.TestDataContext.ChangeTracker_Tracked(Object sender, EntityTrackedEventArgs e) in C:\Users\Michael\Code\EFCore-TrackedEventCriticalSection\DataContext.cs:line 18
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.OnTracked(InternalEntityEntry internalEntityEntry, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.MarkUnchangedFromQuery(ISet`1 handledForeignKeys)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTrackingFromQuery(IEntityType baseEntityType, Object entity, ValueBuffer&amp;amp; valueBuffer, ISet`1 handledForeignKeys)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityTrackingInfo.StartTracking(IStateManager stateManager, Object entity, ValueBuffer&amp;amp; valueBuffer)
   at Microsoft.EntityFrameworkCore.Query.QueryContext.StartTracking(Object entity, EntityTrackingInfo entityTrackingInfo)
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.&amp;lt;_TrackEntities&amp;gt;d__17`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at EFCore_TrackedEventCriticalSection.Program.Main(String[] args) in C:\Users\Michael\Code\EFCore-TrackedEventCriticalSection\Program.cs:line 21</ExceptionString></Exception></TraceRecord>
An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.EntityFrameworkCore.dll
A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

Steps to reproduce

Repro included at https://github.com/optiks/EFCore-TrackedEventCriticalSection.

At a high level:

  1. Subscribe to the ChangeTracker.Tracked event
  2. Force a tracked entity to be materialised
  3. Within the tracked event delegate, perform a data access operation (e.g. a lazy load).

Further technical details

EF Core version: 2.1
Database Provider: EntityFrameworkCore.SqlServerCompact35
Operating system: Windows 10.0.17134.112
IDE: Visual Studio 2017 15.7.2

@optiks
Copy link
Author

optiks commented Jul 10, 2018

@divega As discussed on Skype, this is the issue we hit when using the Tracked event as a workaround for the lack of an ObjectMaterialized/OnLoaded type event (further discussion regarding this in #626).

We're currently working around this via custom Enumerator/IQueryProvider implementations, however we're not overly happy with it.

@divega divega added this to the 2.2.0 milestone Jul 11, 2018
@divega
Copy link
Contributor

divega commented Jul 11, 2018

@ajcvickers Let's discuss next time we have a chance.

@divega
Copy link
Contributor

divega commented Jul 27, 2018

@ajcvickers you mentioned you had some thoughts about this. Any chance you can summarize?

@ajcvickers
Copy link
Member

Talked about it with @divega and it looks like we need to investigate:

  • What the query critical section contains
  • Whether the message should be updated
  • That this will then work with either buffering or MARS
  • Whether or not this can be made to not require MARS by enabling buffering by default

@ajcvickers
Copy link
Member

Opened #14534 for the query critical section part. Leaving this issue to track the Tracked event work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants