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

AssemblyResolve handler for plugin third-party dependencies #521

Merged
merged 5 commits into from Dec 19, 2018

Conversation

3 participants
@hal0x2328
Copy link
Contributor

hal0x2328 commented Dec 13, 2018

I'm writing a plugin for neo-cli to publish smart contract events to a Redis PubSub queue. I quickly ran into a problem - since neo-cli plugins themselves are loaded at runtime, the existing .NET mechanism for resolving third-party dependencies in assemblies does not work (at least not for .Net Core), and the plugin fails to load with the following error:

System.IO.FileNotFoundException: Could not load file or assembly 'ServiceStack.Redis, Version=5.0.0.0, Culture=neutral,
 PublicKeyToken=null'. The system cannot find the file specified.

This patch attempts to address this by adding an AssemblyResolve handler that is called when an assembly fails to load, and proceeds to attempt to resolve and load the dependencies of the plugin. (Note that the way the CurrentDomain_AssemblyResolve function is written here, the third-party DLLs the plugin is dependent on are expected to be in the main working directory of neo-cli, it may be the case that we want to place them in a subfolder of the plugin's tree).

After this patch is applied I no longer experience the above error and my plugin functions as expected.

In my research on this topic, I've also discovered that the approach I've taken may not be the ideal way to go about solving the problem - some other issues with runtime Assembly loading are noted on this page such as versioning issues/race conditions that might occur if multiple plugins attempt to use different versions of the same third-party library. This may not be a huge concern at present, so instead of incorporating this entire third-party class I implemented the more straightforward single-function solution.

catch { }
catch (Exception ex)
{
Console.WriteLine("Failed to initialize plugin: " + ex);

This comment has been minimized.

@erikzhang

erikzhang Dec 17, 2018

Member
Suggested change Beta
Console.WriteLine("Failed to initialize plugin: " + ex);
Plugin.Log(nameof(Plugin), LogLevel.Error, $"Failed to initialize plugin: {ex.Message}");

This comment has been minimized.

@hal0x2328

hal0x2328 Dec 17, 2018

Contributor

Implemented suggested change in both exception handlers b6e4a99

if (assembly != null)
return assembly;

string filename = args.Name.Split(',')[0] + ".dll";

This comment has been minimized.

@shargon

shargon Dec 17, 2018

Member

What format do you receive here in args.Name?

This comment has been minimized.

@hal0x2328

hal0x2328 Dec 17, 2018

Contributor

It's receiving the full assembly name, e.g.
MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null

This comment has been minimized.

@erikzhang

erikzhang Dec 18, 2018

Member

You can use AssemblyName to parse the full name of the assembly:

AssemblyName a = new AssemblyName(args.Name);
string fullname = a.FullName;
string name = a.Name;

This comment has been minimized.

@hal0x2328

hal0x2328 Dec 18, 2018

Contributor

Neat, just made that change in 6160d4c

Joe Stewart and others added some commits Dec 17, 2018

@erikzhang erikzhang merged commit ef46d5d into neo-project:master Dec 19, 2018

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment