当 运行 应用程序来自 dotnet 发布输出文件夹时无法加载文件或程序集 Newtonsoft.Json

Could not load file or assembly Newtonsoft.Json when running app from the dotnet publish output folder

我发现 Newtonsoft.Json 库抛出

有问题
System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'. The system cannot find the file specified

当 运行将应用程序作为 Docker 容器时,我想知道为什么会发生这种情况以及为什么依赖项管理不能顺利进行。

我使用.NET 5

我有一个库 MyLibrary.A,它明确使用 Newtonsoft.Json 13.0.1 来序列化和反序列化 json。

我有一个不同的库 MyLibrary.B,它包装了一个 MassTransit.AmazonSQS 库。这个 MassTransit 库也使用 Newtonsoft.Json,但可能是不同的版本。 如果我不做任何明确的事情,MassTransit 依赖项似乎会显示 Newtonsoft.Json 11.0.2。如果在 MyLibrary.B 我明确添加 Newtonsoft.Json 13.0.1,即使我没有明确使用它,那么 MassTransit 似乎很乐意使用这个最新的 Newtonsoft.Json 13.0.1

现在,我有一个使用 MyLibrary.AMyLibrary.B 的网络应用 MyApp。它在本地运行良好,但我使用 CI/CD 服务器生成 Docker 图像。

现在,我将这个 Docker 图像作为本地容器​​(作为 Docker Compose)旋转,但出现错误

Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'. The system cannot find the file specified.

它抱怨一个甚至不存在的版本。没有 13.0.0.0,这个库好像是从 12.0.3 到 13.0.1.

我现在正在检查我所有的库并确保它们都明确地使用 Newtonsoft.Json 13.0.1。当我检测到其中一些使用依赖于 Newtonsoft.Json 的第三方时,我明确添加了完全相同的版本,这样我就可以在任何地方获得 13.0.1 版本。


更新 1:我的解决方法无效。我不知道还能尝试什么。

我什至已将 Newtonsoft.Json 13.0.1 明确添加到我的网络应用程序中,所以我希望至少在 运行 时间它可用。

此外,如果我 运行 我的网站在本地作为标准的 kestrel AspNetCore 应用程序 (.NET 5) 启动,它会正常启动。到底是怎么回事?为什么我的 docker 容器抱怨找不到 Newtonsoft.Json 13.0.0.0

这些是尝试 运行 作为 Docker 容器时的痕迹

docker run -p 8080:80 \
>     -e ASPNETCORE_ENVIRONMENT=Production \
>     registry.gitlab.com/sample/foo-integration-service:latest
Unable to find image 'registry.gitlab.com/sample/foo-integration-service:latest' locally
latest: Pulling from sample/foo-integration-service
07aded7c29c6: Pull complete 
97aff7269a5a: Pull complete 
633b89d569a5: Pull complete 
bd0e639a2ac9: Pull complete 
a9a5571a369e: Pull complete 
9569d825ee3a: Pull complete 
Digest: sha256:5499b40392512f1731890ccf1ee13507769b733ee2f30c95d281f0550f7a892e
Status: Downloaded newer image for registry.gitlab.com/sample/foo-integration-service:latest
Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'. The system cannot find the file specified.

File name: 'Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at foo.ItgService.Program.Main(String[] args) in /builds/sample/foo-integration-service/src/foo.ItgService/Program.cs:line 10

更新 2:我决定在我的库中随处匹配 MassTransit 依赖项 Newtonsoft.Json 11.0.2问题依旧。 现在的错误是

Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'. The system cannot find the file specified.

我不明白。我对 Newtonsoft.Json 的所有依赖现在都是 11.0.2 并且它仍然在抱怨。我将在 Web 应用程序主程序集中显式添加此版本(即使我并不直接需要它),看看它是否仍然抱怨它。


更新 3:将 Newtonsoft.Json 11.0.2 添加到我的所有库并将完全相同的依赖项作为依赖项添加到我的 Web 应用程序程序集后,仍然存在同样的问题。

根据 Chris 的评论,我现在有一个 .dockerignore

bin/
obj/

