Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 548
jeffrey chan Avatar asked Dec 31 '18 08:12

jeffrey chan


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.

like image 142
Ivan Stoev Avatar answered Oct 20 '22 02:10

Ivan Stoev