没有 frameworkAssemblies 无法解决程序集引用问题

Unable to resolve assembly reference issue without frameworkAssemblies

我正在尝试验证 Protocol Buffers is going to work with the new portable runtimes from the ASP.NET team and ideally most other modern environments. The 3.0.0-alpha4 build was created a while ago using profile259, so I would expect some changes to be required in some cases, but I thought I'd give it a try. I'm aware of Oren Novotny's post about targeting .NET Core, and expected to have to make some changes to the Google.Protobuf nuspec file,但我 运行 遇到的错误让我很困惑。

DNX 版本:1.0.0-rc1-update1

我目前正在尝试测试的场景是一个针对 dnx451 的控制台应用程序。我有一个非常简单的示例应用程序:

using Google.Protobuf.WellKnownTypes;
using System;

public class Program
{
    public static void Main(string[] args)
    {
        Duration duration = new Duration { Seconds = 100, Nanos = 5555 };
        Console.WriteLine(duration);
    }
}

...还有一个小小的 project.json:

{
  "compilationOptions": { "emitEntryPoint": true },
  "dependencies": { "Google.Protobuf": "3.0.0-alpha4" },

  "frameworks": {
    "dnx451": { }
  }
}

请注意,我什至没有在这里使用 dnxcore* - 具有讽刺意味的是,我 that 可以正常工作。

dnu restore 工作正常; dnx run 失败:

Error: c:\Users\Jon\Test\Projects\protobuf-coreclr\src\ProtobufTest\Program.cs(9,9): DNX,Version=v4.5.1 error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

以下更改会导致相同的错误:

工作的步骤(独立地,没有 dependencies 条目),但让我感到困惑:

最终,我不希望用户必须这样做 - 至少,不是为了 Protocol Buffers。我假设这 与我们构建 Protocol Buffers 的方式有关,但由于我没有正确理解原因,所以很难修复。

我希望,如果我能想出一种方法使 dependencies 条目起作用,那么我可以将该依赖项添加到 Protocol Buffers 本身,这很好 - 但因为依赖于 [= project.lock 文件中的 86=] v4.0.0 似乎没有帮助,我一定是遗漏了什么 :(

在我看来,您的问题之所以存在只是因为您选择了控制台应用程序而不是 "ASP.NET Web Application" / "ASP.NET 5 Templates" / "Empty"。我做了简单的测试使用空模板,从 NuGet 添加了 "Google.Protobuf": "3.0.0-alpha4" 最后只是修改了 Startup.cs 以便它使用 Google.Protobuf.WellKnownTypes:

  • 已添加using Google.Protobuf.WellKnownTypes;
  • Configure
  • 内添加了 var duration = new Duration { Seconds = 100, Nanos = 5555 };
  • await context.Response.WriteAsync("Hallo World!");修改为await context.Response.WriteAsync(duration.ToString());

Startup.cs的最终代码:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.DependencyInjection;
using Google.Protobuf.WellKnownTypes;

namespace ProtobufTest
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();

            var duration = new Duration { Seconds = 100, Nanos = 5555 };

            app.Run(async context =>
            {
                await context.Response.WriteAsync(duration.ToString());
            });
        }

        // Entry point for the application.
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

生成的 ASP.NET 5 个应用程序在 Web 浏览器中成功显示 100.5555s

您可以从 here 下载演示项目。

已更新: 我分析了纯控制台 DNX 应用程序的问题,它使用了代码并且可以找到 中问题的原因 duration.ToString() 方法 ,它在 ASP.NET 环境中有效,但在纯控制台应用程序中无效。问题的原因很有趣,我正在尝试调查,但我想与其他人分享我目前的结果

我可以使用以下代码:

using Google.Protobuf.WellKnownTypes;
using System;

namespace ConsoleApp3
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var duration = new Duration { Seconds = 100, Nanos = 5555 };
            Console.WriteLine("{0}.{1:D4}s", duration.Seconds, duration.Nanos);
        }
    }
}

可以从 here 下载工作项目。

我另外评论了那行

//[assembly: Guid("b31eb124-49f7-40bd-b39f-38db8f45def3")]

AssemblyInfo.cs 中没有对 "Microsoft.CSharp" 的不必要引用,其中有很多其他引用。 project.json 包含在演示项目中:

{
  ...

  "dependencies": {
    "Google.Protobuf": "3.0.0-alpha4"
  },

  "frameworks": {
    "dnx451": { },
    "dnxcore50": {
      "dependencies": {
        "System.Console": "4.0.0-beta-23516"
      }
    }
  }
}

顺便说一句,在 "dnxcore50" 中包含 "System.Console": "4.0.0-beta-23516" "frameworks" 的一部分是必需的,因为 Console 命名空间(对于 Console.WriteLine)存在于 mscorlib DNX 4.5.1。如果尝试在 common 依赖级别上添加 "System.Console": "4.0.0-beta-23516",则会出现以文本

