bookshopkeeper
bookshopkeeper

Reputation: 56

Unable to cast object of type 'System.Linq.Expressions.MethodCallExpression' to type 'System.Linq.Expressions.LambdaExpression'

I am using EntityFramework and having trouble with the above error. When I invoke

var documents = docRepository.GetAllSavedDocuments();
var withRatings = documentFilter.GetDocumentsWithRating(documents, 1);

I get the error in the title.

My code is split across a few classes

public class DocRepository
{
    public IQueryable<Document> GetAllSavedDocuments(User user)
    {
        return (from d in AllUsersDocuments(user)
                select d)
            .Include(d => d.Calculations)
            .Include("Calculations.CalculationResults");
    }
}

public class DocumentFilter
{
    public IQueryable<Tuple<Document, Rating>> GetDocumentsWithRating(IQueryable<Document> documents, int id)
    {
        return documents.Select(x => new
        {
            Document = x,
            Rating = _ratingsProvider.GetRating.Compile()(x, id)
        }).AsEnumerable()
            .Select(x => new Tuple<Document, Rating>(x.Document, x.Rating)).AsQueryable().Where(x.Item2 == Rating.Ok);
    }
}

public class RatingProvider
{
    public Expression<Func<Document, int, Rating>> GetRating
    {
        get
        {
            return (document, id) =>
                document.Calculations.Where(x => x.CId == id).Any() ?
                document.Calculations.Single(x => x.CId == id).Rating.Value :
                Rating.Unknown;
        }
    }
}

}

Extra Info

Rating is an Enum. It is nullable on the Calculation class. I initially had IEnumerable, and so I could use a method in the Rating Provider class to filter, something like:

var c = document.Calculations.SingleOrDefault(x => x.Id == id);
if (c == null)
{
    return Rating.None;
}
if (!c.Rating.HasValue)
{
    return Rating.None
}
return c.Rating.Value;

But I would like to keep it queryable so we execute it on SQL side as opposed to enumerating all objects in the code. Please could someone suggest how I can achieve this?

Upvotes: 3

Views: 5610

Answers (1)

michael yin
michael yin

Reputation: 249

You can assign a variable for _ratingsProvider.GetRating first, to avoid the error in the title.

After that you will have another error saying entity framework does not support your query. You need something like LINQKit https://github.com/scottksmith95/LINQKit. AsExpandable() and Invoke(x, id) are from LINQKit.

using LinqKit;
    public IQueryable<Tuple<Document, Rating>> GetDocumentsWithRating(IQueryable<Document> documents, int id)
    {
        var getRatingExpression = _ratingsProvider.GetRating;
        return documents.AsExpandable().Select(x => new
        {
            Document = x,
            Rating = getRatingExpression.Invoke(x, id)
        }).AsEnumerable()
            .Select(x => new Tuple<Document, Rating>(x.Document, x.Rating)).AsQueryable().Where(x.Item2 == Rating.Ok);
    }

Answer to your second question:

    public Expression<Func<Document, int, Rating>> GetRating
    {
        get
        {
            return (document, id) =>
                document.Calculations.Where(x => x.CId == id).Any() ?
                document.Calculations
                        .Where(x => x.CId == id)
                        .Select(x => x.Rating.HasValue ? x.Rating.Value : Rating.None)
                        .Single()
                Rating.Unknown;
        }
    }

Answer to the .AsEnumerable() part of your second question: I guess the problem you had came from the use of Tuple<Document, Rating>, try to define a class

public class DocumentRating
{
    public Document { get; set; }
    public Rating { get; set; }
}
    public IQueryable<DocumentRating> GetDocumentsWithRating(IQueryable<Document> documents, int id)
    {
        var getRatingExpression = _ratingsProvider.GetRating;
        return documents.AsExpandable().Select(x => new DocumentRating
        {
            Document = x,
            Rating = getRatingExpression.Invoke(x, id)
        })
        .Where(x.Item2 == Rating.Ok);
    }

Upvotes: 3

Related Questions