我构建图像的方式是,使用 GitLab,使用标准 dotnet builddotnet publish,然后像这样将发布文件夹的所有内容复制到 Docker 图像中

FROM mcr.microsoft.com/dotnet/aspnet:5.0
COPY publish/ .
EXPOSE 80
ENTRYPOINT ["dotnet", "MyCompany.ItgService.dll"]

更具体地说,我使用 Kaniko,这是我的 .gitlab-ci.yml

image: mcr.microsoft.com/dotnet/sdk:5.0

variables:
  GIT_DEPTH: 1000
  PUBLISH_OUTPUT_DIR: publish
  ENTRYPOINT_DLL: ReplaceMe.dll
  CLUSTER_NAME: ReplaceMe
  SERVICE_NAME: ReplaceMe

stages:
  - build
  - test
  - publish
  - delivery

build:
  stage: build
  script:
    - dotnet restore --no-cache --force
    - dotnet build --configuration Release --no-restore
  artifacts:
    paths:
    - test
    expire_in: 8 hour
  rules:
    - if: $CI_COMMIT_TAG
      when: never  
    - when: always

test:
  stage: test
  services:
    - name: localstack/localstack:0.12.17.5
      alias: localstack
  variables:
    # Localstack with SNS and SQS
    AWS_DEFAULT_REGION: "us-east-1"
    EDGE_PORT: "4566"
    SERVICES: "sns,sqs"
  before_script:
    - rounds=10;
      while [ $rounds -gt 0 ]; do
        curl http://localstack:4566 && echo OK && break || echo FAIL
        rounds=$rounds - 1;
        sleep 5;
      done;
  script: dotnet test --blame --configuration Release
  rules:
    - if: $CI_COMMIT_TAG
      when: never  
    - exists:
      - test/**/*Tests.csproj

publish:
  stage: publish
  before_script:
    - export PATH=$PATH:/root/.dotnet/tools
    - dotnet tool install --global GitVersion.Tool --version 5.7.0
    - dotnet gitversion
    - SEMVER=$(dotnet gitversion -showvariable semver)
    - mkdir version
    - echo "${SEMVER}" > ./version/semver
    - APP_VERSION=$(cat ./version/semver)
  script:
    - dotnet publish -c Release -o $PUBLISH_OUTPUT_DIR -p:Version=$APP_VERSION
  artifacts:
    paths: 
      - $PUBLISH_OUTPUT_DIR/
      - version/
    expire_in: 8 hour
  rules:
    - if: $CI_COMMIT_TAG
      when: never  
    - when: always

container_registry:
  stage: delivery
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  before_script:
    - IMAGE_TAG=$(cat ./version/semver)
    - echo "bin/" > $CI_PROJECT_DIR/.dockerignore
    - echo "obj/" > $CI_PROJECT_DIR/.dockerignore
    - echo "FROM mcr.microsoft.com/dotnet/aspnet:5.0" > $CI_PROJECT_DIR/Dockerfile
    - echo "COPY $PUBLISH_OUTPUT_DIR/ ." >> $CI_PROJECT_DIR/Dockerfile
    - echo "EXPOSE 80" >> $CI_PROJECT_DIR/Dockerfile
    - echo "ENTRYPOINT [\"dotnet\", \"$ENTRYPOINT_DLL\"]" >> $CI_PROJECT_DIR/Dockerfile
    - cat $CI_PROJECT_DIR/Dockerfile
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - cat /kaniko/.docker/config.json
  script:
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:latest --destination $CI_REGISTRY_IMAGE:$IMAGE_TAG
  rules:
    - if: $CI_COMMIT_TAG
      when: never
    - when: always

我不知道 dotnet publish 是否将 docker 搞砸了,或者我的流程是否存在缺陷(它对所有其他服务都适用)。可能是什么问题?

我可以在 dotnet publish 生成的文件中看到 Newtonsoft.Json.dll 以及 MassTransit.AmazonSqsTransport 和所有其他应该被正确复制到 Docker 图像的文件。

此外,如果我打开 .deps.json 我可以看到所有对 Newtonsoft.Json 的引用都是 11.0.2,所以不再有版本冲突(我认为!)。

我没主意了。


