Plugin Framework Logo

New version of Plugin Framework for .NET adds support for reading plugin catalog configuration from appsettings.json and improves plugin tagging. The release is now available through Nuget: https://www.nuget.org/packages/Weikio.PluginFramework/

For those who are not familiar with Plugin Framework, it is a plugin platform for .NET applications, including ASP.NET Core, Blazor, WPF, Windows Forms and Console apps. It is light-weight and easy to integrate and supports multiple different plugin catalogs, including .NET assemblies, Nuget packages and Roslyn scripts.

Here’s an introduction post about Plugin Framework. InfoQ did a an article about the framework, it’s available from their site.

The 1.1.0 release

I’m especially happy with the support for appsettings.json as it’s the first community contribution (thanks @panoukos41 (github.com)!) and as it also enables new scenarios for the framework. Folder and assembly catalogs can be configured with the following syntax (support for more catalog types is coming in the upcoming versions):

  "PluginFramework": {
    "Catalogs": [
      {
        "Type": "Folder",
        "Path": "..\\Shared\\Weikio.PluginFramework.Samples.SharedPlugins\\bin\\debug\\netcoreapp3.1"
      },
      {
        "Type": "Assembly",
        "Path": ".\\bin\\Debug\\netcoreapp3.1\\WebAppPluginsLibrary.dll"
      }
    ]
  }

Sample for this specific feature is available from https://github.com/weikio/PluginFramework/tree/master/samples/WebAppWithAppSettings

New version also improves the plugin tagging. The idea behind plugin tagging is that Plugin Framework can for example scan a folder of dlls and tag the found plugins based on the given criteria. The developer can then use the plugins based on their tags. This is useful in situations where the application supports multiple types of plugins.

Here’s an example where all the found plugins are tagged as “operator”:

            var folderPluginCatalog = new FolderPluginCatalog(@"..\..\..\..\Shared\Weikio.PluginFramework.Samples.SharedPlugins\bin\debug\netcoreapp3.1",
                type => { type.Implements<IOperator>().Tag("operator"); });

And another part of the same app tags plugins as “dialog”:

            var folderCatalog = new FolderPluginCatalog(@"..\..\..\..\WinFormsPluginsLibrary\bin\debug\netcoreapp3.1",
                type =>
                {
                    type.Implements<IDialog>()
                        .Tag("dialog");
                });

Now when these plugins are used in the application, they can be fetched using the tags. Here’s an example where the “operator” and “dialog” tags are used:

        private void AddCalculationOperators()
        {
            var allPlugins = _allPlugins.GetByTag("operator");
            foreach (var plugin in allPlugins)
            {
                listBox1.Items.Add(plugin);
            }
        }

        private void AddDialogs()
        {
            var dialogPlugins = _allPlugins.GetByTag("dialog");
            
            foreach (var dialogPlugin in dialogPlugins)
            {
                var menuItem = new ToolStripButton(dialogPlugin.Name, null, (o, args) =>
                {
                    var instance = (IDialog) Activator.CreateInstance(dialogPlugin);
                    instance.Show();
                });

                dialogPluginsToolStripMenuItem.DropDownItems.Add(menuItem);
            }
        }

Sample for plugin tagging is available from https://github.com/weikio/PluginFramework/tree/master/samples/WinFormsApp

What’s next?

Work will continue on improving (and adding new) plugin catalogs but the main thing for the next release is the support for .NET 5. This will enable one scenario which hasn’t been that well supported: Using Plugin Framework in WebAssembly version of Blazor.

Plugin Framework Logo

Plugin Framework is a new MIT-licensed plugin platform for .NET Core applications. It is light-weight and easy way to add a plugin-support into your application. It supports all the major types of .NET Core applications, including ASP.NET Core, Blazor, Console Apps and WPF & WinForms.Plugin Framework has a built-in support for Nuget packages and feeds.

The Plugin Framework version 1.0.0 is now available from Nuget: https://www.nuget.org/packages/Weikio.PluginFramework/. For Blazor and ASP.NET Core applications the recommended package is https://www.nuget.org/packages/Weikio.PluginFramework.AspNetCore

