Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rx-Linq 2.2.5 FromEventPattern overload throws MethodMissingException due to differences between .NET 4 and .NET 4.5 version #99

Closed
OmerRaviv opened this issue May 19, 2015 · 8 comments

Comments

@OmerRaviv
Copy link

I'm authoring a VIsual Studio extension and have noticed a peculiar problem with the FromEventPattern method. In VS, some VS extensions are compiled against the net40 version of Rx-Linq and some are compiled against the net45 version. Because the two assemblies share the same strong name signature, the extension that loads first will "win" and its version of Rx-Linq will be used by both. It appears that the signature of some FromEventPattern overloads are different between these two versions, which will cause a MethodMissingException to happen.

To reproduce:

  1. Open a new Console app in VS2013. With NuGet, Install-Pckage Rx-Linq.
  2. Paste the following code in:
class Program
    {
        private static event EventHandler MyEvent;

        static void Main(string[] args)
        {            
            var eventAsObservable = Observable.FromEventPattern(ev => MyEvent += ev,ev => MyEvent -= ev);
            eventAsObservable.Subscribe(p => Console.WriteLine("Ping."));
            MyEvent(null, EventArgs.Empty);
            Console.ReadLine();
        }
    }

By default, you program will be compiled against the net45 version of RxLinq. Force it to dynamically load the net40 version instead: Go into the packages\Rx-Linq.2.2.5\lib\net40 folder and copy its contents, then paste it into your \bin\debug folder. Next, manually re-run your application by double clicking the .exe file.

Expected result: The program will run the same as it did when working against the net45 version
Actual result:
Unhandled Exception: System.MissingMethodException: Method not found: 'System.IO
bservable1<System.Reactive.EventPattern1<System.Object>> System.Reactive.Linq.
Observable.FromEventPattern(System.Action1<System.EventHandler>, System.Action
1<System.EventHandler>)'.
at ConsoleApplication5.Program.Main(String[] args)

@OmerRaviv
Copy link
Author

Note that this does not seem reproduce when you use the overloads of FromEventPattern that take in generic type arguments.

@OmerRaviv
Copy link
Author

Apologies, I just now noticed that @forki beat me to the punch and already reported this issue in #97 . In any case, I hope this repro is still useful.

@bartdesmet
Copy link
Collaborator

Thanks for reporting this. We're talking to the CLR/BCL teams about this to see what we can do.

A managed process can't load different versions of the same assembly, and using a different public key seems a bit tricky on our end given build system and signing constraints. It seems existing plugins in Visual Studio encode the version number of their binaries in the assembly name. This, too, is tricky given that other libraries can depend on us, and those would need to do the same thing.

The reason you're experiencing an issue for that particular overload is because of a change to its signature to eliminate a constraint on event arguments being derived from EventArgs, which was removed in .NET 4.5 for event handlers in WinRT which don't have that restriction. A valid option is to use different overloads that have been stable over time.

@forki
Copy link

forki commented May 21, 2015

Thanks for looking into it. In Paket.VisualStudio we now just ilmerge Rx
and the problem is solved for us.
On May 21, 2015 6:18 PM, "bartdesmet" notifications@github.com wrote:

Thanks for reporting this. We're talking to the CLR/BCL teams about this
to see what we can do.

A managed process can't load different versions of the same assembly, and
using a different public key seems a bit tricky on our end given build
system and signing constraints. It seems existing plugins in Visual Studio
encode the version number of their binaries in the assembly name. This,
too, is tricky given that other libraries can depend on us, and those would
need to do the same thing.

The reason you're experiencing an issue for that particular overload is
because of a change to its signature to eliminate a constraint on event
arguments being derived from EventArgs, which was removed in .NET 4.5 for
event handlers in WinRT which don't have that restriction. A valid option
is to use different overloads that have been stable over time.


Reply to this email directly or view it on GitHub
#99 (comment)
.

@OmerRaviv
Copy link
Author

Any progress on this issue? Should we continue to include a renamed/ILMerged version of Rx for now, or is there another workaround?

@clairernovotny
Copy link
Member

For things like VS-addins using shared components, it seems like you'll always be subject to this issue if you use a common library. If you can't load into a separate appdomain (and the VS model may not allow it), you may be better-off using ILMerge to internalize your references. That way you'll always get the dependencies you compiled and tested against. That approach would shield you against more than just Rx version differences.

@OmerRaviv
Copy link
Author

True, but people who are unaware will continue to get bitten hard by issues such as this (In this case at least 4 popular VS extensions were affected, that I know of). I think this really ought to be solved at the NuGet / package management level because it's a really easy mistake to make as a package author. By default, NuGet should not allow you to pack assemblies with the same identity but with differing public APIs for different platforms, unless you explicitly opt-out. This won't make the problem go away entirely but will hopefully help minimize the damage.

@clairernovotny
Copy link
Member

I hate to say it, but all of the .NET Core NuGet packages have the same identity and different public API for different platforms.

If you look, many/most of the packages will have a netstandard1.0 version and a netstandard1.3 version that adds much more surface area. If the 1.0 version is loaded and something else needs the stuff from 1.3, you have the same issue. The packages are backwards compat, so anything expecting to use the one targeting 1.0 can use the 1.3 ver, but the reverse is not true.

Addin scenarios make this tricky and you need to be extra careful when using common libs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants