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/

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

  1. User Authentication with Angular and ASP.NET Core

    User authentication is a fundamental part of any meaningful application. Unfortunately, implementing ...

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

    原文:Building Your First Web API with ASP.NET Core MVC and Visual Studio 作者:Mike Wasson 和 Rick Anderso ...

  3. 【原生态跨平台:ASP.NET Core 1.0(非Mono)在 Ubuntu 14.04 服务器上一对一的配置实现-篇幅1】

    鸡冻人心的2016,微软高产年. build 2016后 各种干货层出不穷. 1 Win10 集成了bash  ,实现了纳德拉的成诺,Microsoft Love Linux!!! 2 跨平台  ,收 ...

  4. ABP 教程文档 1-1 手把手引进门之 ASP.NET Core & Entity Framework Core(官方教程翻译版 版本3.2.5)

    本文是ABP官方文档翻译版,翻译基于 3.2.5 版本 官方文档分四部分 一. 教程文档 二.ABP 框架 三.zero 模块 四.其他(中文翻译资源) 本篇是第一部分的第一篇. 第一部分分三篇 1- ...

  5. 如何使用ASP.NET Core、EF Core、ABP(ASP.NET Boilerplate)创建分层的Web应用程序(第一部分)

    本文是为了学习ABP的使用,是翻译ABP官方文档的一篇实战教程,我暂时是优先翻译自己感兴趣或者比较想学习的部分,后续有时间希望能将ABP系列翻译出来,除了自己能学习外,有可能的话希望帮助一些英文阅读能 ...

  6. 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 ...

  7. ASP.NET Core身份识别

    Introduction to Identity 66 of 93 people found this helpful By Pranav Rastogi, Rick Anderson, Tom Dy ...

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

    在现代Web应用程序中,通常会使用Web, WebApp, NativeApp等多种呈现方式,而后端也由以前的Razor渲染HTML,转变为Stateless的RESTFulAPI,因此,我们需要一种 ...

  9. 5. abp集成asp.net core

    一.前言 参照前篇<4. abp中的asp.net core模块剖析>,首先放张图,这也是asp.net core框架上MVC模块的扩展点 二.abp的mvc对象 AbpAspNetCor ...

随机推荐

  1. Linux - 时间相关命令 - ntpdate, date, hwclock

    1. 概述 最近也不知道写啥了, 把之前的老文档整理一下, 凑个数什么的 配置时间这种工作, 偶尔还是要用一下 主要描述 3 个命令的简单适用 ntpdate hwlock 2. ntpdate 1. ...

  2. 20155212 ch03 课下作业

    T4 题目 通过输入gcc -S -o main.s main.c 将下面c程序"week0603学号.c"编译成汇编代码 int g(int x){ return x+3; } ...

  3. 实验三 敏捷开发与XP实践-1

    实验内容 XP基础 XP核心实践 相关工具 实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim编辑器> 课程 2.完成实验.撰写实验报告,实 ...

  4. C语言 迭代部分的代码编写

    C语言代码学习 迭代部分 迭代要用到函数部分的知识,一开始我写了计算n!的计算,代码和运行结果如下: 结果只能单一的计算出整数内的值,如果输入负值则结果为返回值1,显然是不对的,根据查书学习以后,知道 ...

  5. BZOJ1597_土地购买_KEY

    题目传送门 一道斜率优化的题目. 但暴力方程很关键. 我们先将x作为关键字Sort一遍,再将y处理成单调递减,即把无用的土地去除. f[i]=f[j]+a[i]*b[j+] -a[i]*b[j+]+f ...

  6. windows中使用mysql配置my.ini时的坑

    windows中安装mysql的一般步骤: mysql版本:5.7.16 1.解压 2.把解压的文件夹bin目录地址添加到环境变量PATH里面 3.在文件加中添加配置文件my.ini——配置内容后面说 ...

  7. CSP201312-3:最大的矩形

    引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的"计算机职业资格认证"考试, ...

  8. nginx 源码阅读 core

    ngx_config.h 数据对齐 #define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1)) ngx_core.h #define ng ...

  9. Apriori 获取关联规则实现

    前言 自己的一个Apriori 获取关联规则的python实现.具体原理不讲,代码添加了说明,还是很好理解的. 数据预处理 #最小置信度 min_conf = 0.5 #最小支持度 min_sup = ...

  10. leetcode个人题解——#36 valid Sudoku

    思路题目里已经给出来了,判断是否是一个有效数独,只需满足以下三个条件: 1.同行元素不重复且1-9都有: 2.同列元素不重复且1-9都有: 3.每个粗线分隔的3*3的小九宫格元素不重复且1-9都有. ...