【问题标题】:如何在数据库中保存全局捕获的异常
【发布时间】:2019-05-20 23:30:18
【问题描述】:

我正在构建一个 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... 好像不对..

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

【问题讨论】:

  • 你实际上是在重塑 Elmah,只是为了让你知道
  • 连接数据库失败出现异常怎么办?
  • @mjwills 我想我会抓住它并且什么都不做,或者我会将它保存在文本文件中
  • @AlexanderMujirishvili 您正在混合横切关注点IMO。重新考虑当前的设计选择。
  • 可能把所有异常都保存在文件中会更好,你觉得呢?

标签: c# asp.net-core


【解决方案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>();
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-19
    相关资源
    最近更新 更多