Coder Social home page Coder Social logo

lamondlu / coolcat Goto Github PK

View Code? Open in Web Editor NEW
333.0 13.0 91.0 7.4 MB

A sample about how to create a dynamic plugins mechanism with ASP.NET Core Mvc at runtime. This sample is based on .NET Core 3.1 and .NET 5

Home Page: https://www.cnblogs.com/lwqlun/p/11343141.html

C# 76.69% CSS 5.42% HTML 16.49% TSQL 0.35% JavaScript 1.06%
asp-net-core asp-net-core-mvc dynamic-plugins docker docker-compose plugin-architecture

coolcat's Introduction

CoolCat

A sample about how to create a dynamic plugins mechanism with ASP.NET Core Mvc based on the AssemblyLoadContext.

This whole project is built under .NET Core 3.1 and .NET 5.

What i will do and what i will not do

I want to build a runtime plugin mechanism based on .NET Core 3.1 and .NET 5. Each plugin will be isolated by a custom AssemlyLoadContext. So the framework allow you to reference same library with different version.

Getting Started

  • Clone the source code
  • Run docker-compose up
  • Install the pre-set modules
  • Start to use the system

How to create and publish a plugin

  • Run dotnet new -i CoolCatModule, it will install the CoolCatModule on your machine
  • Run dotnet new CoolCatModule -n {your plugin name}
  • Build the plugin with VisualStudio 2019 or dotnet publish
  • Package the release files into a zip package

coolcat's People

Contributors

fanniejing avatar lamondlu 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

coolcat's Issues

静态文件

插件的css js等静态文件放到哪里好,有什么好的方法么?

如何安装\卸载插件中的 BackgroundService

比如在项目 DemoPlugin1 中添加一个自定义的 BackgroundService,目的是插件加载后就一秒输出一次当前时间,插件卸载时停止输出。

using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace DemoPlugin1
{
    public class Class1 : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                Console.WriteLine(DateTime.Now);
                await Task.Delay(1000);
            }
        }
    }
}

正常的开发方式,需要在 ConfigureServices() 方法中依赖注入

public void ConfigureServices(IServiceCollection services)
{
    services.AddHostedService<Class1>();
}

在插件中配置的 HostedService 要怎么才能运行\停止?

升级插件,版本号升级成功但软件未升级

使用 DemoPlugin1 测试,新增 api Version
image

修改返回值,打包成两个不同的 zip。

        [HttpGet]
        public string Version()
        {
            return "1.1";
        }

发布后打包成 DemoPlugin1.1.zip

        [HttpGet]
        public string Version()
        {
            return "1.2";
        }

发布后打包成 DemoPlugin1.2.zip


先上传 DemoPlugin1.1.zip,然后访问 api
image

然后上传 DemoPlugin1.2.zip,此时版本被更新为 1.2
image
但访问 api,仍返回 1.1
image

使用最新源码,关闭再启用插件打开插件页报这个错,

System.InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'DemoPlugin1.Models.TestClass', but this ViewDataDictionary instance requires a model item of type 'DemoPlugin1.Models.TestClass'.
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(Object value)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary..ctor(ViewDataDictionary source, Object model, Type declaredModelType)
at lambda_method33(Closure , ViewDataDictionary )
at Microsoft.AspNetCore.Mvc.Razor.RazorPagePropertyActivator.Activate(Object page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)
at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable1 statusCode) at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable1 statusCode)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable`1 statusCode)
at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
at Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

docker-compose up 出错

Describe the bug
The target database hasn't been prepared.

Unhandled exception. MySql.Data.MySqlClient.MySqlException (0x80004005): Unable to connect to any of the specified MySQL hosts.

---> System.AggregateException: One or more errors occurred. (Connection refused)

---> System.Net.Sockets.SocketException (111): Connection refused

at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)

at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)

at System.Threading.Tasks.ValueTask.ValueTaskSourceAsTask.<>c.<.cctor>b__4_0(Object state)

--- End of stack trace from previous location ---

at System.Net.Sockets.TcpClient.CompleteConnectAsync(Task task)

--- End of inner exception stack trace ---

at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)

at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout)

at MySql.Data.Common.StreamCreator.GetTcpStream(MySqlConnectionStringBuilder settings)

at MySql.Data.Common.StreamCreator.GetStream(MySqlConnectionStringBuilder settings)

at MySql.Data.MySqlClient.NativeDriver.Open()

at MySql.Data.MySqlClient.NativeDriver.Open()

at MySql.Data.MySqlClient.Driver.Open()

at MySql.Data.MySqlClient.Driver.Create(MySqlConnectionStringBuilder settings)

at MySql.Data.MySqlClient.MySqlPool.CreateNewPooledConnection()

at MySql.Data.MySqlClient.MySqlPool.GetPooledConnection()

at MySql.Data.MySqlClient.MySqlPool.TryToGetDriver()

at MySql.Data.MySqlClient.MySqlPool.GetConnection()

at MySql.Data.MySqlClient.MySqlConnection.Open()

at FluentMigrator.Runner.Processors.GenericProcessorBase.<>c__DisplayClass6_1.<.ctor>b__1()

at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)

at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)

at System.Lazy`1.CreateValue()

