Bug 16904 - [PCL] Json.NET PCL NuGet package incompatible with compilation for device; missing Microsoft.CSharp facade?
Summary: [PCL] Json.NET PCL NuGet package incompatible with compilation for device; mi...
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll ()
Version: 7.0.6.x
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Martin Baulig
Depends on:
Reported: 2013-12-19 19:25 UTC by Brendan Zagaeski (Xamarin Team, assistant)
Modified: 2014-05-02 13:18 UTC (History)
6 users (show)

Is this bug a regression?: ---
Last known good build:

Test case (8.13 KB, application/zip)
2013-12-19 19:25 UTC, Brendan Zagaeski (Xamarin Team, assistant)

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Brendan Zagaeski (Xamarin Team, assistant) 2013-12-19 19:25:23 UTC
Created attachment 5702 [details]
Test case

The .NET 4.5 PCL library from the Json.NET PCL NuGet package (version 5.0.8) is currently incompatible with compilation for device. It looks like a Microsoft.CSharp facade assembly might be needed?

## Steps to reproduce
1. Open attached test case.
2. Restore the NuGet package.
3. Attempt to deploy to device on Visual Studio or Xamarin Studio.

## Result
> error MT2002: Failed to resolve assembly: 'Microsoft.CSharp, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

## Additional information

> monodis --assemblyref Newtonsoft.Json.dll | grep Name= | cut -d'=' -f2 | while read ASSEMBLY; do grep -qr "$ASSEMBLY" /Developer/MonoTouch/usr/lib/mono/2.1/Facades || echo "$ASSEMBLY"; done

... displays just one assembly name:
> Microsoft.CSharp

## Workaround
Remove the default reference to the `portable-net45+wp80+win8` version of the Newtonsoft.Json.dll, and replace it with a reference to the `portable-net40+sl4+wp7+win8` version.

## Version information
Tested on the current stable versions, using the new System.Dynamic.Runtime.dll and System.Runtime.InteropServices.WindowsRuntime.dll from bug #16720.
Comment 2 Martin Baulig 2013-12-20 11:00:16 UTC
We only ship Microsoft.CSharp in XA, but not XI.

I'll check whether we can either enable this or ship some dummy wrapper.
Comment 3 Martin Baulig 2013-12-20 12:04:15 UTC
Ok, I was confused - of course there's a reason why we don't ship this on XI.

This is a limitation in our PCL support - the general policy is that if you attempt to use any type that's outside the supported feature set [*], then the result is undefined.  Undefined means you may get compilation errors, linking errors, random crashes at runtime or anything.  People need to make sure not to use any of these types or they're out of luck - we explicitly do not care about providing good error messages or workaround.  This is a design decision.

Of course, using NuGet makes it easy to hit this limitation by accident.  However, the general principle still applies here: you cannot reference any NuGet package that you would not be able to build.

The best course of action here is to enhance NuGet's multi-platform system to allow it to flag XI/XA as supported/non-supported and to allow specific XI/XA implementations.  The user who installs the package would then automatically get the correct implementation.

For instance, for this package, the author could add some "portable-net45+wp80+win8+xi" version in addition to "portable-net45+wp80+win8" and replace his "BinderWrapper" class with something that doesn't use Microsoft.CSharp and RefEmit.  I have previously already looked at the source code from this package and there is actually some conditional around that function, that's why it works in that other portable profile.

[*] On iOS, this includes all the non-iOS APIs such as RefEmit, Microsoft.CSharp (which uses RefEmit) and the non-SL part of DLR in addition to the all-platform blacklist, containing COM, System.Diagnostics.Tracing (actually not sure about this one) and some obscure types that don't exist in Mono.
Comment 4 Martin Baulig 2013-12-20 12:26:32 UTC

The alternative would have been to add a whole bunch of "throw new NotSupportedException ();" wrappers for missing COM- and other obscure types to our class libraries and then also provide such dummy implementations for the entire RefEmit namespace and other framework assemblies such as Microsoft.CSharp to XI.  A ton of work and would have bloated XI.

This was around March, so long ago where nobody thought about NuGet.

We may need to eventually reevaluate this at some point next year, considering that people may run into this by accident when using NuGet, without making any conscious decision to use any of these APIs.
Comment 5 Brendan Zagaeski (Xamarin Team, assistant) 2013-12-20 12:41:41 UTC
Great explanation! Thanks!

I'll be on the lookout for any other users who hit this, so I can let them know what the problem is.
Comment 6 Bob Dickinson 2013-12-24 13:44:19 UTC
You can add me as someone who used naively used NuGet and ran in to this by accident.  I spent about 10 hours working on this before independently arriving at the workaround given in the bug description, and then finally stumbling across this bug.

