aspnet / Caching

[Archived] Libraries for in-memory caching and distributed caching. Project moved to https://github.com/aspnet/Extensions
Apache License 2.0
480 stars 217 forks source link

System.AppDomainUnloadedException when running UnitTests with RC2 #203

Closed divega closed 8 years ago

divega commented 8 years ago

From @Mertsch on June 3, 2016 12:20

After the upgrade from RC1 to RC2 some of my Unit Tests which test against the DB fail with the following exception. The behavior is reproducible, BUT only happens in a certain combination of tests. So I can't give a direct example as I am unable to find out which combination of my 90 tests causes this. Fact is each tests runs fine on its own, all ran fine together with RC1, all tests run fine under R# Test runner, TFS (VSTS) / Test in VS show the same behavior.

The line where the test fails is a DBContext accessor, but many other DBContext operations have been successful before the specific one.

Someone else seems to have the same issue http://stackoverflow.com/questions/37378698/ef-core-rc2-appdomainunloadedexception I am not using any In-Memory provider, only pure SQL Server

Each Unit Test builds up a completely new DI context, so that nothing is shared between Tests in any (static) way

Error Message:
   Test method xxx.ServiceTests.CleanupSuggestedTest threw exception: 
System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain.
Stack Trace:
    at System.Runtime.Remoting.ObjectHandle.Unwrap()
   at Microsoft.Extensions.Caching.Memory.CacheEntryHelper.get_Scopes()
   at Microsoft.Extensions.Caching.Memory.CacheEntryHelper.EnterScope(CacheEntry entry)
   at Microsoft.Extensions.Caching.Memory.MemoryCache.CreateEntry(Object key)
   at Microsoft.Extensions.Caching.Memory.CacheExtensions.Set[TItem](IMemoryCache cache, Object key, TItem value)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddAsyncQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at yyy.SomeService.<SavePackage>d__6.MoveNext() in C:\Build\_work\22\s\<Project>\Services\SomeService.cs:line 56
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at xxx.ServiceTests.<CleanupSuggestedTest>d__1.MoveNext() in C:\Build\_work\22\s\<Project>.Tests\ServiceTests.cs:line 75
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)

Copied from original issue: aspnet/EntityFramework#5643

divega commented 8 years ago

Moving to caching given the contents of the stack. @Tratcher this is the one I told you about.

Tratcher commented 8 years ago

Is this running against net451 or CoreCLR?

RE: https://github.com/aspnet/Caching/blob/dev/src/Microsoft.Extensions.Caching.Memory/CacheEntryHelper.cs

Tratcher commented 8 years ago

@sebastienros?

Tratcher commented 8 years ago

AppDomains, must be net451...

Mertsch commented 8 years ago

targeting net46 on system with net461 to be precise

muratg commented 8 years ago

Interesting. When upgrading to RC2, I assume you upgraded xunit too?

Looks like something weird's going on with the appdomains which I think xunit uses.

Mertsch commented 8 years ago

@muratg Actually it's MSTest I am not running under .NET Core, I am "only" using the framework inside a classic .NET app, tested with traditional MSTest

muratg commented 8 years ago

@Mertsch You mean a .csproj based project instead of a project.json based one?

Mertsch commented 8 years ago

@muratg Exactly.

frankseibel commented 8 years ago

I'm seeing the same issue. I have a plain old .NET class library using RC2 which I'm running MSTest tests against.

frankseibel commented 8 years ago

Let me add a little more to what I'm seeing. I have about 140 unit tests against several MVC 5 (ASP.NET 4.6) controllers which reference a class library using Entity Framework Core 1.0 RC2. When running the unit tests it gets through about 5 of them before getting the AppDomainUnloadedException. I can then run the failed unit tests and get through another group before getting the exception again. After 3 or 4 attempts they finally complete. Unfortunately in our automated build this will fail our build.

This was not happening with the same unit tests under RC1.

frankseibel commented 8 years ago

We were able to get past this issue. It seemed that methods on our controllers were asynchronous and the tests were not waiting for the threads to complete before continuing. This wasn't an issue until we upgraded to RC2. I'm not sure why our tests worked before to begin with. Either way, once we waited for the threads to complete this issue went away.

vmsgsp commented 8 years ago

We have the same issue. When calling DatabaseContext.Set.SingleOrDefault(this IQueryable source, Expression<Func<TSource, bool>> predicate) we get the AppDomainUnloadedException below.

The exception only occurs if tests from DIFFERENT MS TEST PROJECTS are executed within the same test run. Through debugging in [AssemblyInitialize] of the test projects, I see that they are run in different app domains. So maybe when it comes to the next test project your MemoryCache still has references to the previous app domain from the previous test project?

Maybe the issue is related to the commit https://github.com/aspnet/Caching/commit/f15fb804cdc2e14f9a64896817f3c6c343110820, because there this Remoting.ObjectHandle.Unwrap has been introduced.

DatabaseContext.Set<T>.SingleOrDefault<TSource>(this IQueryable source, Expression<Func<TSource, bool>> predicate); throws System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain. Result StackTrace:
at System.Runtime.Remoting.ObjectHandle.Unwrap() at Microsoft.Extensions.Caching.Memory.CacheEntryHelper.get_Scopes() at Microsoft.Extensions.Caching.Memory.CacheEntryHelper.GetOrCreateScopes() at Microsoft.Extensions.Caching.Memory.CacheEntryHelper.EnterScope(CacheEntry entry) at Microsoft.Extensions.Caching.Memory.CacheEntry..ctor(Object key, Action1 notifyCacheEntryDisposed, Action1 notifyCacheOfExpiration) at Microsoft.Extensions.Caching.Memory.MemoryCache.CreateEntry(Object key) at Microsoft.Extensions.Caching.Memory.CacheExtensions.Set[TItem](IMemoryCache cache, Object key, TItem value) at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)

vmsgsp commented 8 years ago

Now I found the root-cause in my case: The exception occurs when tests of different MS test projects are run. All of the test projects are run within the SAME PROCESS. But every MS test project is executed in a DIFFERENT APP DOMAIN. It seems that the MemoryCache still refers to items belonging to the previous app domain which already has been unloaded (when the previous MS test project run within the same process has terminated).

In my case the following WORKAROUND helps to overcome this limitation: In the MSTest [AssemblyInitialize] call System.Runtime.Remoting.Messaging.CallContext.LogicalSetData("CacheEntry.Scopes", null); which "clears the memory cache".

No idea, whether this workaround has other bad side-effects!

And of course this should be fixed in Microsoft.Extensions.Caching.Memory.CacheEntryHelper as soon as possible.

BrennanConroy commented 8 years ago

https://github.com/aspnet/Caching/commit/27a2072b142a675cde720eebf6a7686dda634fd8

andresmoschini commented 8 years ago

Good job @BrennanConroy fixing this issue!

Sadly @vmsgsp workaround does not work for me.

I am working with version 1.0.0, do you think that is there a simple and minimalist way to apply the fix in my code base?