vkhorikov / SpecPattern

Source code for the Specification Pattern in C# Pluralsight course

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

AndSpecification - The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

AndresRamos opened this issue · comments

When combining multiple specifications I get the following error:
"The LINQ expression node type 'Invoke' is not supported in LINQ to Entities."

I get this error even when combining simple specifications like in the follwing example:

In my MVC ViewModel:

public async Task<ActionResult> Index(ContratosIndexViewModel viewModel)
{
    var contratosSpecification = Specification<Contrato>.All;
    
    if (viewModel.TipoRangoFecha != null)
    {
        contratosSpecification = contratosSpecification.And(new FiltrarPorRangoFechaSpecification(viewModel.FechaInicio, viewModel.FechaFin, viewModel.TipoRangoFecha.Value));
    }

    if (viewModel.ClienteId != null)
    {
        contratosSpecification = contratosSpecification.And(new FiltrarPorClienteIdSpecification(viewModel.ClienteId.Value));
    }

    if (viewModel.EmpresaId != null)
    {
        contratosSpecification = contratosSpecification.And(new FiltrarPorEmpresaIdSpecification(viewModel.EmpresaId.Value));
    }

    viewModel.Contratos = await _mediator.Send(new BuscarContratosQuery(User.Identity.GetUserId(), contratosSpecification));

    return View(viewModel);
}

My Sepcifications:

public class FiltrarPorClienteIdSpecification : Specification<Contrato>
{
    private readonly int _clienteId;

    public FiltrarPorClienteIdSpecification(int clienteId)
    {
        _clienteId = clienteId;
    }

    public override Expression<Func<Contrato, bool>> ToExpression()
    {
        return contrato => contrato.ClienteId == _clienteId;
    }
}

public class FiltrarPorEmpresaIdSpecification : Specification<Contrato>
{
    private readonly int _empresaId;

    public FiltrarPorEmpresaIdSpecification(int empresaId)
    {
        _empresaId = empresaId;
    }

    public override Expression<Func<Contrato, bool>> ToExpression()
    {
        return contrato => contrato.EmpresaId == _empresaId;
    }
}

public class FiltrarPorRangoFechaSpecification : Specification<Contrato>
{
    private readonly DateTime _fechaFin;
    private readonly DateTime _fechaInicio;
    private readonly TipoRangoFechaEnum _tipoFechaFiltro;

    public FiltrarPorRangoFechaSpecification(DateTime fechaInicio, DateTime fechaFin, TipoRangoFechaEnum tipoFechaFiltro = TipoRangoFechaEnum.Creado)
    {
        _fechaInicio = fechaInicio;
        _fechaFin = fechaFin;
        _tipoFechaFiltro = tipoFechaFiltro;
    }

    public override Expression<Func<Contrato, bool>> ToExpression()
    {
        switch (_tipoFechaFiltro)
        {
            case TipoRangoFechaEnum.Creado:
                return contrato => DbFunctions.TruncateTime(contrato.FechaCreado) >= DbFunctions.TruncateTime(_fechaInicio) &&
                                   DbFunctions.TruncateTime(contrato.FechaCreado) <= DbFunctions.TruncateTime(_fechaFin);
            case TipoRangoFechaEnum.Liberado:
                return contrato => DbFunctions.TruncateTime(contrato.FechaLiberado) >= DbFunctions.TruncateTime(_fechaInicio) &&
                                   DbFunctions.TruncateTime(contrato.FechaLiberado) <= DbFunctions.TruncateTime(_fechaFin);
            case TipoRangoFechaEnum.Aprobado:
                return contrato => DbFunctions.TruncateTime(contrato.FechaAprobado) >= DbFunctions.TruncateTime(_fechaInicio) &&
                                   DbFunctions.TruncateTime(contrato.FechaAprobado) <= DbFunctions.TruncateTime(_fechaFin);
            case TipoRangoFechaEnum.Cerrado:
                return contrato => DbFunctions.TruncateTime(contrato.FechaCerrado) >= DbFunctions.TruncateTime(_fechaInicio) &&
                                   DbFunctions.TruncateTime(contrato.FechaCerrado) <= DbFunctions.TruncateTime(_fechaFin);
            case TipoRangoFechaEnum.Vencido:
                return contrato => DbFunctions.TruncateTime(contrato.FechaVencido) >= DbFunctions.TruncateTime(_fechaInicio) &&
                                   DbFunctions.TruncateTime(contrato.FechaVencido) <= DbFunctions.TruncateTime(_fechaFin);
            default:
                return contrato => DbFunctions.TruncateTime(contrato.FechaCreado) >= DbFunctions.TruncateTime(_fechaInicio) &&
                                   DbFunctions.TruncateTime(contrato.FechaCreado) <= DbFunctions.TruncateTime(_fechaFin);
        }
    }
}

In My Handler

public class BuscarContratosQueryHandler : IRequestHandler<BuscarContratosQuery, IEnumerable<ContratoDto>>
{
    private readonly ApplicationDbContext _context;
    private readonly IMapper _mapper;

    public BuscarContratosQueryHandler(ApplicationDbContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }

    public async Task<IEnumerable<ContratoDto>> Handle(BuscarContratosQuery request, CancellationToken cancellationToken)
    {
        return await _context.Contratos.AsNoTracking()
            .Include(contrato => contrato.Empresa)
            .Include(contrato => contrato.Plaza)
            .Include(contrato => contrato.Vendedor)
            .Include(contrato => contrato.Cliente)
            .Include(contrato => contrato.Representante)
            .Where(request.ContratosSpecification.ToExpression())
            .OrderByDescending(c => c.FechaCreado)
            .ProjectTo<ContratoDto>(_mapper.ConfigurationProvider)
            .ToListAsync(cancellationToken);
    }
}

StackTrace

   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.NotSupportedTranslator.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SelectTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
   at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass43_0.<GetResultsAsync>b__1()
   at System.Data.Entity.Core.Objects.ObjectContext.<ExecuteInTransactionAsync>d__156`1.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<ExecuteAsyncImplementation>d__6`1.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.Utilities.TaskExtensions.CultureAwaiter`1.GetResult()
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<GetResultsAsync>d__43.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.Utilities.TaskExtensions.CultureAwaiter`1.GetResult()
   at System.Data.Entity.Internal.LazyAsyncEnumerator`1.<FirstMoveNextAsync>d__9.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.Infrastructure.IDbAsyncEnumerableExtensions.<ForEachAsync>d__2`1.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at SistemaVentasMaquila.Core.Application.Contratos.Queries.BuscarContratos.BuscarContratosQueryHandler.<Handle>d__3.MoveNext() in C:\Users\gerar\Source\Repos\AndresRamos\SistemaVentasMaquila\SistemaVentasMaquila.Core.Application\Contratos\Queries\BuscarContratos\BuscarContratosQueryHandler.cs:line 27
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at SistemaVentasMaquila.Controllers.ContratosController.<Index>d__2.MoveNext() in C:\Users\gerar\Source\Repos\AndresRamos\SistemaVentasMaquila\SistemaVentasMaquila\Controllers\ContratosController.cs:line 90