开头的错误

Error CS0433 The type 'Console' exists in both 'System.Console, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' and 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' ConsoleApp3.DNX 4.5.1

UPDATED 2:一个可以替换行

Console.WriteLine("{0}.{1:D4}s", duration.Seconds, duration.Nanos);

Console.WriteLine((object)duration);

让它工作。仅使用 Console.WriteLine(duration);var str = duration.ToString(); 会产生您所描述的错误。

更新 3: 我验证代码 duration.ToString() 调用 the lines which use the lines 进行格式化。对于 WellKnownTypes 类型(如 Duration),代码 duration.ToString() 似乎与 ((object)duration).ToString() 确实相同。

我认为重要的最新评论。所描述的问题仅存在于 dnx451(或 dnx452 或 dnx46)。如果有人删除行

"dnx451": { },

来自 project.json"frameworks" 部分,然后该程序将仅针对 DNX Core 5.0 ("dnxcore50") 进行编译。可以很容易地验证一个不会有任何问题。

更新 4: 最后我找到了解决您的问题的非常简单的解决方法:只需向项目添加 "Microsoft.AspNet.Hosting": "1.0.0-rc1-final" 依赖项:

{
  "dependencies": {
    "Google.Protobuf": "3.0.0-alpha4",
    "Microsoft.AspNet.Hosting": "1.0.0-rc1-final"
  }
}

随后加载了许多不需要的 dll,但现在依赖项将得到正确解决。

The final project对于dnx451和dnxcore50都可以正常编译。我对结果的解释如下: "Google.Protobuf" 可以同时使用 dnx451 和 dnxcore50,但是 RC1 的自动依赖关系解析仍然有问题,它不能正确解析 "Google.Protobuf".[=60 的一些必需的依赖关系=]

当然,直接添加不需要的 "Microsoft.AspNet.Hosting": "1.0.0-rc1-final" 引用只能被视为一种解决方法。我认为 ASP.NET 5 和 DNX 中使用的依赖项解析仍然存在问题。我在 the issue 之前发布了一些时间,它仍然处于打开状态。该问题提供了一个示例,解决 直接包含的依赖关系 可以提供 另一个 结果,因为依赖关系由 dnu restore 解决。这就是我开始比较 工作代码 的依赖关系的原因,我最初发布给你的是不工作项目的依赖关系。经过一些测试后,我找到了解决方法并将其减少到唯一的依赖项:"Microsoft.AspNet.Hosting": "1.0.0-rc1-final".

因此,如果您眯着眼睛看 project.json,它基本上是一个 nuspec,带有一点点 goop 来描述构建项目所需的编译选项和源代码。今天的 Nuspecs 有 2 个部分,frameworkAssemblies 用于 "built in" 内容,dependencies 用于其他 nuget 依赖项。这里的意思是一样的。当您使用 "the framework" 中的内容时,需要在 frameworkAssemblies 中指定它而不是作为 nuget 包依赖项。

现在进入细节:

当您在 .NET Framework 上使用 PCL 或基于 .NET Core 的库时,引用是引用程序集(有时称为契约程序集)。其中的一些示例是 System.RuntimeSystem.Threading 等。使用基于 MSBUILD 的项目时,有一个运行的任务基本上会自动添加 all System.* 对 C# 编译器的引用以避免这种混乱。这些程序集在 .NET Framework 上称为外观。不幸的是,它添加了 ALL 个,即使它们没有被使用。对 System.Runtime 的依赖是此行为的触发器(当 运行 基于 .NET Framework 的 csproj 文件时)。

添加对同一个包的引用不起作用的原因是这些合同程序集(如 System.Runtime)的 .NET Framework 文件夹 (net4*) 中没有任何 dll .如果您查看这些文件夹,您会看到一个空的 _._ 文件。这样做的原因是,当您使用 frameworkAssembly 引用 System.Runtime 声明 nuget 包时,msbuild 项目系统无法安装它(非常复杂的错误和设计问题)。

这可能使事情变得更加模糊...

我已经接受 作为 为什么 所有这一切发生的原因。现在就我应该而言,看起来我只需要在 nuspec 文件中为 Google.Protobuf:[=16 添加一个 frameworkAssemblies 元素=]

<package>
  <metadata>
    ...
    <frameworkAssemblies>
      <frameworkAssembly assemblyName="System.Runtime" targetFramework="net45" />
    </frameworkAssemblies>
  </metadata>
  ...
</package>

然后 frameworkAssembly 引用最终出现在客户端项目的 project.lock.json 中,一切正常。

但是,根据 David 的其他评论 ("We're going to look at fixing this") 判断,我可能不需要做任何事情...