【问题标题】:Decorate BaseController with Scrutor in .netCore 3.1在 .net Core 3.1 中使用 Scrutor 装饰基本控制器
【发布时间】:2020-10-25 23:03:24
【问题描述】:

我在 .net core 3.1 中有一个带有角前端的应用程序。我想将装饰器用于基本控制器,以便在整个应用程序中记录 CUD 操作。我在项目中使用 Scrutor nuget 包。

基本控制器如下

    using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Xenagos.Data;
using Xenagos.Data.EFCore;
using Xenagos.ViewModels;

namespace Xenagos.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public abstract class BaseController<TEntity, TViewEntity, TRepository> : ControllerBase, IBaseController<TEntity, TViewEntity> 
        where TEntity : class
        where TViewEntity : class, IViewEntity
        where TRepository : IRepository<TEntity>
    {
        private readonly IRepository<TEntity> repository;
        private readonly IMapper mapper;

        public BaseController(TRepository repository, IMapper mapper)
        {
            this.repository = repository;
            this.mapper = mapper;
        }


        // GET: api/[controller]
        [HttpGet]
        public virtual async Task<ActionResult<ComplexData<TViewEntity>>> Get()
        {
            var results = await repository.GetAll();
            List<TViewEntity> resultsView =
                this.mapper.Map<List<TEntity>, List<TViewEntity>>(results);
            return Ok(new ComplexData<TViewEntity>(resultsView));
        }

        // GET: api/[controller]/5
        [HttpGet("{id}")]
        public async Task<ActionResult<TEntity>> Get(int id)
        {
            var entity = await repository.Get(id);
            if (entity == null)
            {
                return NotFound();
            }

            return entity;
        }

        // PUT: api/[controller]/5
        [HttpPut("{id}")]
        public virtual async Task<IActionResult> Put(string id, TViewEntity entity)
        {
            if (!id.Equals(entity.Id))
            {
                return BadRequest();
            }
            await repository.Update(this.mapper.Map<TEntity>(entity));
            return NoContent();
        }

        // POST: api/[controller]
        [HttpPost]
        public virtual async Task<ActionResult<TEntity>> Post(TViewEntity entity)
        {
            await repository.Add(this.mapper.Map<TEntity>(entity));
            return CreatedAtAction("Get", new { id = entity.Id }, entity);
        }

        // DELETE: api/[controller]/5
        [HttpDelete("{id}")]
        public async Task<ActionResult<TViewEntity>> Delete(int id)
        {
            var entity = await repository.Delete(id);
            if (entity == null)
            {
                return NotFound();
            }
            return this.mapper.Map<TViewEntity>(entity);
        }
    }
}

我做的装饰器如下

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xenagos.Controllers;
using Xenagos.ViewModels;

namespace Xenagos.Data
{
    public class LoggingDecorator<T, TViewEntity, TRepository> : IBaseController<T, TViewEntity>
        where T : class
        where TViewEntity : class, IViewEntity
        where TRepository : IRepository<T>
    {

        private IBaseController<T, TViewEntity> _baseController;
        private readonly ILogger<LoggingDecorator<T, TViewEntity, TRepository>> _logger;

        public LoggingDecorator(IBaseController<T, TViewEntity> baseController, ILogger<LoggingDecorator<T, TViewEntity, TRepository>> logger)
        {
            _baseController = baseController;
            _logger = logger;
        }

        Task<ActionResult<TViewEntity>> IBaseController<T, TViewEntity>.Delete(int id)
        {
            _logger.LogWarning($"Deleting record from ... with ID:{id}");
            Task<ActionResult<TViewEntity>> result = _baseController.Delete(id);

            return result;
        }

        public Task<ActionResult<ComplexData<TViewEntity>>> Get()
        {
            return _baseController.Get();
        }

        Task<ActionResult<T>> IBaseController<T, TViewEntity>.Get(int id)
        {
            return _baseController.Get(id);
        }

        public Task<ActionResult<T>> Post(TViewEntity entity)
        {
            _logger.LogWarning($"Adding new record from ... with object data :{JsonConvert.SerializeObject(entity)}");
            return _baseController.Post(entity);
        }

        public Task<IActionResult> Put(string id, TViewEntity entity)
        {
            _logger.LogWarning($"updating record from ... with object data :{JsonConvert.SerializeObject(entity)}");
            Task<IActionResult> result = _baseController.Put(id, entity);

            return result;
        }
    }
}

public void ConfigureServices(IServiceCollection) 的启动类中,我使用以下几行

services.AddScoped<IBaseController<Models.Property, PropertyViewModel>, BaseController<Models.Property, PropertyViewModel, PropertyRepository>>();
            services.Decorate<IBaseController<Models.Property, PropertyViewModel>, LoggingDecorator<Models.Property, PropertyViewModel, PropertyRepository>>();

我已经从基本控制器中提取了一个接口,在所有先前的操作之上。 当应用程序运行时,它不会调用/传递装饰器。我在这里缺少什么? 我之前没有将装饰器模式与 .net 核心和依赖注入一起使用。所有添加的代码都在后端,我根本没有改变前端。

提前谢谢你。

【问题讨论】:

  • 你能把你的非抽象控制器的注册包括进来吗?
  • 问题是是否有可能用控制器做到这一点。这是您用[ApiController] 装饰的BaseController,asp.net 使用它来检测项目中的控制器。因此,当请求进来时,将始终是具有该属性的类而不是您的LoggingDecorator。您无需注册控制器即可使其工作。我认为您可以将 structor 用于除控制器之外的任何其他内容。一个更安全的选择可能是使用操作过滤器。
  • @devNull 我也从非抽象控制器中提取了一个接口,并将其添加为作用域。到目前为止它没有帮助。我还应该做其他事情吗?
  • @Michael,所以,如果我理解,我应该尝试装饰从执行操作(例如更新)的控制器调用它的类?
  • @Tony,如果你想坚持structor,那么可以。但我认为还有其他解决方案。

标签: c# asp.net-core dependency-injection decorator scrutor


【解决方案1】:

使用ActionFilter 记录控制器操作的进入/退出:

public class LoggingActionFilter : IActionFilter
{
    ILogger _logger;
    public LoggingActionFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<LoggingActionFilter>();
    }
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // do something before the action executes
        _logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' executing");
    }
    public void OnActionExecuted(ActionExecutedContext context)
    {
        // do something after the action executes
        _logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' executed");
    }
}

启动

services.AddMvc()
    .AddMvcOptions(options =>
    {
        options.Filters.Add<LoggingActionFilter>();
    });

您可能还想为异步操作实现 IAsyncActionFilter

要了解有关操作过滤器的更多信息,请查看here

您还可以添加异常过滤器来记录所有异常。

【讨论】:

  • 感谢您的回复,我会尝试实施并让您知道结果
  • 这真的很有帮助!感谢您的帮助。但是有一个问题,在您发布的 sn-p 上,您将一个新的 LoggingActionFilter 对象添加到 MVC 过滤器。在第一个 sn-p 上,您放置了一个构造函数参数。添加时如何将 ILoggerFacotry 传递给 MVC 过滤器上的对象实例化?
  • 啊,是的,我的错。您应该使用通用版本,asp.net 将使用 serviceProvider。编辑了我的答案。
猜你喜欢
  • 2020-08-02
  • 1970-01-01
  • 2020-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 2020-06-05
相关资源
最近更新 更多