实体框架核心错误处理

时间:2017-09-21 12:42:15

标签: entity-framework asp.net-core

我使用的是ASP.NET Core 2.0和EF Core 2.0。

我正在尝试捕获Web API中的错误以将它们记录到我的数据库中。当我这样做时,它似乎搞乱了datareader(不关闭)或db上下文。我是将db上下文注入控制器的依赖项。

public class ClientsController : Controller
{
    private VsNetTestContext moDbContext;

    public ClientsController(VsNetTestContext dbContext)
    {
        moDbContext = dbContext;
        if (GlobalExceptionHandler.DbContext == null)
        {
            GlobalExceptionHandler.DbContext = moDbContext;
        }
    }

GlobalExceptionHandler是一个用于跟踪后端异常的单例类。

以下是一个示例get:

// GET api/clients/5/ABC
[HttpGet("{nav}/{id}")]
public IActionResult Get(string nav, string id)
{
    try
    {
        var loAction = new SqlParameter("action", nav);
        var loClientID = new SqlParameter("client_id", (object)id ?? DBNull.Value);
        var loResult = moDbContext.client.FromSql("EXECUTE dbo.GetClientRecord @action, @client_id", loAction, loClientID).SingleOrDefault<client>();
        return new ObjectResult(loResult);
    }
    catch (Exception ex)
    {
        HttpContext.Response.StatusCode = Microsoft.AspNetCore.Http.StatusCodes.Status500InternalServerError;
        return new ObjectResult(GlobalExceptionHandler.Log(ex, "UpdateClientRecord"));
    }
}

我捕获错误并将响应状态设置为500,以便我可以在GlobalExceptionHandler的Log方法中记录数据库中的错误:

public static void Log()
{
    //Writes exception data to the database
    try
    {
        if (DbContext == null)
        {
            return;
        }
        var lcolParams = new List<SqlParameter>();
        lcolParams.Add(new SqlParameter("@err_title", mcErrTitle));
        lcolParams.Add(new SqlParameter("@err_message", mcErrMsg));
        lcolParams.Add(new SqlParameter("@err_description", mcErrDescription));
        lcolParams.Add(new SqlParameter("@app_name", "VsNetTest"));
        lcolParams.Add(new SqlParameter("@app_version", "1.0"));
        DbContext.Database.ExecuteSqlCommand("dbo.LogApplicationError @err_title, @err_message, @err_description, @app_name, @app_version", lcolParams.ToArray());
    }
    catch (Exception ex)
    {
        //ignore
        System.Diagnostics.Debug.WriteLine("Log Error: " + ex.Message);
    }
}

我通过向查询结果中不存在的客户端模型添加属性(另一个难点)强制Get中出现错误,但是在执行ExecuteSqlCommand时会抛出异常。以下是调试输出:

Application Insights Telemetry(unconfigured): { "name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-09-21T11:56:49.4156731Z","tags":{ "ai.application.ver":"1.0.0.0","ai.internal.sdkVersion":"aspnet5c:2.1.1","ai.internal.nodeName":"CTAPLAPTOP4","ai.operation.id":"a52ef379-4f366ebbd2a82b55","ai.operation.name":"GET Clients/Get [id/nav]","ai.cloud.roleInstance":"CTAPLAPTOP4","ai.operation.parentId":"|a52ef379-4f366ebbd2a82b55.","ai.location.ip":"127.0.0.1"},"data":{ "baseType":"MessageData","baseData":{ "ver":2,"message":"Executed DbCommand (59ms) [Parameters=[action='?' (Size = 1), client_id='?'], CommandType='Text', CommandTimeout='30']\r\nEXECUTE dbo.GetClientRecord @action, @client_id","severityLevel":"Information","properties":{ "DeveloperMode":"true","CategoryName":"Microsoft.EntityFrameworkCore.Database.Command","{OriginalFormat}":"Executed DbCommand ({elapsed}ms) [Parameters=[{parameters}], CommandType='{commandType}', CommandTimeout='{commandTimeout}']{newLine}{commandText}","commandType":"Text","elapsed":"59","commandText":"EXECUTE dbo.GetClientRecord @action, @client_id","commandTimeout":"30","parameters":"action='?' (Size = 1), client_id='?'","AspNetCoreEnvironment":"Development"} } } }
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand(59ms) [Parameters=[action='?' (Size = 1), client_id='?'], CommandType='Text', CommandTimeout='30']
EXECUTE dbo.GetClientRecord @action, @client_id
Application Insights Telemetry (unconfigured): { "name":"Microsoft.ApplicationInsights.Dev.Exception","time":"2017-09-21T11:56:49.4890679Z","tags":{ "ai.application.ver":"1.0.0.0","ai.internal.sdkVersion":"aspnet5c:2.1.1","ai.internal.nodeName":"CTAPLAPTOP4","ai.operation.id":"a52ef379-4f366ebbd2a82b55","ai.operation.name":"GET Clients/Get [id/nav]","ai.cloud.roleInstance":"CTAPLAPTOP4","ai.operation.parentId":"|a52ef379-4f366ebbd2a82b55.","ai.location.ip":"127.0.0.1"},"data":{ "baseType":"ExceptionData","baseData":{ "ver":2,"properties":{ "DeveloperMode":"true","CategoryName":"Microsoft.EntityFrameworkCore.Query","error":"System.InvalidOperationException: The required column 'extra_field' was not present in the results of a 'FromSql' operation.\r\n   at Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__11_0(FactoryAndReader s)\r\n   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(Boolean buffer)\r\n   at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()\r\n   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)\r\n   at lambda_method(Closure , QueryContext )\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass17_0`1.<CompileQueryCore>b__0(QueryContext qc)","{OriginalFormat}":"An exception occurred in the database while iterating the results of a query for context type '{contextType}'.{newline}{error}","Exception":"System.InvalidOperationException: The required column 'extra_field' was not present in the results of a 'FromSql' operation.\r\n   at Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__11_0(FactoryAndReader s)\r\n   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(Boolean buffer)\r\n   at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()\r\n   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)\r\n   at lambda_method(Closure , QueryContext )\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass17_0`1.<CompileQueryCore>b__0(QueryContext qc)","AspNetCoreEnvironment":"Development","contextType":"WebApiAureliaCli.Models.VsNetTestContext"},"exceptions":[{"id":23492147,"typeName":"System.InvalidOperationException","message":"An exception occurred in the database while iterating the results of a query for context type 'WebApiAureliaCli.Models.VsNetTestContext'.\r\nSystem.InvalidOperationException: The required column 'extra_field' was not present in the results of a 'FromSql' operation.\r\n   at Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__11_0(FactoryAndReader s)\r\n   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(Boolean buffer)\r\n   at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()\r\n   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)\r\n   at lambda_method(Closure , QueryContext )\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass17_0`1.<CompileQueryCore>b__0(QueryContext qc)","hasFullStack":true,"parsedStack":[{"level":0,"method":"Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory","assembly":"Microsoft.EntityFrameworkCore.Relational, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"},{"level":1,"method":"Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__11_0","assembly":"Microsoft.EntityFrameworkCore.Relational, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"},{"level":2,"method":"Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized","assembly":"Microsoft.EntityFrameworkCore, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"},{"level":3,"method":"Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated","assembly":"Microsoft.EntityFrameworkCore.Relational, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"},{"level":4,"method":"Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1+Enumerator.BufferlessMoveNext","assembly":"Microsoft.EntityFrameworkCore.Relational, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"},{"level":5,"method":"Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.Execute","assembly":"Microsoft.EntityFrameworkCore.SqlServer, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"},{"level":6,"method":"Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1+Enumerator.MoveNext","assembly":"Microsoft.EntityFrameworkCore.Relational, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"},{"level":7,"method":"System.Linq.Enumerable.SingleOrDefault","assembly":"System.Linq, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"},{"level":8,"method":"lambda_method","assembly":"Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"},{"level":9,"method":"Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<>c__DisplayClass17_0`1.<CompileQueryCore>b__0","assembly":"Microsoft.EntityFrameworkCore, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"}]}],"severityLevel":"Error"}}}
Microsoft.EntityFrameworkCore.Query:Error: An exception occurred in the database while iterating the results of a query for context type 'WebApiAureliaCli.Models.VsNetTestContext'.
System.InvalidOperationException: The required column 'extra_field' was not present in the results of a 'FromSql' operation.
   at Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__11_0(FactoryAndReader s)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam, TValue](TValue& target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(Boolean buffer)
   at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.Execute[TState, TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
   at lambda_method(Closure , QueryContext )
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass17_0`1.<CompileQueryCore>b__0(QueryContext qc)

System.InvalidOperationException: The required column 'extra_field' was not present in the results of a 'FromSql' operation.
   at Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__11_0(FactoryAndReader s)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam, TValue](TValue& target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(Boolean buffer)
   at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.Execute[TState, TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
   at lambda_method(Closure , QueryContext )
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass17_0`1.<CompileQueryCore>b__0(QueryContext qc)
Exception thrown: 'System.InvalidOperationException' in Microsoft.EntityFrameworkCore.dll
Exception thrown: 'System.ObjectDisposedException' in Microsoft.EntityFrameworkCore.dll
Log Error: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'VsNetTestContext'.
Application Insights Telemetry(unconfigured): { "name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-09-21T11:56:49.6463034Z","tags":{ "ai.application.ver":"1.0.0.0","ai.internal.sdkVersion":"aspnet5c:2.1.1","ai.internal.nodeName":"CTAPLAPTOP4","ai.operation.id":"a52ef379-4f366ebbd2a82b55","ai.operation.name":"GET Clients/Get [id/nav]","ai.cloud.roleInstance":"CTAPLAPTOP4","ai.operation.parentId":"|a52ef379-4f366ebbd2a82b55.","ai.location.ip":"127.0.0.1"},"data":{ "baseType":"MessageData","baseData":{ "ver":2,"message":"Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext.","severityLevel":"Information","properties":{ "DeveloperMode":"true","CategoryName":"Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor","{OriginalFormat}":"Executing ObjectResult, writing value {Value}.","AspNetCoreEnvironment":"Development","Value":"Microsoft.AspNetCore.Mvc.ControllerContext"} } } }
Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor:Information: Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext.
Application Insights Telemetry (unconfigured): { "name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-09-21T11:56:49.6559626Z","tags":{ "ai.application.ver":"1.0.0.0","ai.internal.sdkVersion":"aspnet5c:2.1.1","ai.internal.nodeName":"CTAPLAPTOP4","ai.operation.id":"a52ef379-4f366ebbd2a82b55","ai.operation.name":"GET Clients/Get [id/nav]","ai.cloud.roleInstance":"CTAPLAPTOP4","ai.operation.parentId":"|a52ef379-4f366ebbd2a82b55.","ai.location.ip":"127.0.0.1"},"data":{ "baseType":"MessageData","baseData":{ "ver":2,"message":"Executed action WebApiAureliaCli.Controllers.ClientsController.Get (WebApiAureliaCliWebpack) in 375.9021ms","severityLevel":"Information","properties":{ "DeveloperMode":"true","CategoryName":"Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker","ElapsedMilliseconds":"375.9021","{OriginalFormat}":"Executed action {ActionName} in {ElapsedMilliseconds}ms","ActionName":"WebApiAureliaCli.Controllers.ClientsController.Get (WebApiAureliaCliWebpack)","AspNetCoreEnvironment":"Development"} } } }
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action WebApiAureliaCli.Controllers.ClientsController.Get(WebApiAureliaCliWebpack) in 375.9021ms
Application Insights Telemetry(unconfigured): { "name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-09-21T11:56:49.6666798Z","tags":{ "ai.application.ver":"1.0.0.0","ai.internal.sdkVersion":"aspnet5c:2.1.1","ai.internal.nodeName":"CTAPLAPTOP4","ai.operation.id":"a52ef379-4f366ebbd2a82b55","ai.operation.name":"GET Clients/Get [id/nav]","ai.cloud.roleInstance":"CTAPLAPTOP4","ai.operation.parentId":"|a52ef379-4f366ebbd2a82b55.","ai.location.ip":"127.0.0.1"},"data":{ "baseType":"MessageData","baseData":{ "ver":2,"message":"Request finished in 403.6099ms 500 application/json; charset=utf-8","severityLevel":"Information","properties":{ "DeveloperMode":"true","CategoryName":"Microsoft.AspNetCore.Hosting.Internal.WebHost","ElapsedMilliseconds":"403.6099","ContentType":"application/json; charset=utf-8","AspNetCoreEnvironment":"Development","StatusCode":"500"} } } }
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 403.6099ms 500 application/json; charset=utf-8
Application Insights Telemetry(unconfigured): { "name":"Microsoft.ApplicationInsights.Dev.Request","time":"2017-09-21T11:56:49.2629283Z","tags":{ "ai.internal.sdkVersion":"aspnet5c:2.1.1","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"CTAPLAPTOP4","ai.operation.id":"a52ef379-4f366ebbd2a82b55","ai.operation.name":"GET Clients/Get [id/nav]","ai.cloud.roleInstance":"CTAPLAPTOP4","ai.location.ip":"127.0.0.1"},"data":{ "baseType":"RequestData","baseData":{ "ver":2,"id":"|a52ef379-4f366ebbd2a82b55.","name":"GET Clients/Get [id/nav]","duration":"00:00:00.4130153","success":false,"responseCode":"500","url":"http://localhost:5813/api/clients/1/","properties":{ "DeveloperMode":"true","httpMethod":"GET","AspNetCoreEnvironment":"Development"} } } }

显然,我并没有以任何方式处理上下文。为什么我在上下文中收到被处置对象错误?这似乎是捕获和跟踪异常的合理方法。

编辑:我应该提一下,如果我删除了异常处理,那么500响应中没有任何关于发生的异常的内容。所以,无法找出服务器或客户端出了什么问题。

2 个答案:

答案 0 :(得分:1)

“DbContext”的默认生存期是“范围”,表示生命周期是请求的持续时间。由于您仅在未设置单例的属性时才设置它,因此它会保留DbContext,从第一个请求到设置属性的控制器,永远不会获得新属性。在第一个请求完成后,将丢弃此DbContext实例。

我的建议是不要使用单例异常处理程序;使用带有作用域生存期的DI容器注册异常处理程序(这将允许它通过DI获取DbContext实例)。

答案 1 :(得分:0)

我能够通过以旧方式打开新连接来解决问题。这使我能够将原始异常捕获到Web API控制器中并将其记录到数据库中:

public static void Log()
{
    //Writes exception data to the database
    try
    {
          using (var cn = new SqlConnection(DbConnectionString))
          {
                cn.Open();
                var cmd = new SqlCommand();
                cmd.CommandText = "dbo.LogApplicationError";
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.Connection = cn;
                cmd.Parameters.AddWithValue("@err_title", mcErrTitle);
                cmd.Parameters.AddWithValue("@err_message", mcErrMsg);
                cmd.Parameters.AddWithValue("@err_description", mcErrDescription);
                cmd.Parameters.AddWithValue("@app_name", "OIO");
                cmd.Parameters.AddWithValue("@app_version", "1.0");
                cmd.Parameters.AddWithValue("@user_name", "");
                cmd.ExecuteNonQuery();
          }
    }
    catch (Exception ex)
    {
          //ignore
          System.Diagnostics.Debug.WriteLine("Log Exception: " + ex.Message);
    }
}