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. 20155209 2016-2017-2 《Java程序设计》第十周学习总结

    20155209 2016-2017-2 <Java程序设计>第十周学习总结 教材学习内容总结 计算机网络,是指分布在不同地理区域的计算机用通信线路互连起来的一个具有强大功能的网络系统.网 ...

  2. 20155212 ch02 课下作业

    20155212 ch02 课下作业 T1 题目 参考附图代码,编写一个程序 "week0601学号.c",判断一下你的电脑是大端还是小端 相关知识 小端法:最低有效字节在最前面 ...

  3. 20155318 2016-2017-2 《Java程序设计》第十周学习总结

    20155318 2016-2017-2 <Java程序设计>第十周学习总结 教材学习内容总结 学习目标 了解计算机网络基础 掌握Java Socket编程 理解混合密码系统 掌握Java ...

  4. 最优布线问题(wire.cpp)

    最优布线问题(wire.cpp) [问题描述] 学校有n台计算机,为了方便数据传输,现要将它们用数据线连接起来.两台计算机被连接是指它们间有数据线连接.由于计算机所处的位置不同,因此不同的两台计算机的 ...

  5. KVM虚拟机无法启动

    一.启动虚拟机报错: [root@KVM ~]# virsh start node-mssql-test01 error: Failed to start domain node-mssql-test ...

  6. Yii2.0 高级模版编写使用自定义组件(component)

    翻译自:http://www.yiiframework.com/wiki/760/yii-2-0-write-use-a-custom-component-in-yii2-0-advanced-tem ...

  7. 关于网易云验证码V1.0版本的服务介绍

    服务介绍 易盾验证码是一个用于区分人和机器的通用验证码组件.传统的字符型验证码由于存在破解率高,用户体验不友好等问题,已不适用于现今的互联网环境.易盾验证码抛弃了传统字符型验证码展示-填写字符-比对答 ...

  8. C/C++ 下mysql应用封装(连接增删改查)

    mysql - 初始化 1) mysql_init():初始化数据库 2) mysql_real_connect()(不推荐用Mysql_connect()):连接数据库 详细代码如下: bool d ...

  9. div不设置高度背景颜色或外边框不能显示的解决方法

    在使用div+css进行网页布局时,如果外部div有背景颜色或者边框,而不设置其高度,在浏览时出现最外层Div的背景颜色和边框不起作用的问题. 大体结构<div class="oute ...

  10. Linux内核学习笔记(1)-- 进程管理概述

    一.进程与线程 进程是处于执行期的程序,但是并不仅仅局限于一段可执行程序代码.通常,进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个 ...