如何将全局捕获的异常保存到数据库中

时间:2018-12-20 12:30:06

标签: c# asp.net-core

我正在构建一个Web应用程序,其中将有很多控制器及其相应的操作方法。

我想将每个异常保存在数据库中,因此我创建了 ExceptionService(在其中注入了DbContext)。

让我们说这是我的控制器的一般形式:

[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
    private readonly UserManager userManager;
    private readonly IExceptionService exceptionService;

    public UserController(UserManager userManager, IExceptionService exceptionService)
    {
        this.userManager = userManager;
        this.exceptionService = exceptionService;
    }

    [HttpPost]
    public async Task<IActionResult> Post([FromBody] User user)
    {

        try
        {
            //some code
        }
        catch (Exception e)
        {
            exceptionService.Save(e);
            //some code
        }
    }
}

为了避免那么多try-catch块,我决定创建一个看起来像这样的过滤器:

public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
    private readonly IExceptionService exceptionService;

    public ApiExceptionFilterAttribute(IExceptionService exceptionService)
    {
        this.exceptionService = exceptionService;
    }

    public override void OnException(ExceptionContext context)
    {
        Exception e = context.Exception;
        exceptionService.Save(e);

        //some code
    }
}

StartUp.cs中ConfigureServices方法中的代码如下(为简单起见删除了一些代码):

services
    .AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
    .AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

services
   .AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default")));

services.AddScoped<UserManager>();
services.AddScoped<SignInManager>();


services.AddScoped<IExceptionService, ExceptionService>();


services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();

ConfgureMvcOptions类如下:

public class ConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
    private readonly IExceptionService exceptionService;

    public ConfigureMvcOptions(IExceptionService exceptionService)
    {
        this.exceptionService = exceptionService;
    }

    public void Configure(MvcOptions options)
    {
        options.Filters.Add(new ApiExceptionFilterAttribute(exceptionService));
    }
}

运行此应用程序时,出现以下错误:

System.InvalidOperationException: 'Cannot consume scoped service 'SmartWay.Services.IExceptionService' from singleton 'Microsoft.Extensions.Options.IConfigureOptions`1[Microsoft.AspNetCore.Mvc.MvcOptions]'.'

如果我将IExceptionServcise的生存期更改为瞬态,那么我必须这样做 Dbcontext,然后是DbContextOptions ...看来这不是正确的方法。

那么,我该如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

要从单例服务解析作用域服务,请尝试_serviceProvider.CreateScope

请按照以下步骤操作:

  1. ExceptionService

    public interface IExceptionService
    {
        void Save(Exception ex);
    }
    
    public class ExceptionService : IExceptionService
    {
        private readonly IServiceProvider _serviceProvider;
        public ExceptionService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        public void Save(Exception ex)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var _context = scope.ServiceProvider.GetRequiredService<MVCProContext>();
                _context.Add(new Book() { Title = ex.Message });
                _context.SaveChanges();
            }
        }
    }
    
  2. Startup.cs

    services.AddSingleton<IExceptionService, ExceptionService>();
    services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();