在 .net Core 3.1 中用 Scrutor 装饰 Base Controller

Decorate BaseController with Scrutor in .netCore 3.1

我在 .net core 3.1 中有一个带有 angular 前端的应用程序。我想将装饰器用于基本控制器,以便在整个应用程序中记录 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;
        }
    }
}

在启动class中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>>();

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

提前致谢。

使用 ActionFilter 记录 enter/exit 控制器操作:

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

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