Building simple plug-ins system for ASP.NET Core(转)

Recently I built plug-ins support to my TemperatureStation IoT solution web site. The code for .NET Core is different from what we have seen on full .NET Framework (application domains etc) but there’s still nothing complex. This blog post describes how to build simple plug-ins support to ASP.NET Core web application.

After some searching in web and some small experimenting I came out with simple solution that covers the following:

  1. Loading types from assemblies
  2. Registering types automatically with built-in dependency injection
  3. Getting instances through built-in dependency injection

The code shown here is also kind of experimental and it is taken from my open-source IoT solution called TemperatureStation.

Calculator plug-ins

TemperatureStation has plug-ins called Calculators. Calculators are classes that can be bound to measurements and when readings are reported then Calculators are run on readings. They provide different calculations like finding freezing point of liquid, estimating the time it takes for liquid to get to freezing point etc.

For calculators there is ICalculator interface shown below.


public interface ICalculator{    double Calculate(SensorReadings readings, Measurement measurement);    string DisplayValue(double value);    bool ReturnsReading { get; }    void SetParameters(string parameters);}


Calculators use CalculatorAttribute to define some metadata.


[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]public class CalculatorAttribute : Attribute{    public CalculatorAttribute()    {        Order = -1;        ShowOnChart = true;    }

    public string Name { get; set; }    public int Order { get; set; }    public bool ShowOnChart { get; set; }    public string DisplayLabel { get; set; }}


And here is the example of dummy calculator.


public class DummyCalculator : ICalculator{    public bool ReturnsReading    {        get { return true; }    }

    public double Calculate(SensorReadings readings, Measurement measurement)    {        return readings.Readings.First().Reading + 10f;    }

    public string DisplayValue(double value)    {        return value.ToString();    }

    public void SetParameters(string parameters)    {    }}

Finding plug-in types

To detect plug-ins I wrote CalculatorsLoader class. This a static class that creates list of calculator types. Rule is simple: class must implement ICalculator interface and must have CalculatorAttribute. Consider this class as internal matter of application.


public static class CalculatorsLoader{    private static IList<Type> _calculatorTypes;

    public static IList<Type> CalculatorTypes    {        get        {            if(_calculatorTypes == null)            {                LoadCalculatorTypes();            }

            return _calculatorTypes.ToList();        }                }

    private static void LoadCalculatorTypes()    {        if (_calculatorTypes != null)        {            return;        }

        var calcs = from a in GetReferencingAssemblies()                    from t in a.GetTypes()                    where t.GetTypeInfo().GetCustomAttribute<CalculatorAttribute>() != null                          && t.GetTypeInfo().ImplementedInterfaces.Contains(typeof(ICalculator))                    select t;

        _calculatorTypes = calcs.OrderBy(t => t.GetTypeInfo().GetCustomAttribute<CalculatorAttribute>().Order).ToList();    }

    private static IEnumerable<Assembly> GetReferencingAssemblies()    {        var assemblies = new List<Assembly>();        var dependencies = DependencyContext.Default.RuntimeLibraries;

        foreach (var library in dependencies)        {            try            {                var assembly = Assembly.Load(new AssemblyName(library.Name));                assemblies.Add(assembly);            }            catch (FileNotFoundException)            { }        }        return assemblies;    }}

Automatic registering of plug-in types

To use ASP.NET Core dependency injection I wrote class that provides extension method for IServiceCollection. Dependency injection is needed because Calculators may use constructor injection to access services and database.


public static class CalculatorExtensions{    public static void AddCalculators(this IServiceCollection services)    {        foreach(var calcType in CalculatorsLoader.CalculatorTypes)        {            services.AddTransient(calcType);        }    }}


This is how to use AddCalculators extension method in Startup class of web application.


