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 thedependencies
section for the framework - Explicitly adding a dependency to
"System.Runtime": "4.0.0-beta-23109"
in thedependencies
section for the framework, and likewise for4.0.10-beta-*
,4.0.20-beta-*
and4.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 justConsole.WriteLine("foo")
(but no other changes) - Changing the type of the
duration
variable toobject
instead ofDuration
- 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 :(
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 anddependencies
for other nuget dependencies. It has the same meaning here. When you use something from "the framework", it needs to be specified inframeworkAssemblies
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 theSystem.*
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 onSystem.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 aframeworkAssembly
reference toSystem.Runtime
, the msbuild project systems fails to install it (very complicated bug and design problem).That probably made things fuzzier...
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 forGoogle.Protobuf
:That
frameworkAssembly
reference then ends up in theproject.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...
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 modifiedStartup.cs
so that it usesGoogle.Protobuf.WellKnownTypes
:using Google.Protobuf.WellKnownTypes;
var duration = new Duration { Seconds = 100, Nanos = 5555 };
inside ofConfigure
await context.Response.WriteAsync("Hallo World!");
toawait context.Response.WriteAsync(duration.ToString());
The final code of
Startup.cs
: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 otherI could make working the following code:
One can download working project from here.
I commented additionally the line
in
AssemblyInfo.cs
to have no unneeded reference to"Microsoft.CSharp"
, which have a lot of other references. Theproject.json
contains in the demo project:By the way including
"System.Console": "4.0.0-beta-23516"
in"dnxcore50"
part of"frameworks"
is required becauseConsole
namespace (forConsole.WriteLine
) exist inmscorlib
ofDNX 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 textUPDATED 2: One can replace the line
to
to make it working. Just the usage of
Console.WriteLine(duration);
orvar 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 codeduration.ToString()
does really the same as((object)duration).ToString()
forWellKnownTypes
types (likeDuration
).The latest remark which I find important. The described problem exist only for dnx451 (or dnx452 or dnx46). If one would remove the lines
from
"frameworks"
part ofproject.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: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 bydnu 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"
.