.NET 手动获取注入对象

打印 上一主题 下一主题

主题 922|帖子 922|积分 2766

前言

当我们使用DI方式写了很多的Service后, 可能会发现我们的有些做法并不是最优的.
获取注入的对象, 大家经常在构造函数中获取, 这样也是官方推荐的方式, 但有时不是效率最高的方法.
如果在构造函数中获取对象,那么每次对象的初始化都会把构造函数中的对象初始化一遍, 如果某个方法只用到其中一个注入对象, 那么其他的注入对象就白注入了
注入方法:

分别为构造函数、方法特性(FromServices)使用注入, 还有今天的主角 手动获取服务注入
构造函数注入
  1. /// <summary>
  2. /// xxx服务
  3. /// </summary>
  4. [Route("api/[controller]")]
  5. [ApiController]
  6. public class ValueController : ControllerBase
  7. {
  8.     private readonly ILogger<ValueController> _logger;
  9.     private readonly IFreeSql _freeSql;
  10.     private readonly IOptions<ValueOptions> _valueOptions;
  11.     private readonly RoadService _roadService;
  12.     /// <summary>
  13.     /// 构造函数
  14.     /// </summary>
  15.     /// <returns></returns>
  16.     public RollerController(ILogger<ValueController> logger, IFreeSql freeSql, IOptions<ValueOptions> valueOptions, RoadService roadService)
  17.     {
  18.         _logger = logger;
  19.         _freeSql = freeSql;
  20.         _baiduOptions = baiduOptions;
  21.         _roadService = roadService;
  22.     }
  23.     /// <summary>
  24.     /// 获取所有项目
  25.     /// </summary>
  26.     /// <returns></returns>
  27.     [HttpGet("project/all")]
  28.     public Task<List<ProjectDto>> GetProjects() => _freeSql.Select<Project>().ToListAsync<ProjectDto>();
  29.     /// <summary>
  30.     /// 获取工程名下的道路
  31.     /// </summary>
  32.     /// <param name="projectId"></param>
  33.     /// <returns></returns>
  34.     [HttpGet("project/{projectId}/road")]
  35.     public Task<List<RoadDto>> GetRoads([FromRoute] Guid projectId) => _roadService.GetRoads(projectId);
  36. }
复制代码
方法特性(FromServices)注入
  1.     /// <summary>
  2.     /// 上传数据
  3.     /// </summary>
  4.     /// <param name="gridDataService"></param>
  5.     /// <param name="dataList"></param>
  6.     /// <param name="roadId"></param>
  7.     /// <returns></returns>
  8.     [HttpPost("road/upload-grid-data/{roadId}")]
  9.     public async Task<IActionResult> UpdateRoadGridData([FromServices] GridDataService gridDataService, [FromBody] List<BizRoadGridInput> dataList, Guid roadId)
  10.     {
  11.         var data = await gridDataService.UploadGridData(dataList, roadId);
  12.         if (!data.ok) return BadRequest(data.message);
  13.         return Ok();
  14.     }
复制代码
如上: 每个方法并不是都用到了构造函数中的服务, 所以我们这里就有性能损失, 毕竟创建对象也是有代价的, 而且还会伴有GC.
手动获取服务注入

在不是Controller中就不能使用[FromServices]特性了
为了能在个别的方法中注入对象就要用到手动获取注册对象的方式
如下:
在能直接拿到HttpContext时
  1.     /// <summary>
  2.     /// 处理道路信息
  3.     /// </summary>
  4.     /// <param name="roadId"></param>
  5.     /// <exception cref="ArgumentNullException"></exception>
  6.     [HttpPost("road/{roadid}/handle-road")]
  7.     public async Task<ActionResult> HandleRoadInfo([FromRoute] Guid roadId)
  8.     {
  9.         var imageService = HttpContext.GetRequiredService.GetRequiredService<ImageService>();
  10.         await imageService.HandleRoadInfo(roadId);
  11.         return Ok();
  12.     }
复制代码
在不能直接拿到HttpContext时,需要先获取IHttpContextAccessor, 然后再获取 HttpContext上下文对象
  1. /// <summary>
  2. /// 图片服务
  3. /// </summary>
  4. public class ImageService
  5. {
  6.     private readonly IHttpContextAccessor _httpContextAccessor;
  7.     /// <summary>
  8.     /// 构造函数
  9.     /// </summary>
  10.     public ImageService(IHttpContextAccessor httpContextAccessor)
  11.     {
  12.         _httpContextAccessor = httpContextAccessor;
  13.     }
  14.     /// <summary>
  15.     /// 处理道路信息
  16.     /// </summary>
  17.     /// <param name="roadId"></param>
  18.     /// <exception cref="ArgumentNullException"></exception>
  19.     public async Task HandleRoadInfo(Guid roadId)
  20.     {
  21.         var roadService = _httpContextAccessor.HttpContext?.RequestServices.GetRequiredService<RoadService>();
  22.         var data = await roadService.GetRoadData(roadId);
  23.         // 省略其他
  24.     }
  25. }
复制代码
注意这里面我们使用了GetRequiredService, 而没有使用GetService, 因为使用GetRequiredService不需要我们自己再去做空值检查, 如果为空再很快就会失败.
关于GetService 和 GetRequiredService

GetService()是IServiceProvider上的唯一方法,ISeviceProvider是ASP.NET核心DI抽象中的中央接口。第三方容器还可以实现可选接口ISupportRequiredService,该接口提供GetRequiredService()方法。当请求的类型serviceType可用时,这些方法的行为相同。如果服务不可用(即它没有注册),则GetService()返回null,而GetRequiredService()抛出一个InvalidOperationException。
GetRequiredService()相对于GetService()的主要好处是当服务不可用时,它允许第三方容器提供额外的诊断信息。因此,在使用第三方容器时最好使用GetRequiredService()。就个人而言,我会在任何地方使用它,即使我只使用内置的DI容器。
总结

将所有方法都用的service中使用构造函数注入是个优选方案, 将个别方法使用到的service使用手动获取的方式代码执行会更有效率
作者:wwmin
出处:https://www.cnblogs.com/cnwwm
联系:wwei.min@163.com 微信:w_wmin
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表