You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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分别加载到独立的上下文,彻底隔离版本冲突:

  1. 整理文件结构

    • 在项目根目录创建ImpinjDependencies和ThingMagicDependencies两个子目录
    • 将Impinj的LLRP.dll(来自OctaneSDK依赖包)放入ImpinjDependencies
    • 将ThingMagic的LLRP.dll和MercuryAPI.dll放入ThingMagicDependencies
  2. 修改项目文件
    移除原有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>
    
  3. 编写加载逻辑
    通过自定义加载上下文分别加载不同厂商的程序集,使用反射调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 20:13:10