Main features

Plugin Framework follows an "Everything is a plugin" -mentality. It provides out of the box support for sharing plugins using Nuget packages, Roslyn scripts and delegates, in addition to the more common ones like .NET assemblies and folders.

Here’s a short summary of the major features provided by Plugin Framework:

  • Deliver plugins as Nuget-packages, .NET assemblies, Roslyn scripts and more.
  • Easy integration into a new or an existing .NET Core application.
  • Automatic dependency management.
  • MIT-licensed, commercial support available.

Quick start: ASP.NET Core

Install-Package Weikio.PluginFramework.AspNetCore

Using Plugin Framework can be as easy as adding a single new line into ConfigureServices. The following code finds all the plugins (types that implement the custom IOperator-interface) from the myplugins-folder.

services.AddPluginFramework<IOperator>(@".\myplugins");

The plugins can be used in a controller using constructor injection:

public CalculatorController(IEnumerable<IOperator> operator)
{
	_operators = operators;
}

Getting started

Best way to learn more about Plugin Framework is through the project's home page at Github: https://github.com/weikio/PluginFramework. The repository contains multiple different samples at the time of writing this. Here's a list:

Plugin Framework & .NET Console Application
Plugin Framework & ASP.NET Core
Plugin Framework & Blazor
Plugin Framework & WPF App
Nuget & Plugin Framework & ASP.NET Core
Roslyn & Plugin Framework & ASP.NET Core
Delegates & Plugin Framework & ASP.NET Core

How does this work?

When you create your application and add support for Plugin Framework, you usually define two things:

1. The specifications for the plugins. In some applications a plugin can add new functionality into the UI. In other apps, plugins are used to distribute logs into multiple different systems. The application defines what kind of extensions it supports.

2. The locations where the application can find the plugins. Many applications use a specific “plugins”-folder to indicate the location where plugins should exist in the hard drive. In some situations plugins are installed in runtime from Nuget. These plugin locations are called catalogs. As a developer you define what catalogs your application uses.

What makes a plugin?

In the context of the Plugin Framework, plugin is a single .NET Type. For some applications a plugin is a type which implements a specific interface. In some applications a plugin is a type which has a single public method called Run. Attributes are often used to indicate the plugins and that is also supported by Plugin Framework. From the Plugin Framework's point of view anything or everything can be a plugin.

What is a plugin catalog?

Each plugin is part of a catalog. Plugin Framework provides the following officially supported catalogs:

  • Type
  • Assembly
  • Folder
  • Delegate
  • Roslyn script
  • Nuget package
  • Nuget feed

License & Source code & Commercial Support & Issue Tracking

As previously mentioned, Plugin Framework is MIT-licensed and its source code is available from GitHub: https://github.com/weikio/PluginFramework. GitHub also contains the issue tracking.

There is also commercial support available for Plugin Framework. That is provided by Adafy https://adafy.com. Though the website is only in Finnish, the support is available both in English and in Finnish.

0 Comments

If you’re using ASP.NET Core 3.1.1 and are seeing HTTP Error 500 when deploying your application into Azure App Service, there’s a high change that the issue is caused by a known issue:

If your project has a package reference that transtively references certain assemblies in the Microsoft.AspNetCore.App shared framework
that are also available as NuGet packages and executes on a runtime other than 64-bit Windows, you will receive a runtime exception at the time the assembly is loaded with a message like:

Could not load file or assembly 'Microsoft.AspNetCore.DataProtection.Abstractions, Version=3.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. The located assembly's manifest definition does not match the assembly reference. (0x80131040)

 

Solution is to switch to 64 bit Azure App Service or to manually reference the problematic package.

