.NET项目中同名不同版本LLRP.dll依赖冲突的解决方法
问题
我正在开发一个同时使用Impinj和Vulcan两家厂商RFID扫描仪的项目:
- Impinj通过NuGet提供名为OctaneSDK的库,其依赖的LLRP.dll包含在libltknet-sdk中
- Vulcan使用ThingMagic提供的库,需手动下载引用,该库也依赖LLRP.dll
这两个LLRP.dll文件互不相同且不兼容,目前项目运行时默认使用的是Impinj依赖的版本,导致Vulcan相关功能无法正常工作。请问有没有办法分离这两个程序集,让各自的依赖使用对应的版本?
参考项目文件(F#开发,语言不影响问题)
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net10.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="OctaneSDK" Version="5.0.0" /> <PackageReference Include="System.IO.Ports" Version="8.0.0" /> </ItemGroup> <ItemGroup> <Reference Include="MercuryAPI, Version=1.12.0.0, Culture=neutral, PublicKeyToken=3b7d0c33b1517ad9, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>Assemblies/MercuryAPI.dll</HintPath> </Reference> <Reference Include="LLRP, Version=1.0.0.6, Culture=neutral, PublicKeyToken=92f466b54dae9fc4"> <SpecificVersion>False</SpecificVersion> <HintPath>Assemblies/LLRP.dll</HintPath> </Reference> </ItemGroup> <ItemGroup> <Compile Include="Program.fs" /> </ItemGroup> </Project>
示例程序代码
open ThingMagic try let reader = Reader.Create("tmr:///com4") reader.Connect () reader.ParamSet("/reader/region/id", Reader.Region.NA2) let readPlan = SimpleReadPlan(null, TagProtocol.GEN2, null, null, 1000) reader.ParamSet("/reader/read/plan", readPlan); let reads = reader.Read(2000) reads |> Array.iter(fun i -> printfn $"{i.EpcString}" ) with ex -> printfn $"{ex.Message}"
解决方案
方法1:独立目录隔离+AssemblyLoadContext加载(最可靠)
通过自定义AssemblyLoadContext将两个LLRP.dll分别加载到独立的上下文,彻底隔离版本冲突:
整理文件结构
- 在项目根目录创建
ImpinjDependencies和ThingMagicDependencies两个子目录 - 将Impinj的LLRP.dll(来自OctaneSDK依赖包)放入
ImpinjDependencies - 将ThingMagic的LLRP.dll和MercuryAPI.dll放入
ThingMagicDependencies
- 在项目根目录创建
修改项目文件
移除原有LLRP.dll引用,调整MercuryAPI引用路径并添加文件复制任务:<ItemGroup> <PackageReference Include="OctaneSDK" Version="5.0.0" /> <PackageReference Include="System.IO.Ports" Version="8.0.0" /> </ItemGroup> <ItemGroup> <Reference Include="MercuryAPI, Version=1.12.0.0, Culture=neutral, PublicKeyToken=3b7d0c33b1517ad9, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>ThingMagicDependencies/MercuryAPI.dll</HintPath> <Private>False</Private> <!-- 禁止自动复制到输出目录,由自定义任务处理 --> </Reference> </ItemGroup> <!-- 自定义构建后复制依赖文件的任务 --> <Target Name="CopyCustomDependencies" AfterTargets="Build"> <Copy SourceFiles="ImpinjDependencies\LLRP.dll" DestinationFolder="$(OutputPath)\ImpinjDependencies" /> <Copy SourceFiles="ThingMagicDependencies\*.dll" DestinationFolder="$(OutputPath)\ThingMagicDependencies" /> </Target>编写加载逻辑
通过自定义加载上下文分别加载不同厂商的程序集,使用反射调用ThingMagic功能避免直接引用冲突:open System open System.Reflection open System.Runtime.Loader // Impinj专属程序集加载上下文 type ImpinjLoadContext() = inherit AssemblyLoadContext("ImpinjContext", isCollectible = false) override _.LoadFromAssemblyPath(path: string) = base.LoadFromAssemblyPath(path) // ThingMagic专属程序集加载上下文 type ThingMagicLoadContext() = inherit AssemblyLoadContext("ThingMagicContext", isCollectible = false) override _.LoadFromAssemblyPath(path: string) = base.LoadFromAssemblyPath(path) // 加载并使用ThingMagic阅读器 let initThingMagicReader () = let tmContext = ThingMagicLoadContext() let mercuryPath = Path.Combine(AppContext.BaseDirectory, "ThingMagicDependencies", "MercuryAPI.dll") let mercuryAssembly = tmContext.LoadFromAssemblyPath(mercuryPath) // 通过反射创建阅读器实例并执行操作 let readerType = mercuryAssembly.GetType("ThingMagic.Reader") let createMethod = readerType.GetMethod("Create", [|typeof<string>|]) let reader = createMethod.Invoke(null, [|"tmr:///com4"|]) let connectMethod = readerType.GetMethod("Connect") connectMethod.Invoke(reader, null) reader // Impinj阅读器使用保持正常,默认加载自身依赖的LLRP.dll let initImpinjReader () = let reader = Impinj.OctaneSdk.Reader() // 执行Impinj阅读器操作 reader
方法2:程序集绑定重定向(仅限强名称差异场景)
如果两个LLRP.dll的版本号或公钥令牌不同,可以通过配置文件指定不同调用者加载对应版本:
对于.NET 5+项目,在根目录创建YourProjectName.runtimeconfig.json:
{ "runtimeOptions": { "assemblyBinding": { "dependencies": [ { "name": "LLRP", "publicKeyToken": "92f466b54dae9fc4", "version": "1.0.0.6", "path": "ThingMagicDependencies/LLRP.dll" }, { "name": "LLRP", "publicKeyToken": "替换为Impinj的LLRP公钥令牌", "version": "替换为Impinj的LLRP版本号", "path": "ImpinjDependencies/LLRP.dll" } ] } } }
注意:此方法要求两个LLRP.dll的强名称信息(版本、公钥令牌)完全不同,否则无法区分。
方法3:进程隔离封装
如果上述方法均失效,可将其中一个厂商的RFID功能封装为独立控制台应用或服务,通过进程间通信(如管道、gRPC)与主程序交互,彻底隔离依赖环境。
内容的提问来源于stack exchange,提问作者JackEncore
相关产品推荐
相关产品推荐

