如何强制Cosmos DB依赖DLL使用内嵌资源,避免寻找不存在的resources.dll
我在编写一个使用Cosmos DB .NET SDK 3.32.2的C#库时,偶尔会抛出找不到Microsoft.Azure.Cosmos.Direct.resources.dll的异常。但这个文件本来就不存在——我反编译确认过,Cosmos.Direct DLL已经把en-US不变区域性资源内嵌在自身里了,而且Cosmos.Direct是Cosmos团队未开源的SDK部分。
需要明确的是,我无法控制Microsoft.Azure.Cosmos.Direct.dll,它是我的依赖项。Microsoft.Azure.Cosmos的NuGet包包含四个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()
从栈跟踪来看,这个依赖库在寻找根本不存在的资源附属程序集,哪怕资源已经内嵌在DLL里了。我已经在自己的AssemblyInfo.cs中设置了[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)],但这只影响我自己的FooBar程序集,对Cosmos的依赖库无效。
我的核心问题是:如何强制依赖DLL(这里是Microsoft.Azure.Cosmos.Direct.dll)使用自身内嵌的资源,而不是尝试寻找不存在的独立resources.dll文件?
可能的原因
- 存在某种在App.config或其他项目配置文件中设置依赖程序集资源加载的方式(需针对依赖库而非自身库);
- Cosmos SDK存在bug;
- 导入Microsoft.Azure.Cosmos包时遗漏了某些区域性相关设置。
1. 配置应用默认区域性
强制应用使用en-US区域性,避免触发非中性资源的查找逻辑:
- 对于.NET Framework项目,在App.config/Web.config中添加:
<configuration> <system.web> <globalization culture="en-US" uiCulture="en-US" /> </system.web> </configuration>
- 对于.NET Core/.NET 5+项目,在启动代码中设置:
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.GetCultureInfo("en-US"); CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo("en-US");
2. 拦截AssemblyResolve事件
通过订阅AppDomain.AssemblyResolve事件,当系统尝试加载不存在的资源附属DLL时,返回主程序集:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var assemblyName = new AssemblyName(args.Name); if (assemblyName.Name.Equals("Microsoft.Azure.Cosmos.Direct.resources", StringComparison.OrdinalIgnoreCase)) { return Assembly.Load("Microsoft.Azure.Cosmos.Direct"); } return null; };
这个逻辑会让资源管理器直接使用主DLL中的内嵌资源,跳过对附属程序集的查找。
3. 升级Cosmos SDK版本
如果是SDK自身的bug,尝试升级到更高版本的Microsoft.Azure.Cosmos NuGet包,后续版本大概率修复了这类资源加载问题。
4. 临时 workaround:手动创建资源DLL
如果上述方法都无效,可以反编译Microsoft.Azure.Cosmos.Direct.dll提取内嵌资源,手动生成Microsoft.Azure.Cosmos.Direct.resources.dll并放入en-US目录中,但这只是临时方案,不推荐长期使用。
内容的提问来源于stack exchange,提问作者Phil

