Unable to resolve assembly reference issue without frameworkAssemblies

.NetNugetDnxNuspec

.Net Problem Overview


I'm trying to validate that 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, but the error I'm running into has me stumped.

DNX version: 1.0.0-rc1-update1

The scenario I'm currently trying to test is a console app targeting dnx451. I have a very simple sample app:

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);
    }
}

... and a tiny project.json:

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

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

Note that I'm not even using dnxcore* here - ironically, I got that to work without issues.

dnu restore works fine; dnx run fails with:

> 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'.

The following changes result in the same error:

  • Explicitly adding a dependency to "System.Runtime": "4.0.0" in the dependencies section for the framework
  • Explicitly adding a dependency to "System.Runtime": "4.0.0-beta-23109" in the dependencies section for the framework, and likewise for 4.0.10-beta-*, 4.0.20-beta-* and 4.0.21-beta*.
  • Adding dependencies to System.Runtime within the NuGet package (locally) and rebuilding against that - project.lock.json was updated to include System.Runtime v4.0.0, but the same error occurred
  • Ditto including a lib\dotnet directory in the package, as well as the dependencies

Steps that did work (independently, and with no dependencies entries), but confuse me:

  • Changing the Console.WriteLine call to just Console.WriteLine("foo") (but no other changes)

  • Changing the type of the duration variable to object instead of Duration

  • Removing all hint of Protocol Buffers entirely, and instead using TimeSpan or similar

  • Adding the following to project.json in the dnx451 section:

      "frameworkAssemblies": {
        "System.Runtime": ""
      }
    

Ultimately, I don't want users to have to do this - at least, not for the sake of Protocol Buffers. I'm assuming this is something to do with how we're building Protocol Buffers, but as I don't understand the cause properly, it's hard to fix.

I expect that if I could work out a way of making a dependencies entry work, I could then add that dependency into Protocol Buffers itself, which would be fine - but as having a dependency on System.Runtime v4.0.0 in the project.lock file doesn't seem to help, I must be missing something :(

.Net Solutions


Solution 1 - .Net

So if you squint and look at the project.json, it's basically a nuspec with a little bit of goop to describe what compilation options and sources are needed to build to project. Nuspecs today have 2 sections, frameworkAssemblies for "built in" stuff and dependencies for other nuget dependencies. It has the same meaning here. When you use something from "the framework", it needs to be specified in frameworkAssemblies vs being a nuget package dependency.

Now onto specifics:

When you use a PCL or .NET Core based library on .NET Framework, the references are to reference assemblies (sometimes called contract assemblies). Some examples of these are things like System.Runtime, System.Threading etc. When using MSBUILD based projects, there is a task that runs which basically automatically adds all of the System.* references to the C# compiler to avoid this mess. These assemblies are called facades on .NET Framework. The unfortunate part is that it adds ALL of them even if they aren't used. A dependency on System.Runtime is the trigger for this behavior (when running on .NET Framework based csproj files).

The reason adding a reference to the same package doesn't work is because the .NET Framework folder (net4*) for those contract assemblies (like System.Runtime), don't have any dlls in them. If you look at those folders, you'll see an empty _._ file. The reasoning for this is because when you declare a nuget package with a frameworkAssembly reference to System.Runtime, the msbuild project systems fails to install it (very complicated bug and design problem).

That probably made things fuzzier...

Solution 2 - .Net

I've accepted David Fowler's answer as the reason why all of this happened. Now in terms of what I should do about it, it looks like I just need to add a frameworkAssemblies element in the nuspec file for Google.Protobuf:

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

That frameworkAssembly reference then ends up in the project.lock.json in the client project, and all is well.

However, judging by David's other comment ("We're going to look at fixing this") I may not need to do anything anyway...

Solution 3 - .Net

It seems to me that your problem exist only because you've chosen Console application instead of "ASP.NET Web Application" / "ASP.NET 5 Templates" / "Empty". I made simple test usage Empty template, have added "Google.Protobuf": "3.0.0-alpha4" from NuGet and finally just modified Startup.cs so that it uses Google.Protobuf.WellKnownTypes:

  • added using Google.Protobuf.WellKnownTypes;
  • added var duration = new Duration { Seconds = 100, Nanos = 5555 }; inside of Configure
  • modified await context.Response.WriteAsync("Hallo World!"); to await context.Response.WriteAsync(duration.ToString());

The final code of 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);
    }
}

The resulting ASP.NET 5 application displayed successfully 100.5555s in the web browser.

You can download the demo project from here.

UPDATED: I analysed the problem with pure console DNX application, which uses the code and could found the the reason of the problem in duration.ToString() method, which works in ASP.NET environment, but not in pure Console application. The reason of the problem is interesting and I'm trying to investigate, but I wanted to share my current results with other

I could make working the following code:

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);
        }
    }
}

One can download working project from here.

I commented additionally the line

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

in AssemblyInfo.cs to have no unneeded reference to "Microsoft.CSharp", which have a lot of other references. The project.json contains in the demo project:

{
  ...

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

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

By the way including "System.Console": "4.0.0-beta-23516" in "dnxcore50" part of "frameworks" is required because Console namespace (for Console.WriteLine) exist in mscorlib of DNX 4.5.1. If one would try to add "System.Console": "4.0.0-beta-23516" on the level of common dependencies one get the error with starts with the text

> 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: One can replace the line

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

to

Console.WriteLine((object)duration);

to make it working. Just the usage of Console.WriteLine(duration); or var str = duration.ToString(); produces the error, which you described.

UPDATED 3: I verified that the code duration.ToString() calls the lines which use the lines for formatting. It seems that the code duration.ToString() does really the same as ((object)duration).ToString() for WellKnownTypes types (like Duration).

The latest remark which I find important. The described problem exist only for dnx451 (or dnx452 or dnx46). If one would remove the lines

"dnx451": { },

from "frameworks" part of project.json then the program will be compiles for DNX Core 5.0 only ("dnxcore50"). One can easy verify that one will don't have any problem more.

UPDATED 4: Finally I've found very simple workaround to your problem: one needs just add "Microsoft.AspNet.Hosting": "1.0.0-rc1-final" dependency to the project:

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

It follows to loading many unneeded dlls, but now the dependencies will be do correctly resolved.

The final project can be compiled without any problems for both dnx451 and dnxcore50. I interpret the results as following: "Google.Protobuf" do work with both dnx451 and dnxcore50, but the automatic dependency resolving of RC1 is still buggy and it can't correctly resolves some required dependencies of "Google.Protobuf".

Of cause adding directly unneeded "Microsoft.AspNet.Hosting": "1.0.0-rc1-final" reference can be seen as a workaround only. I think that dependency resolution used in ASP.NET 5 and DNX is still buggy. I posted some time before the issue, which is still opened. The issue provides an example when the resolving of direct included dependency can provide another results as dependencies resolved by dnu restore. It was the reason why I started to compare the dependency of working code, which I posted you initially with the dependencies of not-working project. After some tests I found the workaround and reduced it to the only dependency: "Microsoft.AspNet.Hosting": "1.0.0-rc1-final".

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJon SkeetView Question on Stackoverflow
Solution 1 - .NetdavidfowlView Answer on Stackoverflow
Solution 2 - .NetJon SkeetView Answer on Stackoverflow
Solution 3 - .NetOlegView Answer on Stackoverflow