更新 4 我刚刚清除了所有本地 nuget 包(使用 Ubuntu)

dotnet nuget locals all --clear
Clearing NuGet HTTP cache: /home/diegosasw/.local/share/NuGet/v3-cache
Clearing NuGet global packages folder: /home/diegosasw/.nuget/packages/
Clearing NuGet Temp cache: /tmp/NuGetScratch
Clearing NuGet plugins cache: /home/diegosasw/.local/share/NuGet/plugins-cache
Local resources cleared.

有趣的是,当我在我的项目上恢复依赖关系时

dotnet restore

我可以在 /home/diegosasw/.nuget/packages/newtonsoft.json 看到 有版本 11.0.29.0.1

所以我猜某个地方有一个子依赖项正在使用 Newtonsoft 9.0.1,即使我的 dotnet publish 生成的 *.deps.json 中没有 Newtonsoft.Json 9.0.1 的踪迹,我想知道这是否与我的问题有关,也许是因为那是正在加载的程序集并且 11 被忽略了?


更新 5 我刚刚看到 Newtonsoft.Json 9.0.1 正在被一些测试项目使用,因为我可以在 coverlet.core 1.0.0Microsoft.Extensions.DependencyModel 2.1.0

下的 coverlet.collector.deps.json 跟踪它

我想这不是我的问题的原因。

我还用 docker export $(docker ps -lq) -o foo.tar 验证了容器有 Newtonsoft.Json.dll.

我想了解为什么会发生这种情况,并了解如何更好地解决此类问题。


更新 6(10 月 4 日)

我不认为 Docker 有问题。我认为问题可能与 dotnet publish 或我遗漏或做错的事情有关。

我把 Docker 留在外面了,因为我没有发现那里有什么不对劲。 我试着简单地做一个

dotnet publish -c Release -o publish

并在其中执行应用程序 使用 dotnet MyCompany.ItgService.dll 发布文件夹以重现异常。

但在发布之前,当我 运行 申请时

dotnet run -c Release --project src/Rubiko.ItgService

我没有得到那个例外。

请参阅 https://github.com/dotnet/sdk/issues/21716 了解完整的详细信息、跟踪、树结构等。

总结

问题是:


最终更新:问题已解决。请参阅我自己的回复,其中有关如何正确解决此类问题的信息以及我如何通过确保我的测试项目(使用不同版本的库)不发布工件和覆盖所需的依赖程序集来解决它。

使用MassTransit依赖的版本即可,比v13早很多。在没有正确的程序集重定向的情况下升级过去可能会导致您的问题。

我解开了谜底。它与 Docker 无关。这有点与 dotnet publish 有关,但 SDK 运行良好。

正如最初怀疑的那样,问题是版本冲突。使用 dotnet publish -c Release -o publish 发布时,我可以在那里看到 Newtonsoft.Json.dll。但是以下让我怀疑

ls publish/ -al | grep Newtonsoft
-rwxrw-r-- 1 diegosasw  89K mar 22  2017 Newtonsoft.Json.Bson.dll*
-rwxrw-r-- 1 diegosasw 641K mar 24  2018 Newtonsoft.Json.dll*

2018 对于那个版本来说似乎有点老了。如果……正在发布的 Newtonsoft.Json 程序集毕竟不是 11.0.2 版本怎么办?

我更新了我所有的库以匹配 MassTransit 对 Newtonsoft 11.0.2 的依赖,但我在 Update 4 上的发现让我认为还有一些其他项目间接依赖于 Newtonsoft.Json 9.0.1,这就是为什么我可以看到正在本地缓存 nuget 包。

如果以某种方式发布的程序集不是预期的 Newtonsoft.Json 11.0.2,而是 Newtonsoft.Json 9.0.1,则在抱怨找不到程序集 Newtonsoft.Json 11.0.2[=31] 时,该错误是有意义的=]

宾果!

我在 Ubuntu 中安装了 exiftool 以检查 dll 和 exe 版本。

sudo apt install libimage-exiftool-perl

我运行以下

