【问题标题】:Getting SqlException and ObjectDisposedException in Asp.net core 5 webapi在 Asp.net core 5 webapi 中获取 SqlException 和 ObjectDisposedException
【发布时间】:2021-05-09 14:20:56
【问题描述】:

我有一个专辑表(专辑实体)存储库,如您所见,我在存储库中进行过滤和分页:

public IQueryable<Album> GetAllAlbums(AlbumQueryParameters queryParameters)
    {
        var albums = _cache.Get<IQueryable<Album>>("_AllAlbums");

       if(albums == null){
           albums = _context.Albums;
           _cache.Set<IQueryable<Album>>("_AllAlbums", albums, ApplicationDefaults.MemoryCachingOption);
        }

        if(queryParameters.ArtistId != null)
        {
            albums = albums.Include(a => a.ArtistToAlbums)
            .SelectMany(a => a.ArtistToAlbums)
            .Where(a => a.ArtistId == Convert.ToInt32(queryParameters.ArtistId))
            .Select(a => a.Album);
        }

        if(queryParameters.GenreId != null)
        {
            albums = albums.Include(a => a.AlbumToGenres)
            .SelectMany(a => a.AlbumToGenres)
            .Where(a => a.GenreId == Convert.ToInt32(queryParameters.GenreId))
            .Select(a => a.Album);
        }

        // Pagination
        albums = albums.Skip(queryParameters.Size * (queryParameters.Page - 1))
        .Take(queryParameters.Size);

        return albums;
    }

这是我的控制器:

[HttpGet]
    public async Task<IActionResult> GetAllAlbums([FromQuery] AlbumQueryParameters queryParameters)
    {
        var albums = _albumRepository.GetAllAlbums(queryParameters);
        return Ok(await albums.ToListAsync());
    }

当我尝试从该操作中获取数据时,我收到此错误:

    Microsoft.Data.SqlClient.SqlException (0x80131904): The number of rows provided for a FETCH clause must be greater then zero.
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlDataReader.TryHasMoreRows(Boolean& moreRows)
   at Microsoft.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout, Boolean& more)
   at Microsoft.Data.SqlClient.SqlDataReader.ReadAsyncExecute(Task task, Object state)
   at Microsoft.Data.SqlClient.SqlDataReader.InvokeAsyncCall[T](AAsyncCallContext`1 context)
--- End of stack trace from previous location ---
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at StoreApi.Controllers.AlbumController.GetAllAlbums(AlbumQueryParameters queryParameters) in /home/nalien/Projects/albumshop/StoreApi/Controllers/AlbumController.cs:line 27
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
ClientConnectionId:66426711-f9b4-470a-898d-a0a0bd3082d1
Error Number:10744,State:1,Class:15

HEADERS
=======
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate, br
Host: localhost:5001
Referer: http://localhost:5000/api/album?artistId=1
User-Agent: PostmanRuntime/7.26.8
Postman-Token: 317f1fb0-a03b-4c15-a6da-87b69934bf57

当我评论 Pagination 时,我收到一个新错误:

System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance 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 instance, or wrapping it 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: 'ApplicationDbContext'.
   at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_ChangeTracker()
   at Microsoft.EntityFrameworkCore.Query.CompiledQueryCacheKeyGenerator.GenerateCacheKeyCore(Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.RelationalCompiledQueryCacheKeyGenerator.GenerateCacheKeyCore(Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerCompiledQueryCacheKeyGenerator.GenerateCacheKey(Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at StoreApi.Controllers.AlbumController.GetAllAlbums(AlbumQueryParameters queryParameters) in /home/nalien/Projects/albumshop/StoreApi/Controllers/AlbumController.cs:line 27
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate, br
Host: localhost:5001
Referer: http://localhost:5000/api/album?artistId=2
User-Agent: PostmanRuntime/7.26.8
Postman-Token: 6291c9cf-87cd-45a8-bc84-8d796efbed03

这是我注册服务的方式:

services.AddDbContext<ApplicationDbContext>(options => {options.UseSqlServer(configuration.GetConnectionString("MainDatabase"));});
services.AddTransient<IAlbumRepository, AlbumRepository>();
services.AddMemoryCache();

我不知道如何解决我的问题。

【问题讨论】:

  • 第一个错误可能是因为您没有设置queryArguments.Page,并且在您的存储库上您已经编写了Skip(queryArguments.Size * (queryArguments.Page - 1)),所以如果 Page =0 和 Size>0 您调用 Skip 时带有一个负值。

标签: c# asp.net-core asp.net-web-api entity-framework-core


【解决方案1】:

第一个异常发生在Skip和Take为0的时候,可以在给查询加分页前检查一下:

if(queryParameters.Size>0) 
   albums = albums.Skip(queryParameters.Size * (queryParameters.Page - 1)) .Take(queryParameters.Size);

第二个例外是关于对象生命周期,您将ApplicationDbContext 添加为Scope 意味着它存在于请求生命周期中,但您将IAlbumRepository 添加为Transient 意味着为每个调用接口提供一个新实例,因此当您调用此行:

var albums = _albumRepository.GetAllAlbums(queryParameters);

创建IAlbumRepository 的一个实例,当响应和返回相册时,它将被释放,并且其中的任何对象都将被释放(包括ApplicationDbContext),为防止此异常,只需将IAlbumRepository 添加为Scoped,如下所示:

services.AddScoped<IAlbumRepository, AlbumRepository>();

我认为最好返回 List 而不是 IQueryable 这样的东西:

public async List<Album> GetAllAlbums(AlbumQueryParameters queryParameters)
    {
     //do some stuff
     return await ablums.ToListAsync();
    }

在控制器中:

return Ok(await _albumRepository.GetAllAlbums(queryParameters));

【讨论】:

  • 您帮助我解决了第一个错误,我诚挚地感谢您,但第二个问题仍然存在。当我即将发送第二个请求时,它确实发生了;我的意思是第一个请求没问题。
  • 谢谢;你确实帮了我很多。祝我的朋友好运。
猜你喜欢
  • 2021-04-09
  • 2020-03-09
  • 2017-01-04
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
  • 2021-04-19
相关资源
最近更新 更多