My specific manifestation was: Reference to type 'System.Dynamic.IDynamicMetaObjectProvider' claims it is defined assembly 'System.Dynamic.Runtime' ...

I was hoping that the System.Dynamic.Runtime façade fix in 7.0.6 would address this, but it did not.

Granted, I am a Xamarin n00b (and do not 100% follow the technical description behind the WONTFIX above), but I just used NuGet like I would in a Windows project, and it worked fine on Android and my PCL (in addition to my Win and WinPhone client projects).  The fact that the workaround worked certainly made it seem like there must be a better solution than manually hammering the "correct" reference into the csproj file (assuming you can figure out what the problem is).

FWIW, I ran into several other people (on Xamarin forums) who hit this wall and just gave up (either elected not to use JSON.Net, or went to using the fairly crusty backlevel version in the Xamarin component store). JSON.Net is kind of a big deal on C#, and I think developers coming to Xamarin will expect it to work, out of the box, using NuGet.

As someone who blew an entire day down this rathole (and is on the fence about dropping $2k for Xamarin), this seems to me like it deserves a fix of some kind.  Just my $.02.
Comment 7 999komi 2013-12-29 22:52:57 UTC
I'm a Xamarin noob, and I just ran into this.  Is this saying I used an unsupported type somewhere?  How can I tell which one?
Comment 8 Brendan Zagaeski (Xamarin Team, assistant) 2013-12-30 15:22:51 UTC
@Bob Dickinson: Thanks for adding your different error message! Hopefully that will help some other folks find this bug report more quickly.

The WONTFIX is really just saying that Xamarin has no plans to add the Microsoft.CSharp facade assembly for Xamarin.iOS.

As Martin mentioned, the ideal fix would instead be "to enhance NuGet's multi-platform system to
allow it to flag XI/XA as supported/non-supported..." If NuGet gains this ability, then package developers will be able to specify that only certain packages (or certain PCL profiles within the packages) are supported for Xamarin.iOS projects. For example, for the current Json.NET library, these flags could tell NuGet to pick the `portable-net40+sl4+wp7+win8` version of the library when adding the package to Xamarin.iOS projects. One tricky part of the problem is that the `portable-net45+wp80+win8` library would be compatible with Xamarin.iOS if it simply avoided the Microsoft.CSharp types, so limiting Xamarin.iOS to the `net40+sl4` libraries for _every_ NuGet package would be an undesirable restriction. Instead, a new kind of per-package compatibility flag for Xamarin.iOS and Xamarin.Android is needed.

The MT2002 error indicates that the project, or one of the libraries it uses, includes a reference to an assembly that is not available on Xamarin.iOS. In the particular case of "Microsoft.CSharp, Version=" from this bug report, the problem arises because the Microsoft.CSharp.dll facade assembly does not exist on Xamarin.iOS. Therefore any PCL that you want to use on Xamarin.iOS must _not_ reference any type from the Microsoft.CSharp namespace. If you have the source code for the PCL, you can try to track down the problem by looking for files that include either a `using Microsoft.CSharp;` statement or fully-qualified types with the `Microsoft.CSharp` prefix.
Comment 9 Bob Dickinson 2013-12-30 15:48:02 UTC
Thanks for the response.  I think I now have a better understanding of this problem.

My concern with the "it's not our problem" response is that I think to the extent that you offer the ability to create a PCL that includes your platform targets in the profile, and that it is generally the case that .NET components don't understand those platform targets, you need to do a better job of communicating to the developer what is happening.  In a "normal" Visual Studio / .NET world, when I used NuGet to add a component/reference and it succeeds, then I'm in business.  With the Xamarin extension and targets, when NuGet appears to succeed it may or may not result in something you can actually build and run, and there is no real indication of that.  That is going to be a giant, unpleasant surprise to a lot of Visual Studio types.

Is it possible to determine that the package doesn't specifically support XI/XA so that you can warn the developer at the time they bring it in (just a warning that it may or may not work)?  Is it possible to actually scan the package for dependencies at the time it is brought in to determine whether it is in fact going to work?  I mean, I get "it's not our fault that it didn't work", but I still think you have the responsibility to detect that it didn't work and explain specifically why at the time, to avoid people going down this path (for a non-Xamarin expert, tracking that failure back to the root cause it going to be pretty tough).

Also, just generally the fact that JSON.Net via NuGet doesn't work out of the box on iOS projects should be a serious concern for you.  It's probably one of the top components in use in the C# world, and the version that does work from your component store is over a year old and one major release version behind.  Maybe there should be another bug that says "Get JamesNK to either add XI/XA support or pull CSharp facade references so the next release will work with Xamarin".