Giter VIP home page Giter VIP logo

prise's Introduction

What is Prise?

Prise Logo


Prise is a plugin framework for .NET (Core) applications, written in .NET (Core). The goal of Prise, is enable you to write decoupled pieces of code using the least amount of effort, whilst maximizing the customizability and backwards-compatability. Prise helps you load plugins from foreign assemblies. It is built to decouple the local and remote dependencies, and strives to avoid assembly mismatches.

GitHub Issues
GitHub Pull Requests
License

๐Ÿ“œ Check out the documentation ๐Ÿ“œ


๐Ÿš€ Builds and Tests ๐Ÿงช


alt text
alt text
alt text
alt text
alt text
alt text
alt text

๐Ÿ“ฆ Latest version

ย  ย 
NuGet Badge NuGet Badge
NuGet Badge NuGet Badge
NuGet Badge NuGet Badge
NuGet Badge NuGet Badge
NuGet Badge NuGet Badge
NuGet Badge NuGet Badge

prise's People

Contributors

merken avatar tartanlegrand avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

prise's Issues

Feature - Add support for Razor Class Library views

First let me say, remarkable work. This is absolutely the closest I've found to the "holy grail" of hot-swappable MVC controllers & views. That it unpacks nuget packages and loads from there is 100% what I've dreamt of (short of a service that polls nuget feeds and downloads updates to the plugins folder).

I tried adding Prise to a solution with one main WebApp and several Razor Class Libraries with their own views and controllers and was able to load/update one of the RCL projects at runtime. The only flaw I ran into there was that the views (usually merged into the main project at build/start) weren't found so I could only return content results.

I may be pushing this farther than was intended, or farther than anyone has time to devote to making possible but if down the road you could reference & update RCL packages at runtime that would be a stunning achievement.

Thanks again!

James White

How IConfiguration Got Injected ?

First, define Plugin in as follows:

    [Plugin(PluginType = typeof(IPlugin))]
    public class ActivityPlugin : IPlugin
    {
    }

    [PluginBootstrapper(PluginType = typeof(ActivityPlugin))]
    public class PluginBootstrapper : IPluginBootstrapper
    {
        public IServiceCollection Bootstrap(IServiceCollection services)
        {
            var config = services.BuildServiceProvider().GetRequiredService<IConfiguration>();
            System.Console.WriteLine("elsa config: {0} / {1}", config.GetSection("Elsa").Value, config["Elsa"]);
            var timerConfig = config.GetSection("Elsa:Timers");

            // check value here, if not then throw 
            if (string.IsNullOrEmpty(timerConfig.Value))
            {
                throw new ArgumentNullException("please config Elsa:Timers in appsettings.json");
            }
            return services.AddTimerActivities(options => options.Bind(timerConfig));
        }
    }

Then, host config settings

 // does it is necessary ?
 // services.AddSingleton<IConfiguration>(Configuration);

 // This is required for our razor pages to be found
 // services.AddRazorPages().AddRazorRuntimeCompilation();
  services.AddPrise<IPlugin>(options => {
          var builder = options.WithDefaultOptions(Path.Combine(AppDomain.CurrentDomain.BaseDirectory ?? "", "Plugins"))
       .AddPriseRazorPlugins(Environment.WebRootPath)
       .IgnorePlatformInconsistencies()
       .ScanForAssemblies(composer => composer.UseDiscovery())
        // Find the code in example project, how magic comes in here ?
        .UseHostServices(services, new[] { typeof(IConfiguration) }) 
        .WithAssemblySelector<AssemblyPluginSelector<IPlugin>>();
 });

But, when run the host
image
image

Why is TaskCanceledException not being caught but other exceptions are?

I am using Prise Plugin Framework and MailKit SMTP Client.

During the ConnectAsync call, if the async call timeouts, it will throw a TaskCanceledException. This will be caught in the caller and after logging it will be thrown down the call stack. But this will not be caught after the Execute method in the Prise plugin and the app will crash - other exceptions are being caught fine and don't crash the app.

try
{
    await _smtpClient.ConnectAsync(sendEmail.Host, sendEmail.Port);
    // other code
}
catch
{
    // logging
    throw;
}

This code is being called by the plugin with return await service.ExecuteAsync(parameters, context);

I'm expecting this to throw the exception to its parent but it doesn't happen.

The plugin looks as follows:

