Unable to cast the type 'System.Int32' to type 'System.Object during EF Code First Orderby Function

Viewed 265

I'm using Specification pattern in EF Code First. When I do order by operation, VS throw a new exception

enter image description here

The specification pattern is copy from eShopOnWeb

I just change a little bit, here is my change code:

public class Specification<T> : ISpecification<T>
{ 

    public Expression<Func<T, object>> OrderBy { get; private set; }

    public Specification(Expression<Func<T, bool>> criteria)
    {
        Criteria = criteria;
    }

    public Specification<T> OrderByFunc(Expression<Func<T, object>> orderByExpression)
    {
        OrderBy = orderByExpression;
        return this;
    }
}

Here is my invoke code, it's very pretty simple:

static void TestSpec()
    {
        var spec = new Specification<ExcelData>(x => x.RowIndex == 5)
            .OrderByFunc(x => x.ColumnIndex);

        using (var dbContext = new TechDbContext())
        {
            var top10Data = dbContext.ExcelData.Take(10).ToList();
            var listExcel = dbContext.ApplySpecification(spec).ToList();

            Console.WriteLine();
        }
    }

If I comment OrderByFunc, then everything is fine to me. no error throw from vs.

I had try many times search the error message in google, but none of answer is my case.

So I have to ask a question in here.

When I debug OrderBy property in SpecificationEvaluator.cs, I found there is a Convert method. enter image description here

So I know the error is about cast error, but how do I fix this cast type error?

Please help me!

1 Answers

The solution is to create new lambda expression with cast (Convert) removed, and then use it to call the Queryable class OrderBy / OrderByDescending method either dynamically (using DLR dispatch or reflection) or by emitting Expression.Call to it.

For the first part, add the following helper method to the SpecificationEvaluator class:

static LambdaExpression RemoveConvert(LambdaExpression source)
{
    var body = source.Body;
    while (body.NodeType == ExpressionType.Convert)
        body = ((UnaryExpression)body).Operand;
    return Expression.Lambda(body, source.Parameters);
}

Then replace the code

query = query.OrderBy(specification.OrderBy);

with either

query = Queryable.OrderBy((dynamic)query, (dynamic)RemoveConvert(specification.OrderBy));

or

var keySelector = RemoveConvert(specification.OrderBy);
query = query.Provider.CreateQuery<T>(Expression.Call(
    typeof(Queryable), nameof(Queryable.OrderBy),
    new[] { typeof(T), keySelector.ReturnType },
    query.Expression, keySelector));

Do similar for the specification.OrderByDescending.

Related