at System.Lazy`1.get_Value()

at FluentMigrator.Runner.Processors.GenericProcessorBase.get_Connection()

at FluentMigrator.Runner.Processors.GenericProcessorBase.EnsureConnectionIsOpen()

at FluentMigrator.Runner.Processors.MySql.MySqlProcessor.Exists(String template, Object[] args)

at FluentMigrator.Runner.Processors.MySql.MySqlProcessor.TableExists(String schemaName, String tableName)

at FluentMigrator.Runner.VersionLoader.get_AlreadyCreatedVersionTable()

at FluentMigrator.Runner.VersionLoader.LoadVersionInfo()

at FluentMigrator.Runner.VersionLoader..ctor(IProcessorAccessor processorAccessor, IConventionSet conventionSet, IMigrationRunnerConventions conventions, IVersionTableMetaData versionTableMetaData, IMigrationRunner runner)

--- End of stack trace from previous location ---

at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)

at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)

at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance[T](IServiceProvider provider, Object[] parameters)

at Microsoft.Extensions.DependencyInjection.FluentMigratorServiceCollectionExtensions.<>c.b__0_6(IServiceProvider sp)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.b__0(ServiceProviderEngineScope scope)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)

at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)

at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)

at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)

at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)

at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)

at System.Lazy`1.CreateValue()

at System.Lazy`1.get_Value()

at FluentMigrator.Runner.MigrationRunner.get_VersionLoader()

at FluentMigrator.Runner.MigrationRunner.IsMigrationStepNeededForUpMigration(IMigrationInfo migration, Int64 targetVersion)

at FluentMigrator.Runner.MigrationRunner.<>c__DisplayClass60_0.b__0(KeyValuePair`2 pair)

at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()

at FluentMigrator.Runner.MigrationRunner.MigrateUp(Int64 targetVersion, Boolean useAutomaticTransactionManagement)

at FluentMigrator.Runner.MigrationRunner.MigrateUp(Boolean useAutomaticTransactionManagement)

at FluentMigrator.Runner.MigrationRunner.MigrateUp()

at CoolCat.Startup.ConfigureServices(IServiceCollection services) in /app/CoolCat/Startup.cs:line 41

at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)

at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)

at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.g__Startup|0(IServiceCollection serviceCollection)

at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)

at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.b__0(IServiceCollection services)

at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services, Object instance)

at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass13_0.b__0(HostBuilderContext context, IServiceCollection services)

at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()

at Microsoft.Extensions.Hosting.HostBuilder.Build()

at CoolCat.Program.Main(String[] args) in /app/CoolCat/Program.cs:line 10

插件从高版本降级会导致宿主程序异常退出

使用 DemoPlugin1 项目测试,如下图所示。
两份压缩包,仅 plugin.json 不同,其余内容完全一致。plugin.json 也仅有 "version": "1.1.0" 和 "version": "1.1.1" 的区别。

image
image

先上传 x1.1.0.zip 再上传 x1.1.1.zip,程序版本从 1.1.0 升级到 1.1.1
先上传 x1.1.1.zip 再上传 x1.1.0.zip,在点击 submit 按钮时宿主程序异常退出

