• 仅针对Controller的属性注入;
  • 使用默认容器,不依赖第三方库;

故事背景

  闲来无事给项目做优化,发现大多数Controller里面都会用到Logger和AutoMapper,每个Controller都构造函数注入,感觉重复劳动太多了,ASP.NET Core默认容器也并不支持属性注入。写到基类里面通过构造注入ServiceProvider进行获取?这样每次都要传递ServiceProvider,也并不方便。嗯。。。有没有办法使用默认容器实现Controller的属性注入呢。本着有问题先搜一搜的态度,找到了一篇相关文章

  文中使用替换IControllerActivator,并将Controller手动添加到DI容器(可以直接使用AddControllersAsServices拓展方法)的方式实现了属性注入。也就是说必须使用从DI获取Controller的方式,才能使用这种属性注入的方式。不过之前看过一篇文章(找不到了。。。)里面的回复,大意为ASP.NET Core默认不使用DI获取Controller,是因为DI容器构建完成后就不能变更了,但是Controller是可能有动态加载的需求的。嗯。。。本着最大兼容性的考虑,看看有没有不干涉Controller创建方式也能实现这个功能的办法。

想了个逻辑

  首先确认目的:在不改变ControllerActivator功能的前提下,为其生成的Controller设置属性。具体的逻辑就有点绕了:我们需要实现一个设置Controller属性的IControllerActivator(一个静态代理类),并替换掉DI容器中的IControllerActivator声明,但仍然需要DI容器构建之前已声明的ControllerActivator(因为我们不知道具体如何构建),并且在我们实现的IControllerActivator中访问(作为被代理的对象)。

针对每个逻辑:

  • 实现一个设置Controller属性的IControllerActivator静态代理:这个很好写;
  • 用静态代理类替换掉DI容器中的IControllerActivator声明:这个也很简单;
  • DI容器保留之前已声明的ControllerActivator:这个可以直接将IControllerActivator声明中的实现类型作为服务添加到ServiceCollection中;
  • 在静态代理类中访问上一步添加到ServiceCollectionIControllerActivator具体实现:这个问题有点小麻烦。因为静态代理类和原始的IControllerActivator实现类都需要使用DI容器构建,那么静态代理类只能通过构造函数注入IControllerActivator实现类,但是我们没办法在写代码的时候就确定要注入的类型,也不能直接依赖IControllerActivator(自己依赖自己了)。想来想去。。。嗯。。。我们可以使用泛型实现代理类的逻辑,并在添加服务描述时动态生成一个泛型类型。

写代码

首先需要一个ASP.NET Core项目。

关键代码

嗯。。属性注入的关键点,只要有ServiceProvider,其它的就好说了。

  • 那就先写一个IServiceProviderSetter放在这里
using System;

public interface IServiceProviderSetter
{
void SetServiceProvider(IServiceProvider serviceProvider);
}
  • 实现泛型静态代理
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers; public class ControllerActivatorProxy<TControllerActivator> : IControllerActivator where TControllerActivator : IControllerActivator
{
private readonly TControllerActivator _originalControllerActivator; public ControllerActivatorProxy(TControllerActivator originalControllerActivator)
{
//引用原始ControllerActivator
_originalControllerActivator = originalControllerActivator;
} public object Create(ControllerContext context)
{
//使用原始ControllerActivator创建controller
var controller = _originalControllerActivator.Create(context); if (controller is IServiceProviderSetter serviceProviderSetter)
{
//具体要干什么由controller内部决定
serviceProviderSetter.SetServiceProvider(context.HttpContext.RequestServices);
} return controller;
} public void Release(ControllerContext context, object controller)
{
_originalControllerActivator.Release(context, controller);
}
}
  • 替换服务描述,保留之前已声明的ControllerActivator。为了方便,写个拓展方法吧。
using System.Linq;

using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; public static class AutowiredControllerServiceProviderExtensions
{
public static void AutowiredControllerServiceProvider(this IMvcBuilder mvcBuilder)
{
var services = mvcBuilder.Services; //查找原始服务描述
var serviceDescriptor = services.Where(m => m.ServiceType == typeof(IControllerActivator)).First(); var existedServiceType = serviceDescriptor.ImplementationType; //动态创建代理类型
var controllerActivatorType = typeof(ControllerActivatorProxy<>).MakeGenericType(existedServiceType); //将原始实现直接添加到services中
services.Add(ServiceDescriptor.Describe(existedServiceType, existedServiceType, serviceDescriptor.Lifetime)); //替换IControllerActivator服务为使用动态构建的代理类型
services.Replace(ServiceDescriptor.Describe(typeof(IControllerActivator), controllerActivatorType, serviceDescriptor.Lifetime));
}
}

嗯。。这好像有点有趣的地方。。

关键代码写完了,接下来是使用

  • 建立一个Controller基类并应用属性
using System;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; [ApiController]
[Route("api/[controller]")]
public class TestBaseController : ControllerBase, IServiceProviderSetter
{
//logger只是个示例,仅在使用时才创建,实际还可以直接获取,或者使用Lazy<T>等各种操作。其它属性同理。
private ILogger _logger;
private IServiceProvider _serviceProvider; protected ILogger Logger
{
get
{
if (_logger != null)
{
return _logger;
}
_logger = _serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger(GetType());
return _logger;
}
} [NonAction]
public void SetServiceProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
}
  • Startup中配置使用
public void ConfigureServices(IServiceCollection services)
{
//使用默认Controller生成方式
services.AddControllers().AutowiredControllerServiceProvider();
//使用DI生成Controller方式
//services.AddControllers().AddControllersAsServices().AutowiredControllerServiceProvider();
}
  • 示例Controller,这里就直接使用默认生成的Controller示范了