Debugging this can be a pain as Azure App Service’s diagnostic tools think that everything is running smoothly and IIS’ stdout only reports that application has successfully started. What can help is wrapping the CreateWebHostBuilder inside a try-catch. Here’s an example which uses nLog:

        public static void Main(string[] args)
        {
            var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
            var isDev = string.Equals(environment, "development", StringComparison.InvariantCultureIgnoreCase);
            var configFileName = isDev ? "nlog.Development.config" : "nlog.config";

            var logger = NLogBuilder.ConfigureNLog(configFileName).GetCurrentClassLogger();

            logger.Info("Starting application. {Environment}, {LoggingConfigurationFileName}", environment, configFileName);

            try
            {
                CreateWebHostBuilder(args).Build().Run();
            }
            catch (Exception e)
            {
                logger.Error(e, "Failed to start application", e);
            }
            finally
            {
                NLog.LogManager.Shutdown();
            }
        }

0 Comments

When Adaptive Cards for .NET is used to render a HTML card, the card renderer automatically adds few classes into the actions. Here’s an example where an adaptive card is rendered in ASP.NET Core using the defaults:

image

We can see that the actions automatically get classes like ac-pushButton and ac-action-openUrl. CSS can be used to customize these classes. But if you’re using something like Bootstrap, it’s quite likely that you want to use classes like btn and btn-primary. This post shows how to use Bootstrap’s btn-classes with Adaptive Cards’ HTML renderer in ASP.NET Core.

Modifying the HTML renderer

You can modify the adaptive cards’ HTML renderer to inject the classes you like. The modifications happen through AdaptiveCardRenderer.ActionTransformers.Register. The modifications can be registered for example in the Configure-method of the application:

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddLocalization();
            services.AddDistributedMemoryCache();
            services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(20); });

            AdaptiveCardRenderer.ActionTransformers.Register<AdaptiveOpenUrlAction>((action, tag, context) =>
            {
                tag.AddClass("btn");
                tag.AddClass("btn-primary");
            });

The example above adds “btn” and “btn-primary” classes into each OpenUrl action:

image

Conditional classes

Classes can be added conditionally, based on the card’s schema. Here’s an example where action’s style is used to define the applied CSS classes:

            AdaptiveCardRenderer.ActionTransformers.Register<AdaptiveOpenUrlAction>((action, tag, context) =>
            {
                tag.AddClass("btn");

                if (string.Equals(action.Style, "destructive", StringComparison.InvariantCultureIgnoreCase))
                {
                    tag.AddClass("btn-danger");
                }
                else
                {
                    tag.AddClass("btn-primary");
                }
            });

image

As we can see, btn-danger is applied to the button on the right side.

image

Custom attributes

Register-method can be used for more than just adding custom classes. AddAttribute can be used to add new custom attributes to the button:

            AdaptiveCardRenderer.ActionTransformers.Register<AdaptiveOpenUrlAction>((action, tag, context) =>
            {
                tag.Attr("id", AdaptiveCardRenderer.GenerateRandomId());
                tag.Attr("data-ac-url", action.Url);
            });

Note

Register-method can be called only once per element type. If you have multiple calls to Register<AdaptiveOpenUrl>, the last one wins:

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddLocalization();
            services.AddDistributedMemoryCache();
            services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(20); });

            AdaptiveCardRenderer.ActionTransformers.Register<AdaptiveOpenUrlAction>((action, tag, context) =>
            {
                tag.AddClass("btn");
                tag.AddClass("btn-primary");
            });
            
            AdaptiveCardRenderer.ActionTransformers.Register<AdaptiveOpenUrlAction>((action, tag, context) =>
            {
                tag.AddClass("btn");
                tag.AddClass("btn-danger");
            });

image

0 Comments

Attach to dotnet.exe extension for Visual Studio 2017 & 2019 now has a new version 1.0.0.1. As the version number indicates, only a small thing has changed: The extension manifest had problems with its dependencies, causing issues when trying to install the extension to VS 2019 Preview.

The new version is now available for download from the project’s repository: https://github.com/mikoskinen/AttachToDotnet/tree/master/releases/1.0.0.1

Unfortunately the extension is still not available through the Visual Studio’s extension marketplace, but I hope to have it there in the following week.

Background

Just a quick recap and reasoning for the extension: There’s usually many dotnet.exe processes. Attaching Visual Studio’s debugger to the correct one can be hard. This extensions aims to solve the issue by displaying the actual .DLL of the dotnet.exe process.