此时控制台上没有错误日志,只有异常退出码:
Mystique\Mystique\bin\Debug\netcoreapp3.0\Mystique.exe (process 17672) exited with code -1073741819.

Dynamic adding CompiledRazorItems

我在自己的项目中尝试了将Area分到其他程序集中,然后发现可以通过给RazorViewEngineOptions添加其他的FileProvider、使用自己编写RazorCompiledItemLoader以支持从其他项目文件夹中监测cshtml动态修改。

今日重新阅读了您的第三篇文章,见到您说不能动态加载RazorCompiledItemAssembly,然后尝试在我自己的那个Loader中加了个throw来寻找调用堆栈。

System.Exception: Exception of type 'System.Exception' was thrown.
   at Microsoft.AspNetCore.Mvc.ApplicationParts.AreaRazorAssemblyPart.Microsoft.AspNetCore.Mvc.ApplicationParts.IRazorCompiledItemProvider.get_CompiledItems() in C:\Users\tlylz\Source\Repos\OnlineJudge\JudgeWeb.Features.AspNetCore\AppPart\AreaRazorAssemblyPart.cs:line 27
   at Microsoft.AspNetCore.Mvc.ApplicationParts.RazorCompiledItemFeatureProvider.PopulateFeature(IEnumerable`1 parts, ViewsFeature feature)
   at Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager.PopulateFeature[TFeature](TFeature feature)
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.CompiledPageRouteModelProvider.GetViewFeature(ApplicationPartManager applicationManager)
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.CompiledPageRouteModelProvider.GetViewDescriptors(ApplicationPartManager applicationManager)+MoveNext()
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.CompiledPageRouteModelProvider.CreateModels(PageRouteModelProviderContext context)
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.CompiledPageRouteModelProvider.OnProvidersExecuting(PageRouteModelProviderContext context)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionDescriptorProvider.BuildModel()
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionDescriptorProvider.OnProvidersExecuting(ActionDescriptorProviderContext context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.DefaultActionDescriptorCollectionProvider.UpdateCollection()
   at Microsoft.AspNetCore.Mvc.Infrastructure.DefaultActionDescriptorCollectionProvider.Initialize()
   at Microsoft.AspNetCore.Mvc.Infrastructure.DefaultActionDescriptorCollectionProvider.get_ActionDescriptors()
   at Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.GetTreeRouter()
   at Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.RouteAsync(RouteContext context)
   at Microsoft.AspNetCore.Routing.RouteCollection.RouteAsync(RouteContext context)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)

(有行号的是我自己的代码,不是官方的,我个人比较喜欢把这种Infra放到和官方命名空间里一样的位置。)

实际上IActionDescriptorChangeProvider的Token被激活以后,我的AreaRazorAssemblyPart是被重新执行了一遍的。所以猜想,您的第三篇文章中加载失败的问题,可能和CompiledRazorAssemblyPart有关系。

直接卸载,在点安装后出现如下问题

RuntimeBinderException: Cannot convert type 'System.Collections.Generic.List<DemoPlugin1.Models.BookViewModel>' to 'System.Collections.Generic.List<DemoPlugin1.Models.BookViewModel>'
CallSite.Target(Closure , CallSite , object )

插件间的这种通信方式是否会造成内存泄漏

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

SQL Server Support

I am trying to set this up under sql server but its not doing the migrations i changed the connection string but nothing is happening the site is just spinning.

插件 支持 `ConfigureServices(IServiceCollection services)` 与 `Configure(IApplicationBuilder app, IWebHostEnvironment env)`

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

插件如何 注入服务,以及添加 Middleware

Describe the solution you'd like
A clear and concise description of what you want to happen.

public interface IStartupPlugin : IPlugin
{
  /// <summary>
  /// This method gets called by the runtime. Use this method to add services to the container.
  /// </summary>
  /// <param name="services"></param>
  void ConfigureServices(IServiceCollection services);
  
  /// <summary>
  /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  /// </summary>
  /// <param name="app"></param>
  /// <param name="env"></param>
  void Configure(IApplicationBuilder app);
}

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Middleware 目测必须重新组建,因此没法热插拔,可以考虑对实现了 IStartupPlugin 插件,进行重启

Additional context
Add any other context or screenshots about the feature request here.

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.