using System;
using System.Collections.Generic;
using System.Linq; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; public class WeatherForecastController : TestBaseController
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
}; [HttpGet]
public IEnumerable<WeatherForecast> Get()
{
Logger.LogInformation("Start");
var rng = new Random();
var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
Logger.LogInformation("End"); return result;
}
}

好了,可以看效果了

仅仅是个思路,仅供参考。代码

ASP.NET Core默认容器实现Controller的属性注入的更多相关文章

  1. ASP.NET Core 奇淫技巧之伪属性注入

    一.前言 开局先唠嗑一下,许久未曾更新博客,一直在调整自己的状态,去年是我的本命年,或许是应验了本命年的多灾多难,过得十分不顺,不论是生活上还是工作上.还好当我度过了所谓的本命年后,许多事情都在慢慢变 ...

  2. 聊聊ASP.NET Core默认提供的这个跨平台的服务器——KestrelServer

    跨平台是ASP.NET Core一个显著的特性,而KestrelServer是目前微软推出了唯一一个能够真正跨平台的Server.KestrelServer利用一个名为KestrelEngine的网络 ...

  3. ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客

    原文:ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客 版权声明:本文为starfd原创文章,转载请标明出处. https://blog.c ...

  4. 避免在ASP.NET Core 3.0中为启动类注入服务

    本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...

  5. [ASP.NET Core开发实战]基础篇02 依赖注入

    ASP.NET Core的底层机制之一是依赖注入(DI)设计模式,因此要好好掌握依赖注入的用法. 什么是依赖注入 我们看一下下面的例子: public class MyDependency { pub ...

  6. Asp.Net Core 进阶(三)—— IServiceCollection依赖注入容器和使用Autofac替换它

    Asp.Net Core 提供了默认的依赖注入容器 IServiceCollection,它是一个轻量级的依赖注入容器,所以功能不多,只是提供了基础的一些功能,要实现AOP就有点麻烦,因此在实际工作当 ...

  7. ASP.NET Core MVC 中的 [Controller] 和 [NonController]

    前言 我们知道,在 MVC 应用程序中,有一部分约定的内容.其中关于 Controller 的约定是这样的. 每个 Controller 类的名字以 Controller 结尾,并且放置在 Contr ...

  8. 在ASP.NET Core MVC中子类Controller拦截器要先于父类Controller拦截器执行

    我们知道在ASP.NET Core MVC中Controller上的Filter拦截器是有执行顺序的,那么如果我们在有继承关系的两个Controller类上,声明同一种类型的Filter拦截器,那么是 ...

  9. ASP.NET Core :容器注入(二):生命周期作用域与对象释放

    //瞬时生命周期 ServiceCollection services = new ServiceCollection(); services.AddTransient<TestServiceI ...

随机推荐

  1. dede 织梦的安装 出现dir

    安装dede系统.(如果您已经安装,请跳过本步.)直接运行:http://您的域名/install如果没有出现该页面,而是出现了一个空白页面写着dir 那么请删除install文件夹中的 instal ...

  2. 风炫安全web安全学习第三十三节课 文件包含漏洞基础以及利用伪协议进行攻击

    风炫安全web安全学习第三十三节课 文件包含漏洞基础以及利用伪协议进行攻击 文件包含漏洞 参考文章:https://chybeta.github.io/2017/10/08/php文件包含漏洞/ 分类 ...

  3. Spring 抽象的缓存包 spring-cache

    项目实战 用户信息查询接口中,使用@Cacheable注解  绑定相关信息接口中,使用@CachePut更新用户信息到缓存 更新用户信息接口中,使用@CacheEvict删除缓存信息 使用步骤 添加依 ...

  4. 十八般武艺玩转GaussDB(DWS)性能调优:SQL改写

    摘要:本文将系统介绍在GaussDB(DWS)系统中影响性能的坏味道SQL及SQL模式,帮助大家能够从原理层面尽快识别这些坏味道SQL,在调优过程中及时发现问题,进行整改. 数据库的应用中,充斥着坏味 ...

  5. Spring中的@Valid 和 @Validated注解你用对了吗

    1.概述 本文我们将重点介绍Spring中 @Valid和@Validated注解的区别 . 验证用户输入是否正确是我们应用程序中的常见功能.Spring提供了@Valid和@Validated两个注 ...

  6. Openstack Ocata 负载均衡安装(二)

    Openstack OCATA 负载节点(二) 安装haproxy: apt install haproxy 配置haproxy: vim /etc/haproxy/haproxy.cfg globa ...

  7. Python利用最优化算法求解投资内部收益率IRR【一】

    一. 内部收益率和净现值 内部收益率(Internal Rate of Return, IRR)其实要和净现值(Net Present Value, NPV)结合起来讲.净现值指的是某个投资项目给公司 ...

  8. SWPU2019

    一.题目打开介绍 这是题目本身打开的样子,继续进入题目 二.做题 简单的登陆界面和注册界面,没有sql注入已经尝试 申请发布广告 习惯性的测试 然后开始尝试注入,抓包, 两个都要,经过union注入判 ...

  9. 物理STANDBY库创建还原点(打开为read write后再变回主库)

    开启STANDBY库为READ WRITE 1.取消主库传送归档 SQL> alter system set log_archive_dest_state_2=defer; 2.取消备库应用日志 ...

  10. Nginx和Tomcat配置SSL实现https访问

    环境:CentOS 7 Nginx版本: nginx/1.18.0 1. 安装nginx 详细步骤可以参考如下官网:http://nginx.org/en/linux_packages.html#RH ...