[Plugin(PluginType = typeof(IPlugin))]
public class Email : BasePlugin<EmailInputModel>
{
	[field: PluginService(ProvidedBy = ProvidedBy.Host, ServiceType = typeof(IServiceProvider))]
	private IServiceProvider ServiceProvider { get; set; }

	[PluginActivated]
	public void OnActivated()
	{
		InitializeServices(ServiceProvider);
	}

	protected override async Task<PluginExecutionResult> ExecutePluginAsync(
		InputModel parameters,
		PluginExecutionContext context,
		IServiceProvider provider)
	{
		IEmailPluginService service = provider.GetRequiredService<IEmailPluginService>();
		return await service.ExecuteAsync(parameters, context);
	}
}

Does this library supports Blazor?

Hi, I just found this and the library seems awesome. I will definitely give it a try. I was just wondering if I could use this to map routable and not routable razor components to the host project, similar to how the MVC stuff works.

Add and retrieve different implementations of a interface?

if you have

public interface IService { }  
public class ServiceA : IService { }
public class ServiceB : IService { } 

usually, one can register different implementations of an interface with the ServiceCollection:

services.AddSingleton<IService, ServiceA>();
services.AddSingleton<IService, ServiceB>();

and later retrieve a list of different implementations by

using Microsoft.Extensions.DependencyInjection;

var services = serviceProvider.GetServices<IService>();
var serviceB = services.First(o => o.GetType() == typeof(ServiceB)); 

// or (if IService has a Name property)

var serviceZ = services.First(o => o.Name.Equals("Z"));

However, when using Prise

services.AddPrise<IService>(options =>
   options.WithPluginPath(path).
   WithPluginAssemblyName(name), ServiceLifetime.Singleton);

it is possible to register different implementations of the same interface, but GetServices() only returns the last registered entry.

Do I miss some specific configuration / options or is there a (clean) workaround ?

Dependency management question

I am struggling with setting up a basic use case for our application. While the examples work easily enough, the problem is that most of our plugins have a number of dependencies, some of which are nuget packages hosted in our DevOps Artifacts in Azure. I looked at the Prise source code to see how it handled nupkg files and it's pretty clear that it only does a simple unzip and does not handle any kind of dependency resolution. Does this imply that any dependencies that the plugin has (all the way down the dependency chain) must be either referenced by the host or be part of the plugin assembly directory?

On a related note, the first time I attempted to run the project, I had the plugin dll in the directory by itself. Upon running the program, Prise told me that it could not load the plugin because Microsoft.Extensions.DependencyModel was missing. So I thought I'd just drop it into the plugin directory, but that did not work. I even tried adding an explicit nuget reference to the package and that did not work either. I verified that the dll is in the host's bin directory. I am running an Azure Function app. Here is the error:

Prise: Could not load file or assembly 'Microsoft.Extensions.DependencyModel, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The system cannot find the file specified.
[2021-10-25T22:48:12.347Z] Evaluate failed:    at Prise.AssemblyLoading.DefaultPluginDependencyContextProvider.FromPluginLoadContext(IPluginLoadContext pluginLoadContext)
[2021-10-25T22:48:12.350Z]    at Prise.AssemblyLoading.DefaultAssemblyLoadContext.LoadPluginAssembly(IPluginLoadContext pluginLoadContext)
[2021-10-25T22:48:12.352Z]    at Prise.DefaultPluginLoader.LoadPlugin[T](AssemblyScanResult scanResult, String hostFramework, Action`1 configureLoadContext)

[Question] From the Host Web API's perspective: Is there a way to limit max execution time of a plugin?

I have a scenario where the Base plugin contract can be implemented in multiple ways for different use cases. All the plugin implementations are loaded into the Host app in different Assembly Load Contexts. Since the host app doesn't have control over the code written into the plugins, how can it terminate a plugin execution if it's taking longer than a specific timespan?

I have tried using cancellation token as an argument to the methods specified in the base plugin interface, but it's the responsibility of the implementation to co-operate with the cancellation. Is there a way to enforce termination from the host app/ calling method?

System.Text.Json.JsonException when using a plug-in interface with async Task

When creating a plug-in contract using an async method that just returns a Task, I get this exception when trying to call the plug-in through Prise:

System.Text.Json.JsonException: 'A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 0.'

It's quite easy is to replicate:

    public interface IMyPlugin
    {
        public Task TestAsync();
    }

    [Plugin(PluginType = typeof(IMyPlugin))]
    public class MyPlugin: IMyPlugin
    {
        public async Task TestAsync()
        {
            await Task.Run(() => Console.WriteLine("Inside of plug-in.")).ConfigureAwait(false);
        }
    }

On the host side, just call:

await plugin.TestAsync().ConfigureAwait(false);

and you get the exception above.

If you change the return to Task<string> or something else, it seems to work fine.

Loading 3.1 runtime assemblies on 5.0 fails

When multiple runtimes are installed on the Host:
2.1.811
3.1.404
5.0.100

And the host is configured to run as 3.1 application, then loading the System.Runtime 5.0.0 fails.
Expected behaviour: when the host is operating using the 3.1 runtime, the 3.1 runtime assemblies must be loaded, even though 5.0 assemblies are available on the host.

DI With Blazor components

To get routing to work with blazor server side - I have to add the loaded plugin assemblies to the router

<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@_pluginAssemblies">

This then seems to render the loaded plugins fine, until I try to inject plugin services - I can inject things I declare directly in the host (such as config services, etc), but I cannot inject services I've created and set up in the plugin (using bootstrapper and PluginService, etc), it seems to tie the assembly to the host IServiceProvider.

Additionally, I noticed the MvcPluginLoader does not fire the activated method, so i did this manually using IPluginActivator.

Invoke Plugin Publish command from Post Build Events

I need to call Prise plugin publish command from the Project Post Build Events.

I will jump into research to find out how to do it, but I f you can post example or instructions will be great.

Thanks for this useful code !

Fix loading an older version of a runtime assembly

When multiple runtimes are installed on the Host:
2.1.811
3.1.404
5.0.100

And the host is configured to run as 3.1 application, then loading the System.Runtime 5.0.0 fails.
Expected behaviour: when the host is operating using the 3.1 runtime, the 3.1 runtime assemblies must be loaded, even though 5.0 assemblies are available on the host.

Possibility to make plugins singletons?

Is there any possibility to add plugins as a singleton instance? For example if a plugin wants to keep some state over the time of its execution. It seems that the whole design is currently focused on creating a plugin instance per scope.

Load plugin with isolated environment

I came across this interesting c# plugin loader, which is awesome and attract my interest to explore more.

This plugin loader is able to load DLL externally. On the other hand, it raises a security concern. Let's say the DLL is develop by external developer which may pose some security risk.

How can we control the loaded plugin such that it runs in a limited or isolated environment. E.g. the plugin has limited access to the file systems at the server?

DependencyInjection with MvcPluginLoader

Hi,

First off nice library, second of all I'm trying to get the Prise.Mvc to work with Dependency Injection. I got it to work when when loading the plugins in ConfigureServices however when I move it to Configure it can't initialize the plugin.

This is my code for activating the plugins:

           using (var scope = services.BuildServiceProvider().CreateScope())
            {
                var pathService = scope.ServiceProvider.GetRequiredService<IPathService>();
                var mvcPluginLoader = scope.ServiceProvider.GetRequiredService<IMvcPluginLoader>();
                var pluginLoader = scope.ServiceProvider.GetRequiredService<IPluginLoader>();
                var builder = scope.ServiceProvider.GetRequiredService<ApplicationPartManager>();


                var plugins = await mvcPluginLoader.FindPlugins<IPluginMvc>(pathService.GetPluginPath());

                foreach (var assemblyScanResult in plugins)
                {
                    Console.WriteLine(assemblyScanResult.ContractType);

                    var plugin = await mvcPluginLoader.LoadPluginAssembly<IPluginMvc>(assemblyScanResult);
                    builder.ApplicationParts.Add(new PluginAssemblyPart(plugin.Assembly));
                }
            }

And this is my controller in the plugin:

public class TestController : ControllerBase
    {
        private readonly ILogger<TestController> _logger;
        private readonly IPathService _pathService;

        public TestController(ILogger<TestController> logger, IPathService pathService)
        {
            _logger = logger;
            _pathService = pathService;
        }

        [HttpGet("Test")]
        public IActionResult Test()
        {
            _logger.LogInformation("Test called!");
            return Ok(_pathService.GetPluginPath());
        }
    }

I'm using the latest version of the plugin and .NET 5.0

Is it possible to have a simple plugin sample for a Console App?

I'm searching to use this framework to manage some plugins in a .NET Core console app (.NET Core 3.1)
I'm sure that the problem is on my side, but I'm not able to adapt the code from your samples to my application.

Is it possible to have a simple sample for a Console App?

In my app I obtain this error: "System.TypeLoadException: 'Declaration referenced in a method implementation cannot be a final method....."

Plugin not found during scanning because of folder "runtimes"

Hello ! I'm a student and for my internship, I've been trying to develop a 100% modular architecture and that's how I found your framework.
I've been trying to activate a SQL plugin using Entity, but the plugin is never found when the publish folder is scanned with the PluginLoader.
Here's the csproj of my plugin :
image
It's pretty much identical to your SqlPlugin in your samples.

To see if the problem came from my Prise configuration, I tried to implement the same contract with a test project without the Entity dependencies and it worked !
Since the only difference I've noticed in my publish folder was the lack of a folder called "runtimes", I re-published my normal sql plugin and deleted the folder. Now the plugin is scanned and found, but it can't be activated. Here's the stacktrace just in case :
image

I suppose this was to be expected, but now I'm a bit stuck and I don't understand how the folder can stop the loader from finding my plugin...

Also, all the projects I've been talking about have .NET 5.0 as their TargetFramework, except for my Contract which is aiming for netstandard2.0.

Do you have any idea how I could solve this ?
Thanks in advance and sorry if I wasn't clear enough !

Contract ui

Hello,

I wanted to know if it was simple to make a contract for plugins to access UI components, from the application.

[Enhancement] MAUI support ?

I think that instantiating MAUI might be worthwhile.

We can start testing to see what changes we need to make to support cross platform plugins. ๐Ÿ”ฅ ๐Ÿ”ฅ

Steps to test:

  • Create MAUI projet
  • Create MAUI Contract
  • Create MAUI Plugin

If the three steps above are achievable without change, then that's fine, otherwise we'll have to make the changes and implement cross-platform testing.

Steps in case of non compatibility:

  • Define the items to be modified in Prise
  • Define whether the changes made will not affect the other elements of Catch
  • Define cross-platform testing
  • Define samples MAUI for project

Is it planned to add documentation on how to use the Framework?

Hi, I'm starting on using the framework to try to manage a my library as a plugin.
I have downloaded all the samples to check how works the framework, but it's not easy, for me, without knowing the architecture.
For example is not clear how manage the 'compatibility' with older plugins.
My idea is to have a base contract with the version of the plugin, but I'm not sure if this is the correct way to allow the compatibility from older and newer plugins.

For this reason I'm asking if it's planned to write a wiki or a documentation on the framework.

Thanks

[Enhancement] Wrapper

Consideration of other languages. Use of a wrapper to develop plugins in other languages and have them detected by Prise.

PluginBootstrapper.Bootstrap event called on each Http Get Request

I'm using Prise in a dotnetCore3.1 windows service, that does plugin discovery in the ApplicationLifetime.ApplicationStarted Event to add new Web Controllers at runtime.

Everything works fine !, I'm able to invoke Web Modules Methods, access Host Configuration using the Proxy, and invoke Services registered by the Bootstrapper of each Controller.

I use an Interface to allow Prise discover the module on the ApplicationStarted Event : IWebModule

Later when perform a Http Get request to any Controller method (I have three controllers and three bootstrapper classes), the Bootstrapper event is called.

But then after continuous request, the Bootstrapper event and all services are been loaded again and again.
This seems to me like a performance issue. :(

How can I preserve the scoped ServiceCollection and use the same one over the calls?, there is some singleton services and all other are scoped services.

I would like to: when load the plugin, load the service collection and later be able to access some singleton services inside the Scoped service collection.

I'm developing a Health Check system to monitor the Web Module Status, that is perform getting some data from singleton services running inside the Web Module.

services.AddPrise() middleware seems to be missing in latest .net 6 version release

Not sure if i am missing something. I am trying to update a 3.1 core project to .net 6 that using the Prise plugin framework. We have upgraded the nuget package to Prise 6.0.0 and the service.AddPrise() middleware method is no longer available. Is there any new documentation on migrating from .net core 3.1 to .net 6 for the Prise nuget package?

Authorize attribute on plugin controller issue

Hi

Firstly i just want to say thanks for creating this library, i am finding it very useful. However I have run into a hurdle around authorization on the controllers.

I am adding Authorization in my hosts startup like below:

services.AddAuthorization(options =>
 {
                options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build();
                options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
            });

And then applying the authorize attribute to the controller :

[Authorize(Policy = "ApiUser")]
    [ResponseCache(NoStore = true)]
    [ApiVersion("1.0")]
    [ApiController]
    [Route("api/[controller]/[action]")]
    public class ServiceNowRequestController : ControllerBase

When i try and hit the api endpoint i get an error of "Request not found. The specified uri does not exist." However if i remove the authorize attribute i can call it fine.

I tried doing AddAuthorization again in the controller bootstrape but this results in the same error.

Is it possible to use Auth with plugins at all, if so can you point me in the right direction.

thanks in advance

Question: Plugins from parallel projects (on solution)

Hello there!

From what I understand you need to have plugins in a specific directory for the system to work. From the tutorial:

We will publish the OpenWeather.Plugin to the _dist folder created at the root of our project, which means that the WeatherForecastController needs to look for Plugins from that directory on the local disk.

If I have a solution with several projects, one of them being the host, and another being a plugin, can I make it work without copying files? I mean, just by clicking "play" in Visual Studio without manual steps? Maybe with project references?

Can this be made?
Thanks

State of the Project

Seeing the last commits seems to be 2 years ago - what's the state of this project?

Getting started urls are dead

Hi,
The URLS on the getting started page are all not found...
Just wanted to check the implementation since i'm developing a application which needs a plugin based architecture.

How to use it in desktop application?

I have a .NET core 2.2 based Avalonia desktop application in which I want to use your framework. Let me know how shall I use your framework. Below is my current code for loading.

IServiceScope RegisterServices()
        {
            var collection = new ServiceCollection();

            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                if (!assembly.FullName.StartsWith("Jaya", StringComparison.InvariantCultureIgnoreCase))
                    continue;

                var types = assembly.DefinedTypes;
                foreach (TypeInfo typeInfo in types)
                    if (typeInfo.IsClass && typeInfo.Name.EndsWith("Service", StringComparison.InvariantCulture))
                        collection.AddScoped(typeInfo);
            }

            var container = collection.BuildServiceProvider();
            var scopeFactory = container.GetRequiredService<IServiceScopeFactory>();
            return scopeFactory.CreateScope();
        }

Plugin framework version is newer than the host

This is more of question really, if anyone stumbled across this problem:

I run into an issue when trying to use load plug-ins from a test assembly, using dotnet test.
I've trived with both MSTest and xUnit, but essentially run into a mismatch of the .NETCoreApp version.
The contract, plug-in and the host have the <TargetFramework>netcoreapp3.1</TargetFramework> set.

Error:

 Test method Acme.LoadDummyPlugin threw exception: 
    Prise.PrisePluginException: Plugin framework version .NETCoreApp,Version=v3.1 is newer than the host .NETCoreApp,Version=v2.1. Please upgrade the host to load this plugin.

When I convert from a unit test to a console app, everything runs fine. But I'd love to create unit tests for the plug-ins.

Has anyone experienced this problem?

FindPlugin Problem

Why don't use PluginTypeName for FindPlugin?

My Situation is there have several implementation plugins within same assembly, so I can't use FindPlugin method to find specific plugin for execute.

I wish that assign PluginTypeName with implementation Type name and FindPlugin method can be implemented find by PluginTypeName as below.

PluginTypeName = implementation.Name, in public virtual Task<IEnumerable<AssemblyScanResult>> Scan(IAssemblyScannerOptions options)

wish be .FirstOrDefault(p => p.PluginTypeName == plugin); or not .FirstOrDefault(p => p.AssemblyPath.Split(Path.DirectorySeparatorChar).Last().Equals(plugin)); in FindPlugin implementation

How about with my recommendation? I wish it can be change in new version. Thanks.

[BUG] When loading the same assembly twice

Assembly scanning results in returning an entry per found plugin interface implementation, because of this, the same assembly is returned multiple times and thus loaded multiple times.
This results in a runtime error when executing any of the plugins from this assembly.

Proposed fix: let assembly scanning return 1 assembly per found plugin implementation type.

Loading plugins on runtime

It's possible load plugins on runtime ?
I was thinking of a 'marketplace' where my customers can select and install the plugins without the need of restarting the app

Let me know if that is possible, thanks!

[Enhancement] DependaBot ๐Ÿค–

I think that to increase productivity and security we should instantiate DependaBot so that it can propose package updates.

This way we would have instant visibility of the updates to be done. ๐Ÿš€

Support for OSX?

Hi I am looking for cross platform plugin systems for .net core and find yours, but trying the example with avalonia on OS X Mojave get the error "Unhandled exception. Prise.PrisePluginException: Platform is not supported"... so my question is, what is necesary to make osx supported?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.