$ exiftool publish/Newtonsoft.Json.dll
ExifTool Version Number         : 11.88
File Name                       : Newtonsoft.Json.dll
Directory                       : publish
File Size                       : 465 kB
File Modification Date/Time     : 2021:07:19 19:52:18+02:00
File Access Date/Time           : 2021:10:04 12:53:14+02:00
File Inode Change Date/Time     : 2021:10:04 12:44:39+02:00
File Permissions                : rwxrw-r--
File Type                       : Win32 DLL
File Type Extension             : dll
MIME Type                       : application/octet-stream
Machine Type                    : Intel 386 or later, and compatibles
Time Stamp                      : 2016:06:13 13:05:00+02:00
Image File Characteristics      : Executable, Large address aware, DLL
PE Type                         : PE32
Linker Version                  : 48.0
Code Size                       : 465920
Initialized Data Size           : 2048
Uninitialized Data Size         : 0
Entry Point                     : 0x738b6
OS Version                      : 4.0
Image Version                   : 0.0
Subsystem Version               : 4.0
Subsystem                       : Windows command line
File Version Number             : 9.0.1.19813
Product Version Number          : 9.0.1.0
File Flags Mask                 : 0x003f
File Flags                      : (none)
File OS                         : Win32
Object File Type                : Dynamic link library
File Subtype                    : 0
Language Code                   : Neutral
Character Set                   : Unicode
Comments                        : Json.NET is a popular high-performance JSON framework for .NET
Company Name                    : Newtonsoft
File Description                : Json.NET .NET Standard 1.0
File Version                    : 9.0.1.19813
Internal Name                   : Newtonsoft.Json.dll
Legal Copyright                 : Copyright © James Newton-King 2008
Legal Trademarks                : 
Original File Name              : Newtonsoft.Json.dll
Product Name                    : Json.NET
Product Version                 : 9.0.1
Assembly Version                : 9.0.0.0

如您所见,发布的程序集是 Newtonsoft.Json 9.0.1。一种如释重负的感觉涌上心头。

我转到我的测试项目并将以下内容添加到 *.csproj

<IsPublishable>false</IsPublishable>

rm -rd publish
dotnet publish -c Release -o publish

关键时刻

$ exiftool publish/Newtonsoft.Json.dll
ExifTool Version Number         : 11.88
File Name                       : Newtonsoft.Json.dll
Directory                       : publish
File Size                       : 641 kB
File Modification Date/Time     : 2018:03:24 18:44:14+01:00
File Access Date/Time           : 2021:10:04 12:44:38+02:00
File Inode Change Date/Time     : 2021:10:04 12:57:29+02:00
File Permissions                : rwxrw-r--
File Type                       : Win32 DLL
File Type Extension             : dll
MIME Type                       : application/octet-stream
Machine Type                    : Intel 386 or later, and compatibles
Time Stamp                      : 2098:12:14 20:33:48+01:00
Image File Characteristics      : Executable, Large address aware, DLL
PE Type                         : PE32
Linker Version                  : 48.0
Code Size                       : 653824
Initialized Data Size           : 2048
Uninitialized Data Size         : 0
Entry Point                     : 0xa16b6
OS Version                      : 4.0
Image Version                   : 0.0
Subsystem Version               : 4.0
Subsystem                       : Windows command line
File Version Number             : 11.0.2.21924
Product Version Number          : 11.0.2.0
File Flags Mask                 : 0x003f
File Flags                      : (none)
File OS                         : Win32
Object File Type                : Dynamic link library
File Subtype                    : 0
Language Code                   : Neutral
Character Set                   : Unicode
Comments                        : Json.NET is a popular high-performance JSON framework for .NET
Company Name                    : Newtonsoft
File Description                : Json.NET .NET Standard 2.0
File Version                    : 11.0.2.21924
Internal Name                   : Newtonsoft.Json.dll
Legal Copyright                 : Copyright © James Newton-King 2008
Legal Trademarks                : 
Original File Name              : Newtonsoft.Json.dll
Product Name                    : Json.NET
Product Version                 : 11.0.2
Assembly Version                : 11.0.0.0

现在发布的程序集是预期的 11.0.2

我确认运行从发布文件夹中安装我的应用程序现在可以正常工作!

cd publish
dotnet MyCompany.ItgService.dll