如何捕获EF异常

4
我希望能捕获Entity Framework中的错误。因此,以下是我的代码:
 return await _context.SaveChangesAsync(cancellationToken);

如果上述代码出现问题,我希望能够将其捕获并记录为数据库中的警告,但目前它会自动保存为错误,而实际上应该是警告。

以下是我得到的错误:

在保存上下文类型“ValuationsReporting.Models.ValuationsReportingContext”的更改时,数据库中发生了异常。 System.InvalidOperationException: 在实体类型“ValuationFundTemplate”上,“TemplateTypeId”属性具有临时值。请显式设置一个永久值,或确保数据库配置为为此属性生成值。 at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.Validate(ModificationCommand modificationCommand) at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IReadOnlyList`1 entries)+MoveNext() at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(DbContext _, ValueTuple`2 parameters, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList`1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)

System.InvalidOperationException

Microsoft.EntityFrameworkCore.Update

我尝试使用try catch语句,但一旦运行到那一行,错误就会在数据库中记录。

实际代码:

try
                {
                    foreach (var template in snapshotDto.SnapshopFundTemplateDtos)
                    {
                        if (template.FundId != null)
                        {
                            foreach (var fundId in template.FundId)
                            {
                                var tempTemplate = allFundTemplates.Where(x => x.ValuationId == valuation.Id && x.FundId == fundId && x.TemplateTypeId == template.TemplateTypeId).FirstOrDefault();

                                //var tempTemplate = await _valuationFundTemplateRepository.GetOne(valuation.Id, fundId, template.TemplateTypeId, true, cancellationToken);
                                if (tempTemplate == null)
                                {
                                    tempTemplate = new ValuationFundTemplate();
                                    tempTemplate.CreatedBy = _userRepository.claimsPrincipal.Identity.Name;
                                    tempTemplate.CreatedOn = DateTime.Now.ToUniversalTime();
                                    isTemplateUpdate = false;
                                }
                                //tempTemplate.IsDeleted = false;
                                //if (template.IsDeleted)
                                //{
                                //     _valuationFundTemplateRepository.Delete(tempTemplate);
                                //}
                                //else
                                //{
                                //tempTemplate.IsDeleted = template.IsDeleted;

                                tempTemplate.IsDeleted = false;
                                tempTemplate.IsDefaultFundTemplate = template.IsDefault;
                                tempTemplate.FundId = fundId;
                                tempTemplate.ValuationId = valuation.Id;
                                tempTemplate.TemplateTypeId = 0;
                                tempTemplate.TemplateId = template.TemplateId;
                                tempTemplate.ModifiedBy = _userRepository.claimsPrincipal.Identity.Name;
                                tempTemplate.ModifiedOn = DateTime.Now.ToUniversalTime();
                                tempTemplates.Add(tempTemplate);
                                if (isTemplateUpdate)
                                {
                                    _valuationFundTemplateRepository.Update(tempTemplate);
                                }
                                else
                                {
                                    await _valuationFundTemplateRepository.Insert(tempTemplate, cancellationToken);
                                }
                                //  }

                                await _valuationFundTemplateRepository.SaveAsync(cancellationToken);//here is where the error occurs which i dont want to capture in database.
                                if (!isTemplateUpdate)
                                    valuation.ValuationFundTemplate.Add(tempTemplate);
                            }

                        }
                    }
                }catch(Exception e)
                {
                    var z = e.Message;
                }



public virtual async Task<int> SaveAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            return await _context.SaveChangesAsync(cancellationToken);
        }

1
@PrashantPimpale,我已经尝试使用try catch了,但错误仍然被记录在数据库中。 - taashibhulani
无法理解 - “错误已记录在数据库中”?你能解释一下吗? - Prashant Pimpale
@PrashantPimpale 我尝试了以下代码:try { return await _context.SaveChangesAsync(cancellationToken); } catch(Exception e) { var z1 = e.Message; return 0; }但是在进入 catch 块之前,它仍然被记录为数据库中的错误。 - taashibhulani
2个回答

2

我同意@PrashantPimpale的看法,你可以简单地使用try catch

但对于高级方法,我建议你可以使用全局错误处理中间件。 通过这种方式,你可以捕获整个dotnet API/应用程序中发生的任何错误。

以下是详细说明:

// Extension method used to add the middleware to the HTTP request pipeline.
    public static class ErrorHandlingMiddlewareExtensions
    {
        public static IApplicationBuilder UseErrorHandlingMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ErrorHandlingMiddleware>();
        }
    }

然后在启动类的Configure()方法中添加app.UseErrorHandlingMiddleware();

您需要首先创建一个ErrorHandlingMiddleware类。

 public class ErrorHandlingMiddleware
    {
        private readonly RequestDelegate _next;

        public ErrorHandlingMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            try
            {
                await _next(httpContext);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(httpContext, ex);
            }
        }

        private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
          // Implement how you want to handle the error.
        }
}

错误日志已经在数据库中记录。但是我不想将一个特定的错误记录在数据库中。 - taashibhulani
请问您能否分享您的代码?这将非常有帮助。 - Mohan Rajput
我知道这个错误是如何产生的。实际上,我也尝试在save async方法中加入try catch,但仍然会在此之前将错误记录在数据库中。 - taashibhulani
你尝试过将Update方法改为异步的吗? - Mohan Rajput
1
是的,我做到了。基本上我想要做的是,我想关闭所有来自实体框架的错误日志记录。 - taashibhulani
显示剩余5条评论

0

继续我的评论,我已经找到了解决方案。

这里有两件事情不是立即清楚的,所以需要澄清:

  1. 有合法的理由想要抑制一些数据库错误并将它们视为警告(例如)例如自定义触发器检查数据完整性

  2. 这与捕获来自EF Core的异常无关,EF Core直接发出DbUpdateExceptions的错误日志行,并将该异常抛出以供应用程序捕获

因此,这里的解决方案是配置EF Core以更改其行为并将保存更改的失败记录为警告而不是错误(可以完全抑制-但我正在安全地玩)。 我没有找到实际的文档,但通过一些实验,我发现像这样配置您的上下文可以正常工作:

protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
    base.OnConfiguring(builder);

    builder
        .ConfigureWarnings(x => x
            .Log(
                (CoreEventId.SaveChangesFailed, LogLevel.Warning)
            )
        );
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接