After installing .NET Framework 4.7.1, endpoints that were previously working can fail to start, throwing an exception instead. This occurs because 4.7.1 has moved support for .NET Standard 2.0 directly into the framework. As part of this, netstandard.dll is now registered in the global assembly cache (GAC). The assembly scanner ends up loading the assembly from the GAC instead of loading the copy included in the local bin folder.
Upon endpoint startup, an exception similar to the following is thrown:
Unhandled Exception: System.Exception: Could not load 'C:\LoadingRepro\bin\Debug\net461\netstandard.dll'. Consider excluding that assembly from the scanning. ---> System.IO.FileLoadException: API restriction: The assembly 'file:///C:\LoadingRepro\bin\Debug\net461\netstandard.dll' has already loaded from a different location. It cannot be loaded from a new location within the same appdomain.
at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, Boolean suppressSecurityChecks, StackCrawlMark& stackMark)
at System.Reflection.Assembly.ReflectionOnlyLoadFrom(String assemblyFile)
at NServiceBus.Hosting.Helpers.AssemblyScanner.ReferencesNServiceBus(String assemblyPath, Dictionary`2 processed, String coreAssemblyName)
at NServiceBus.Hosting.Helpers.AssemblyScanner.TryLoadScannableAssembly(String assemblyPath, AssemblyScannerResults results, Dictionary`2 processed, Assembly& assembly)
--- End of inner exception stack trace ---
at NServiceBus.Hosting.Helpers.AssemblyScanner.TryLoadScannableAssembly(String assemblyPath, AssemblyScannerResults results, Dictionary`2 processed, Assembly& assembly)
at NServiceBus.Hosting.Helpers.AssemblyScanner.GetScannableAssemblies()
at NServiceBus.EndpointConfiguration.GetAllowedTypes(String path)
at NServiceBus.EndpointConfiguration.Build()
at NServiceBus.Endpoint.Create(EndpointConfiguration configuration)
at NServiceBus.Endpoint.<Start>d__1.MoveNext()
--- 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`1.GetResult()
at LoadingRepro.Program.<Main>d__0.MoveNext() in C:\LoadingRepro\Program.cs:line 27
--- 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.GetResult()
at LoadingRepro.Program.<Main>(String[] args)
A customer raised a question about assembly scanning crashing after the Windows Fall Update. After rolling back that update, NServiceBus seems to work fine again. Apparently, an error is thrown when scanning netstandard.dll and the following error is thrown.
{System.IO.FileLoadException: API restriction: The assembly 'file:///<WEB APP LOCATION>\bin\Debug\net462\netstandard.dll' has already loaded from a different location. It cannot be loaded from a new location within the same appdomain.
at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, Boolean suppressSecurityChecks, StackCrawlMark& stackMark)
at System.Reflection.Assembly.ReflectionOnlyLoadFrom(String assemblyFile)
at NServiceBus.Hosting.Helpers.AssemblyScanner.ReferencesNServiceBus(String assemblyPath)
The issue was fixed by adding the following line to configuration
endpointConfiguration.AssemblyScanner().ExcludeAssemblies("netstandard");
The reason this is occurring is because the Fall Creators Update includes .NET Framework 4.7.1. This version has full .NET Standard 2.0 support built in, so none of the facade assemblies are required. This is mentioned in the release announcement: https://blogs.msdn.microsoft.com/dotnet/2017/10/17/announcing-the-net-framework-4-7-1/
The netstandard assembly is now in the GAC, and even when you're running < 4.7.1, the version in the GAC is loaded over the version that gets placed in the bin folder.
This seems to have introduced some compatibility problems not just limited to the reflection issue we're seeing: https://github.com/Microsoft/dotnet-framework-early-access/issues/9
This will hit anyone who installs 4.7.1 on any OS it will run on, not just the latest Windows 10 update.
To see the problem, the project needs to be targeting < 4.7.1 and have a dependency on something that is targeting netstandard2.0. If the project targets 4.7.1, then the netstandard assembly isn't in the project output.
It appears that V6 is the only version of NServiceBus that is impacted by this. I've tested V5 and not seen the problem. It's also already been addressed in V7 by adding an additional public key token to the default runtime assembly exclusion list.
It does appear that Microsoft is aware of the problem and should be fixing it at some point. Given that there are two workarounds available, (exclude the assembly, or target 4.7.1) does it seem like we should patch V6 to add the additional public key token that was added to V7?
FYI I've filed https://github.com/dotnet/corefx/issues/24847 to ensure this is something Microsoft is looking at.
closing this issue as the bug will be fixed in versions 6.4.2, 6.3.8 and 6.2.8. Note that NServiceBus version 5 and 7 are not affected by this bug.
Most helpful comment
The reason this is occurring is because the Fall Creators Update includes .NET Framework 4.7.1. This version has full .NET Standard 2.0 support built in, so none of the facade assemblies are required. This is mentioned in the release announcement: https://blogs.msdn.microsoft.com/dotnet/2017/10/17/announcing-the-net-framework-4-7-1/
The netstandard assembly is now in the GAC, and even when you're running < 4.7.1, the version in the GAC is loaded over the version that gets placed in the bin folder.
This seems to have introduced some compatibility problems not just limited to the reflection issue we're seeing: https://github.com/Microsoft/dotnet-framework-early-access/issues/9
This will hit anyone who installs 4.7.1 on any OS it will run on, not just the latest Windows 10 update.
To see the problem, the project needs to be targeting < 4.7.1 and have a dependency on something that is targeting netstandard2.0. If the project targets 4.7.1, then the netstandard assembly isn't in the project output.
It appears that V6 is the only version of NServiceBus that is impacted by this. I've tested V5 and not seen the problem. It's also already been addressed in V7 by adding an additional public key token to the default runtime assembly exclusion list.
It does appear that Microsoft is aware of the problem and should be fixing it at some point. Given that there are two workarounds available, (exclude the assembly, or target 4.7.1) does it seem like we should patch V6 to add the additional public key token that was added to V7?