In my C # code, I have 2 WHERE queries, both of which I can call IQueryable, and all this compiled before SQL, and both of them have a lot of common logic.
I believe this is not a duplication of this similar question: Using a function in a Select Entity Framework Query clause , because in my script the function in question can be converted to SQL-EF, it just does not understand that it can do this.
Requests approximately:
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<Template> set, User user) { return set.Where(temp => temp.Requests .Where(req => req.WasSent) .OrderByDescending(req => req.DueDate) .Take(2) .SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id)) .Contains(user.Id)); }
AND
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<DataReturn> set, User user) { return set.Where(ret=> ret.Entity.Id == user.Entity.Id && ret.Request.Template.Requests .Where(req => req.WasSent) .OrderByDescending(req => req.DueDate) .Take(2) .SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id)) .Contains(user.Id)); }
So, the basic BusinessLogic rule for "owns the template", and then the consequence for "belongs to DataReturn if the company matches the AND own template"
As you can see, thinking only about C #, they can be easily reorganized as:
private static bool UserOwnsTemplate(User user, Template temp) { return temp.Requests .Where(req => req.WasSent) .OrderByDescending(req => req.DueDate) .Take(2) .SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id)) .Contains(user.Id); } public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<Template> set, User user) { return set.Where(temp => UserOwnsTemplate(user, temp)); } public static IQueryable<DataReturn> WhereIsOwnedByUser(this IQueryable<DataReturn> set, User user) { return set.Where( ret => ret.Entity.Id == user.Entity.Id && UserOwnsTemplate(user, ret.Request.Template) ); }
Thus, reducing duplication (Yay!)
But , then EF will complain that he does not know what to do with UserOwnsTemplate , despite the fact that he copes with the logic in SQL.
AFAICT there is no good way to solve this problem. I think my options are:
- Turn
UserOwnsTemplate to UDF, the SQL function defined in the database.- But I cannot create UDF from C # lamda, I have to define SQL, which will be more complex.
- Assign
Expression<Func<Template,bool>> , which UserOwnsTemplate defines as a variable, and then create the corresponding Expression<Func<DataReturn ,bool>> for the DataReturn version manually, using Expression.AndAlso to glue the two sentences together.- Meta programming. Ughhh. I did this earlier in another project, and it was nasty, and a nightmare to maintain.
- Live with duplication.
- It is likely to happen unless SO reports otherwise .;)
Can anyone see the other available options?
Can I do anything to get EF to parse a function in SQL? (the phrase "inling" comes to mind, but I don’t know what I think what I mean?)
Can someone see a way to convert ret.Request.Template to IQueryable so that I can just call another WhereIsOwnedBy extension method?
Any other suggestions in EVERYONE?