Search code examples
c#.netdllazure-cosmosdbcultureinfo

Cosmos DB .NET SDK: could not find 'resources.dll' that never existed


Yes, I know this question looks similar to C# - 'Resources' DLL failing to be loaded as it doesn't exist, but my question is about a DLL that I do not own. That other question involves the author's own DLL that they wrote.

EDIT: after solving the problem, it turns out to have been the exact same scenario. I have self-flagged my own question as a duplicate.

Background

I am writing a C# library that uses the Cosmos DB .NET SDK 3.32.2. Sometimes, this library throws an exception because it could not find Microsoft.Azure.Cosmos.Direct.resources.dll. This file never existed in the first place; the Cosmos.Direct DLL ships with en-US invariant culture resources embedded inside the DLL. Yes, I decompiled it to check - Cosmos.Direct seems to be part of the SDK that the Cosmos team didn't release as open-source.

To be clear, I do not control Microsoft.Azure.Cosmos.Direct.dll. That is my dependency. FYI, the NuGet package for Microsoft.Azure.Cosmos ships with four DLLs, one of them being Microsoft.Azure.Cosmos.Direct.dll.

FooBar.OurCustomExceptionType: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.IO.FileNotFoundException: Could not load file or assembly 'file:///C:\Foo\Bar\Microsoft.Azure.Cosmos.Direct.resources.dll' or one of its dependencies. The system cannot find the file specified.

   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.InternalGetSatelliteAssembly(String name, CultureInfo culture, Version version, Boolean throwOnFileNotFound, StackCrawlMark& stackMark)
   at System.Resources.ManifestBasedResourceGroveler.GetSatelliteAssembly(CultureInfo lookForCulture, StackCrawlMark& stackMark)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(CultureInfo culture, Dictionary`2 localResourceSets, Boolean tryParents, Boolean createIfNotExists, StackCrawlMark& stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo requestedCulture, Boolean createIfNotExists, Boolean tryParents, StackCrawlMark& stackMark)
   at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)
   at System.Resources.ResourceManager.GetString(String name, CultureInfo culture)
   at Microsoft.Azure.Documents.StoreResult.CreateStoreResult(StoreResponse storeResponse, Exception responseException, Boolean requiresValidLsn, Boolean useLocalLSNBasedHeaders, Uri storePhysicalAddress)
   at Microsoft.Azure.Documents.StoreReader.<ReadMultipleReplicasInternalAsync>d__14.MoveNext()
   
   ... long stack trace ...

   at FooBar.OurCustomCosmosWrapper`1.<GetItemAsync>d__4.MoveNext()

Looking at the stack trace, the library is searching for a "satellite assembly" of resources that never existed, even though the resources are embedded in the DLL.

I have set my own app to use [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)] in AssemblyInfo.cs, but clearly that is not affecting this Cosmos dependency library. It only affects my own FooBar assembly that takes a dependency on the Cosmos SDK.

My question

How do I force my one of my dependency DLLs (in this case, Microsoft.Azure.Cosmos.Direct.dll) to use its own embedded resources instead of trying to find a separate resources.dll file that doesn't exist?

I bet this could be one of a few problems:

  • There is some way to set dependency assembly resource loading in App.config or some other project config file that I don't know about. Remember: I don't need to set this for my own library; I need to set this for one of my dependencies.
  • This is a bug in the Cosmos SDK.
  • I missed a culture-related setting somewhere when importing the Microsoft.Azure.Cosmos package.

Solution

  • It was not Cosmos's fault; we actually had an AppDomain.AssemblyResolve handler registered somewhere else that was intercepting assembly loads, even if they were embedded assemblies, and was attempting to load them from a specific location as a .dll file. This file never existed, so it would fail.

    That SO post I originally mentioned in my question? C# - 'Resources' DLL failing to be loaded as it doesn't exist

    It did in fact solve the problem. Just search your repo for AssemblyResolve, check to see if a handler function was registered for it, then makes sure that handler returns null (or nullptr if in C++) when it encounters one of your embedded resource assemblies.