Reputation: 56
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
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