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

FolderPluginCatalog doesn't automatically load dependent assemblies in the same folder #46

Open
nitz opened this issue Apr 11, 2021 · 3 comments

Comments

@nitz
Copy link

nitz commented Apr 11, 2021

Hello!

Real quick I wanted to say that I'm so glad I found this project, it's the exact amount of auto-magic I was hoping for In a plugin system!

So, I'm making use of a FolderPluginCatalog for now, just as I'm setting up the scaffolding for a project. One of the plugin assembles references a nuget package. I'm using the "xcopy deploy" method to copy my plugin assemblies (and their dependencies via <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>) to my plugins folder.

This is a slimmed down version of my PluginCoordinator:

	public class PluginCoordinator
	{
		private static readonly DirectoryInfo[] _pluginPaths = new[]
		{
			new DirectoryInfo("./plugins")
		};

		private static readonly Type _pluginBaseType = typeof(IPlugin);

		private static readonly Type[] _pluginTypes = new[]
		{
			typeof(IFooPlugin),
			typeof(IBarPlugin),
			typeof(IBazPlugin),
			/* and so on */
		};

		private static ILoggerFactory _loggerFactory { get; set; }

		public static int LoadPlugins(ILoggerFactory loggerFactory)
		{
			_loggerFactory = loggerFactory;

			var allCatalogs = new CompositePluginCatalog();

			foreach (DirectoryInfo pluginPath in _pluginPaths)
			{
				foreach (Type type in _pluginTypes)
				{
					IPluginCatalog catalog = LoadPluginsOfType(pluginPath, type);
					allCatalogs.AddCatalog(catalog);
				}
			}

			// initialize all catalogs syncronously
			allCatalogs.Initialize().Wait();

			// get all the plugins and do something fun with them!
			var plugins = allCatalogs.GetPlugins();

			return plugins.Count;
		}

		private static IPluginCatalog LoadPluginsOfType(DirectoryInfo pluginDirectory, Type pluginType)
		{
			TypeFinderCriteria? pluginCriteria = TypeFinderCriteriaBuilder.Create()
				.Implements(_pluginBaseType)
				.Implements(pluginType)
				.Build();

			if (pluginCriteria == null)
			{
				throw new InvalidOperationException("Failed to build plugin search criteria.");
			}

			var contextOptions = new PluginLoadContextOptions()
			{
				LoggerFactory = () => _loggerFactory.CreateLogger<PluginAssemblyLoadContext>(),
				//AdditionalRuntimePaths = new List<string> { pluginDirectory.FullName }
				/* ^^^ This is key! ^^^ */
			};

			FolderPluginCatalogOptions options = new()
			{
				IncludeSubfolders = false,
				PluginLoadContextOptions = contextOptions,
				TypeFinderOptions = new TypeFinderOptions()
				{
					TypeFinderCriterias = new List<TypeFinderCriteria> { pluginCriteria }
				}
			};

			return new FolderPluginCatalog(pluginDirectory.FullName, options);
		}
	}

The line commented in the creation of the PluginLoadContextOptions is where I've found my problem. With the code as shown (not setting the additional runtime paths to include the exact same folder causes a FileNotFound exception when say the assembly containing the IFoos tries to load (thus needing it's NuGet dependency -- which is in the folder with it). However, adding the AdditionalRuntimePaths back in like it's written there: Everything loads peachy just like you'd imagine it should.

I can't tell if this is an issue from me using it wrong (and I'm expected to specify the folder not only in the FolderPluginCatalog constructor, but also through the context options,) or if it's something that the framework should handle and just doesn't.

Thanks again for the awesome project!

@mikoskinen
Copy link
Contributor

Thank for reporting this! Based on the description it might be a bug and it should automatically include the plugin directory. I'll check and get back to you.

@nitz
Copy link
Author

nitz commented Apr 13, 2021

Awesome, no rush as the workaround I found seems to work fine! The more I was thinking about it too, it's very likely "technically correct" as is: The plugin directory wouldn't be on PATH, and the application's working directory isn't switched in there. It definitely does seem like it might be expected behavior by most users... but you know, they wouldn't call it DLL hell if loading them always made sense either 🙂

@franklupo
Copy link

News?

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

3 participants