Base solution for your next web application
Open Closed

Error when calling a repository from within a Linq Select #11135


User avatar
0
rickfrankel created

Prerequisites

Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.

  • What is your product version?
  • 11.1.2
  • What is your product type (Angular or MVC)?
  • Angular
  • What is product framework type (.net framework or .net core)?
  • .Net Core

If issue related with ABP Framework

  • What is ABP Framework version?
  • 7.3

Related potentially to this ticket but thought I'd create a new one. https://support.aspnetzero.com/QA/Questions/10816/MvcAuthorizationAbpAuthorizationFilter---UnitOfWork-error

When you have code that makes a call to an IRepository from within a Select on an IQueryable (or an IEnumerable, if you call ToList() before the Select it doesn't make a difference) you get the below error when using MySQL as your DB.

ERROR 2022-06-27 11:01:23,556 [81 ] Mvc.ExceptionHandling.AbpExceptionFilter - Value cannot be null. (Parameter 'unitOfWork') System.ArgumentNullException: Value cannot be null. (Parameter 'unitOfWork') at Abp.EntityFrameworkCore.Uow.UnitOfWorkExtensions.GetDbContext[TDbContext](IActiveUnitOfWork unitOfWork, Nullable1 multiTenancySide, String name) at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase3.GetDbQueryTable() at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase3.GetQueryable() at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase3.GetAllIncluding(Expression1[] propertySelectors) at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase3.GetAll() at Abp.Domain.Repositories.AbpRepositoryBase2.FirstOrDefault(TPrimaryKey id) at Abp.Domain.Repositories.AbpRepositoryBase2.Get(TPrimaryKey id) at Inbound.Operations.BookingAttachmentAppService.<>c__DisplayClass6_0.b__13(BinaryObject _) in C:\Source\Inbound\aspnet-core\src\Inbound.Application\Operations\BookingAttachmentAppService.cs:line 109

at System.Linq.Enumerable.SelectListIterator`2.MoveNext() at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType) at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType) at Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer) at Abp.Auditing.JsonNetAuditSerializer.Serialize(Object obj) at Abp.AspNetCore.Mvc.Auditing.AbpAuditActionFilter.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.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.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)


As per the other ticket when using the vanilla setup using MSSQL you get the below exception

ERROR 2022-06-23 17:11:17,525 [37 ] Mvc.ExceptionHandling.AbpExceptionFilter - There is already an open DataReader associated with this Connection which must be closed first. System.InvalidOperationException: There is already an open DataReader associated with this Connection which must be closed first. at Microsoft.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command) at Microsoft.Data.SqlClient.SqlCommand.ValidateCommand(Boolean isAsync, String method) at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String method) at Microsoft.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.InitializeReader(Enumerator enumerator) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.MoveNext() at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable1 source, Boolean& found) at lambda_method2167(Closure , QueryContext ) at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable1 source, Expression1 predicate) at Abp.Domain.Repositories.AbpRepositoryBase2.FirstOrDefault(TPrimaryKey id) at lambda_method2163(Closure , QueryContext , DbDataReader , ResultContext , SingleQueryResultCoordinator ) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.MoveNextAsync() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at Inbound.Authorization.Users.UserAppService.GetUsers(GetUsersInput input) in C:\Source\Inbound-v11-2\aspnet-core\src\Inbound.Application\Authorization\Users\UserAppService.cs:line 124 at lambda_method2062(Closure , Object ) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)

at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.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.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

To reproduce all I did was take a newly downloaded vanilla setup and change the following default app service. You will see on the call to populate the property Fred in the select an exception is thrown. This did not happen in my previous version which was 10.2.

    [HttpPost]
    public async Task<PagedResultDto<UserListDto>> GetUsers(GetUsersInput input)
    {
        var query = GetUsersFilteredQuery(input);

        var userCount = await query.CountAsync();

        var users = await query
            .OrderBy(input.Sorting)
            .PageBy(input)
            .ToListAsync();

        var userListDtos = ObjectMapper.Map<List<UserListDto>>(users);
        await FillRoleNames(userListDtos);

        var roles = _userRoleRepository.GetAll();

        var test = await roles.Select(_ => new
        {
            Fred = _roleRepository.FirstOrDefault(_.RoleId)
        }).ToListAsync();


        return new PagedResultDto<UserListDto>(
            userCount,
            userListDtos
        );
    }

4 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @rickfrankel

    I think this problem might be related to https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.x/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client. In the previous versions, some queries were executed locally in some cases. So, this was working on the previous versions but it had a bad performance.

    So, you should make a join to Roles in your query and select the fields you want.

  • User Avatar
    0
    rickfrankel created

    if that were true then i would expect ToList to fix the problem as per that article, however it does not. i still get a problem.

    i think the issue is more related to connection or unit of work management and it somehow being closed early.

    Rick

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    We also changed the logic related to UoW in the Framework but I think it is not related here (not totally sure but seems like that). Do you change your code like this ?

    var roles = _userRoleRepository.GetAll().ToList();
    
    var test = await roles.Select(_ => new
    {
    	Fred = _roleRepository.FirstOrDefault(_.RoleId)
    }).ToListAsync();
    
  • User Avatar
    0
    rickfrankel created

    Interesting. Ok I think you are correct after all with your first response.

    Putting in a ToList does work. However in my full application it doesn't.

    In my full application the ToList doesn't work. However I think there is a lot more complexity in my Linq to SQL in my application which is triggering the issue. In the simple application as shown above the ToList does work.

    Very strange. Thankfully I've only used it in a few places so have managed to work around the issue. Thanks for pointing me to it.

    Cheers