weikio / pluginframework Goto Github PK
View Code? Open in Web Editor NEWEverything is a Plugin in .NET
License: MIT License
Everything is a Plugin in .NET
License: MIT License
For example each option should be better documented.
All the Nuget packages currently use the Weik.io logo. Update the packages so that Plugin Framework's logo is used.
Your mvc example is rubbish has no purpose provide a better example of say loading a button and a action happen.
The plugins that I have created is having some dependencies being injected via the constructor of the plugin.
In my application I'm using Autofac as the container and I have created an extension method which will scan the location where I have my plugins and registers the dependencies to the container. The scanning and registration seems to be working but due to some reason when I'm trying to get the plugin by tag i'm getting an error that the dependencies of the plugin cannot be resolved.
I just want to check if this is possible , if so am I missing something. Also it would be helpful if there is examples that I can check it out.
Any help is highly appreciated !
Sometimes its helpful when u use SQL Connection Pooling in a Plugin, to disable Unload for AssemblyLoadContext, but i miss the Option to do this. It will be great if you can add this Option. Thanks
I can successful load razor components from loosely coupled modules using an appropriate plugin interface, but this doesn't include the CSS isolation. Any thoughts?
said above
Please add a winforms sample when you can.
Looks great!!
Tim
Background
The typefinder can currently find types which implement an interface.
But in some scenarios it would be great if we could find types which satisfy the interface without implementing it.
Solution ideas
There's at least couple ways to implement this:
Maybe at this point we could separate TypeFinder into a separate Nuget package?
Also, should we enable ducktaping for the found types at this point? Maybe wrap the types which could implement an interface with a new class that actually implements the interface and use that as a proxy. Not sure about this one. We could perhaps use something like Impromptu for handling the ducktaping.
Plugin Framework supports plugin tagging. It would help if catalogs would support getting a list of plugins based on a tag.
I'm not yet publishing nuget packages to nuget.org. So for development I'm using Visual Studio's built in nuget filesystem repository to publish packages. The source in VS is named "Microsoft Visual Studio Offline Packages" and typically points to C:\Program Files (x86)\Microsoft SDKs\NuGetPackages. I can see that my packages have been added successfully to that root path via nuget.exe add. However, I have yet to find an example of how to point weikio to this folder and successfully load. Is loading catalogs from a local filesystem repository a supported scenario? If so, can you provide an example of how that could be accomplished (working with dependencies)?
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 IFoo
s 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!
It would be nice to have a way for an assembly/Nuget package to describe the plugin's content.
For example currently we have to use the following syntax:
var catalog = new NugetPackagePluginCatalog("Serilog", "2.9.0", configureFinder: configure =>
{
configure.HasName("Serilog.Log");
});
In the future we would like to be able to write this:
var catalog = new NugetPackagePluginCatalog("Serilog", "2.9.0");
So that the Serilog-package could tell the Plugin Framework that Serilog.Log is the type-plugin for this package.
NOTE: It's unlikely but possible that a same plugin package / assembly is used from two or more different apps and for each app the "plugin" is a different thing. The interface for getting the plugin definition should maybe take HostApp's name and HostApp's version as parameters.
Hi,
How can I initialize a plugin with IApplicationBuilder and IServiceCollection?
best regards
I want to load my plugins with their dependencies. Because #46 I try with nuget packages but I faced with same problem.
Is there way to load local nuget package and dependencies from official nuget feed? I don't want to publish my packages into nuget.org.
Currently configuration file supports the configuration of Assembly and Folder catalogs.
Add support for configuring feed and package catalogs.
Does this library use ReflectionOnlyLoad when identifying assemblies to load? A quick search of the source tree seemed to suggest it doesn't.
With other libraries in the .net framework days, I experienced problems where merely searching for assemblies with a given class method signature would load the assembly into the process. Not only does that lock the file but IIRC it can mean unwanted static constructors get run. How does this library avoid such problems?
Configure the projects so that they target both the netcoreapp31/netstandard20 as they currently do and add support for .NET 5.
So all our project files in the future should target both the netcoreapp31/netstandard20 and .NET 5.
Hi, @mikoskinen.
Thank you for your great efforts on this, really appreciate it!
While testing this framework for my case, I found inconsistency with TypeFinderCriterias for FolderPluginCatalog. To be more precise, the problem is in FolderPluginCatalog.IsPluginAssembly method, as it uses the obsolete TypeFinderCriterias property to determine if the specified assembly contains plugins.
https://github.com/weikio/PluginFramework/blob/master/src/Weikio.PluginFramework/Catalogs/FolderPluginCatalog.cs#L192
https://github.com/weikio/PluginFramework/blob/master/src/Weikio.PluginFramework/Catalogs/FolderPluginCatalog.cs#L248
As the result, if you configure FolderPluginCatalogOptions with the new TypeFinderOptions and not with the obsolete TypeFinderCriterias, it will treat all assemblies as those that contain plugins, which leads to unnecessary assembly loading.
I've already created a fix to this issue, but it seems that I don't have permission to push it.
Could you please take a look?
Thanks,
Vasyl
Replace it with StringBuilder & CodeToAssemblyGenerator.
There is a Blazor Server sample, which is successful. Then I created a Blazor WebAssembly App and build the project. It shows:
Weikio.PluginFramework.Samples.Shared.csproj targets netcoreapp3.1. It can't be targeted to .NETStandard,Version=v2.1
Weikio.PluginFramework.AspNetCore.csproj targets netcoreapp3.1. It can't be targeted to .NETStandard,Version=v2.1
I noticed the TargetFramework
in the Blazor WebAssembly App project is:
<PropertyGroup>
<TargetFramework>netstardard2.1</TargetFramework>
</PropertyGroup>
How to solve this?
First of all thank you sharing this awesome library.
Use case
Create plugin catalogs of type "Type" with tags
Scenario
Issue
Plugins are not created with tags.
I have attached a unit test project with scenario. Please let me know if i'm, missing something ?
Hi All,
On .net 6 and I'm having some trouble getting a NugetFeedPlugInCatalog configured properly.
Setting up the web service, within ConfigureDevelopmentServices
var packageFolder = @"C:\PlugInStaging\EasyPost";
var feedDetails = new NuGetFeed("PlugIn Staging", packageFolder);
var pluginCatalog = new NugetFeedPluginCatalog(feedDetails);
services.AddPluginFramework()
.AddPluginCatalog(pluginCatalog)
.AddPluginType<IPlugIn>();
I think that this throws when DI resolution kicks in, from a component that requires the catalog.
Maybe a Packaging issue so I included a .nuspec which should force all dependencies into the .net6 nuget folder (based on this => https://josef.codes/dotnet-pack-include-referenced-projects/)
I copied the package to the package folder. When unzipped, the package does contain the required dependencies.
After retest, same issue.
The framework is very elegant IMHO. Excited to use this! I'm just not getting my proof of concept locked down just yet.
Any suggestions to get there?
Thanks,
Travis
Thank you for the framework. Great work.
Can I load Controllers from a plugin and get them to work with the routing?
Nuget Catalog currently contains all the logic for communicating with the Nuget feeds. We should remove this logic and instead use NugetDownloader.
Hey there, awesome project. What I was wondering is: How can I load and unload at runtime?
I have read https://www.infoq.com/articles/Plugin-Framework-DotNet/ and was wondering if that is still the current state regarding unloading.
I am using .NET Core 5.
Best,
Manuel
Background
Plugin Framework's ASP.NET Core package contains code which allows the developer to automatically register the plugins into the dependency injection system. Given the following code:
services.AddPluginFramework()
.AddPluginCatalog(folderPluginCatalog)
.AddPluginType<IOperator>();
All the IOperators are available through IEnumerable:
public CalculatorController(IEnumerable<IOperator> operators)
{
_operators = operators;
}
In addition, a single IOperator can also be injected:
public CalculatorController(IOperator myOperator)
{
_myOperator = myOperator;
}
The problem:
The problem with injecting a single IOperator is that the developer can't control which IOperator is returned. This is hard coded as FirstOrDefault in AddPluginType:
var result = GetTypes<T>(sp);
return result.FirstOrDefault();
Enhancement:
The developer should be able to control which IOperator is the "default". This would be useful in scenarios where only one plugin of a given type can be enabled (through UI or through configuration, for example).
Maybe the ideal solution for this could be through IOptions. The AddPluginType should contain a parameter which can be used as a syntactic sugar but under the hood, this should just configure the default option.
I skimmed your code for few minutes and found nothing (correct me if I'm wrong!) about security. Plugins will have complete freedom to do everything. Is there anyway to restrict what plugin can do in this framework?
It should be possible to configure Plugin Framework using a configuration file (appsettings.json or some other json-file).
Add this functionality into a new library. The AspNet-project should reference this new library as the functionality is most likely used with ASP.NET Core.
I was wondering if this would work with other .Net Frameworks? We currently have many applications that are in the 4.x framework and would like to incorporate something like this. Would the entire application need to be converted to .Net 4.x or does it incorporate features that are only available with .Net Core?
Can the framework load older Framework code in the .Net Core version?
Thanks,
Tim
Is there a way I can set the logger factory so that the internal logs can be viewed in case of any error in loading the plugins or it's dependencies.
I'm trying to understand the different options to integrate in my project. Once again thank you for sharing this project !
In this sample project WebAppWithAppSettings. I see there is a project reference of the plugin project in the host application. My understanding was when we implement plugin framework we don't need direct reference of the plugin project in the host application.
Please let me know if my understanding is correct or wrong.
Hi - I just ran across an issue which I think understand now: I had the problem that I could successfully instantiate plugins, but for some reason could not invoke a method on one of them because that required to load an additional assembly. At this time the AssemblyLoadContext was in state Unloading and I got this exception:
System.IO.FileLoadException: Could not load file or assembly 'MyPluginLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. An operation is not legal in the current state. (0x80131509)
File name: 'MyPluginLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
---> System.InvalidOperationException: AssemblyLoadContext is unloading or was already unloaded.
at System.Runtime.Loader.AssemblyLoadContext.VerifyIsAlive()
at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath)
at Weikio.PluginFramework.Context.PluginAssemblyLoadContext.Load(AssemblyName assemblyName)
at System.Runtime.Loader.AssemblyLoadContext.ResolveUsingLoad(AssemblyName assemblyName)
at System.Runtime.Loader.AssemblyLoadContext.Resolve(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
At this time I no longer had a reference to the plugin catalog which itself holds a reference to the AssemblyLoadContext. As soon as I kept the reference to the catalog this issue was gone.
Is it correct that I have to keep a permanent reference to the catalog to ensure that delayed loading of dependencies can be done for a plugin?
The ASP.NET Core application sometimes starts accepting requests before the plugin catalogs are initialized. This is most commonly seen with the Nuget catalog.
Hi,
now that .net 6 is already here is the functionality going to improve with hot reload and runtime unloading/reloading of plugins without restarting the application. with net 6 the multiplatform android,ios,mac etc support is also given by microsoft with blazor and all. whats the plan for this next update, what i am after is basically just blazor & hot reload currently.
thanks for such an awesome work
Issue:
The future version should automatically try to resolve the additional runtime/shared paths/frameworks required by the plugins:
Relates to #21 where we had to manually add additional runtime path:
PluginLoadContextOptions.Defaults.AdditionalRuntimePaths.Add(Path.GetDirectoryName(typeof(Button).Assembly.Location));
The AspNetCore-library also has this:
var aspNetCoreLocation = Path.GetDirectoryName(aspNetCoreControllerAssemblyLocation);
PluginLoadContextOptions.Defaults.AdditionalRuntimePaths.Add(aspNetCoreLocation);
Without these, the plugins can't load all the required DLLs.
Possible solution:
It might be possible to use MetadataReference to get the references. For example:
var assemblyRef = MetadataReference.CreateFromFile(assemblyPath);
var references = assembly.GetReferencedAssemblies();
This must be done recursively to find all the referenced assemblies and their locations. This info is then added to PathAssemblyResolver in FolderPluginCatalog.
PluginFramework supports installing plugins using Nuget. This is implemented in NugetPackagePluginCatalog.
If NugetPackagePluginCatalog's packagesFolder is set, the Nuget package gets always installed into the same path. Still, NugetCatalog always tries to download the Nuget package.
Change
We want to download the package only once. When NugetPackagePluginCatalog is initialized, it should check if the package has been previously installed and if so, it should use that instead of trying to download the package again.
Implementation idea
When first initializing the catalog, can we serialize and save nugetDownloadResult into some fixed location (based on the packages folder & package identifier (name.version)) in NugetPackagePluginCatalog.Initialize? Before calling nuGetDownloader.DownloadAsync we could check if the file already exists and deserialize the result instead of using NugetDownloader.
Note
Behind the scenes NugetPackagePluginCatalog uses NugetDownloader. NugetDownloader takes care of installing the correct version of the package (x86 vs x64 vs arm vs Win10 vs Win11 etc). NugetDownloader passes NugetDownloadResult to PluginFramework and PF uses that info when initializing the catalog.
NugetDownloader can be found from https://github.com/weikio/nugetdownloader but we shouldn't need to modify it.
Testing
New unit test should be created to test this functionality by cloning the NugetPackagePluginCatalogTests.CanInstallPackageWithNativeDepencencies. This is a good test case as it uses the native dlls etc. which are provided by the NugetDownloader.
Hello there,
As I understand it, you can have a few types of "Catalogues". I understand the "typical" scenario is to just have a Plugins directory alongside the hosting binary. This makes it necessary to copy files around... If I have a solution with several projects, one of them being the host application, and another one a plugin, can I make it work with just clicking the "play" button on Visual Studio? To simplify development... Maybe with project references?
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.