public void ConfigureServices(IServiceCollection services){    // ...

    services.AddMvc();

    services.AddSingleton<ICalculatorProvider, CalculatorProvider>();    services.AddCalculators();

    // ...}


When web application starts then ConfigureServices method is called and Calculators are automatically registered.

Plug-in provider

To access calculators I wrote ICalculatorProvider interface and CalculatorProvider class. This class with CalculatorExtensions are the only classes that access CalculatorsLoader directly. All other classes in system use provider to query calcutor types. ICalculatorProvider is introduced to ASP.NET Core dependency injection in application Startup class.

GetCalculators() method uses framework level dependency injection to create instances of ICalculator.


public interface ICalculatorProvider{    IDictionary<string, ICalculator> GetCalculators();    IEnumerable<string> GetNames();    IEnumerable<Type> GetTypes();}

public class CalculatorProvider : ICalculatorProvider{    private IServiceProvider _serviceProvider;

    public CalculatorProvider(IServiceProvider serviceProvider)    {        _serviceProvider = serviceProvider;    }

    public IDictionary<string,ICalculator> GetCalculators()    {        var result = new Dictionary<string, ICalculator>();

        foreach(var type in CalculatorsLoader.CalculatorTypes)        {            var calc = (ICalculator)_serviceProvider.GetService(type);            var name = type.GetTypeInfo().GetCustomAttribute<CalculatorAttribute>().Name;

            result.Add(name, calc);        }

        return result;    }

    public IEnumerable<string> GetNames()    {        return CalculatorsLoader.CalculatorTypes                .Select(t => t.GetTypeInfo().GetCustomAttribute<CalculatorAttribute>()?.Name)                .Where(t => !string.IsNullOrWhiteSpace(t));                }

    public IEnumerable<Type> GetTypes()    {        return CalculatorsLoader.CalculatorTypes;    }}

Using plug-ins in code

Here is the example about how I use ICalculatorProvider in controller code to output the list of available calculators.


public class DummyController : Controller{    private ICalculatorProvider _calculatorProvider;

    public DummyController(ICalculatorProvider calculatorProvider)    {        _calculatorProvider = calculatorProvider;    }

    public IActionResult GetCalculators()    {        var calculators = _calculatorProvider.GetCalculators();        var output = "";

        foreach (var calculator in calculators)        {            output += calculator.Key + "\r\n";        }

        return Content(output, "text/plain");    }}


The real usage in my solution is more complex but this example gives the point.

Wrapping up

On .NET Core things work a little bit differently compared to full .NET Framework. Although this solution is not perfect it was still pretty easy to find information and make code work. It was also easy to automatically register plug-in types with ASP.NET Core framework-level dependency injection and come out with simple classes that architecturally fit in to web application. For my IoT system this solution today is good enough to go with.

原文:http://gunnarpeipman.com/2017/01/aspnet-core-plugins/

时间: 2024-10-12 15:56:59

Building simple plug-ins system for ASP.NET Core(转)的相关文章

第二章 指南(2)用 Visual Studio 和 ASP.NET Core MVC 创建首个 Web API

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 原文:Building Your First Web API with ASP.NET Core MVC and Visual Studio 作者:Mike Wasson 和 Rick Anderson 翻译:谢炀(kiler) 校对:何镇汐.

ASP.NET Core 介绍

原文 https://docs.asp.net/en/latest/intro.html by Daniel Roth, Rick Anderson and Shaun Luttin 章节 什么是 ASP.NET Core? 为什么重新设计出一个 AsP.NET Core? 应用解析 启动 服务 中间件 ASP.NET Core 是对 ASP.NET 的一次重大修改,基本是重新设计了.本篇向你介绍了ASP.NET Core 的一些新概念以及这些新的特性能对我们编写现在流行的WEB应用提供哪些帮助

Handle Refresh Token Using ASP.NET Core 2.0 And JSON Web Token

来源:   https://www.c-sharpcorner.com/article/handle-refresh-token-using-asp-net-core-2-0-and-json-web-token/ In this article , you will learn how to deal with the refresh token when you use jwt (JSON Web Token) as your access_token. Backgroud Many peo

ASP.NET Core 认证与授权[4]:JwtBearer认证

在现代Web应用程序中,通常会使用Web, WebApp, NativeApp等多种呈现方式,而后端也由以前的Razor渲染HTML,转变为Stateless的RESTFulAPI,因此,我们需要一种标准的,通用的,无状态的,与语言无关的认证方式,也就是本文要介绍的JwtBearer认证. 目录 Bearer认证 JWT(JSON WEB TOKEN) 头部(Header) 载荷(Payload) 签名(Signature) 示例 模拟Token 注册JwtBearer认证 添加受保护资源 运行

Building microservices with ASP.NET Core (without MVC)(转)

There are several reasons why it makes sense to build super-lightweight HTTP services (or, despite all the baggage the word brings, “microservices”). I do not need to go into all the operational or architectural benefits of such approach to system de

Asp.Net Core使用System.Drawing.Common部署到docker报错问题

原文:Asp.Net Core使用System.Drawing.Common部署到docker报错问题 Asp.Net Core 2.1发布后,正式支持System.Drawing.Common绘图了,可以用来做一些图片验证码之类的功能.但是把网站部署到docker容器里运行会遇到很多问题,也是非常闹心的,本文记录这些问题,希望帮到有需要的人. 创建网站 前提条件:安装最新版VS2017和Net Core SDK 2.1. 首先新建网站,选择Asp.Net Core 2.1 Web应用程序(模型

A Simple Actions Recognition System

1. Problem Definition There's no doubt that researches and applications on the foundation of videos has become a popular field including intelligence surveillance, interactions between human and machines, content-based video retrieval and so on. Howe

ASP.NET Core Building chat room using WebSocket

Creating "Login form" We use here simple form where user can insert his or her preferred nick name for chat. To keep us focused on WebSocket stuff we don't add any logic or checks in this point. Let's add view called InsertUserName.cshtml under

Building a Web App with ASP.NET Core, MVC, Entity Framework Core, Bootstrap, and Angular

Since I have spent many years on Windows Application development in my first three years of software career.  I was interested in the C#, had tried to understand the basic knowledge about C#. The programs, the patterns, the object-oriented methodolog