views:

69

answers:

2

When calling Max() on an IQueryable and there are zero records I get the following exception.

The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.

var version = ctx.Entries
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
    .Max(e => e.Version);

Now I understand why this happens my question is how is the best way to do this if the table can be empty. The code below works and solves this problem, but its very ugly is there no MaxOrDefault() concept?

int? version = ctx.Entries
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
    .Select(e => (int?)e.Version)
    .Max();
+3  A: 

Yes, casting to Nullable of T is the recommended way to deal with the problem in LINQ to Entities queries. Having a MaxOrDefault() method that has the right signature sounds like an interesting idea, but you would simply need an additional version for each method that presents this issue, which wouldn't scale very well.

This is one of many mismatches between how things work in the CLR and how they actually work on a database server. The Max() method’s signature has been defined this way because the result type is expected to be exactly the same as the input type on the CLR. But on a database server the result can be null. For that reason, you need to cast the input (although depending on how you write your query it might be enough to cast the output) to a Nullable of T.

Here is a solution that looks slightly simpler than what you have above:

var version = ctx.Entries 
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId) 
    .Max(e =>(int?)e.Version);

Hope this helps.

divega
Hey Divega, see my answer do you see any issues with this approach?
bleevo
bleevo, I am afraid unless you change your method to take IQueryable<T> and Expression<Func<>> arguments, the method will implicitly switch to client-side evaluation with LINQ to Objects. Personally, I think just casting to T? is simpler.
divega
Welcome to SO, Diego! Good to see you here!
Craig Stuntz
+1  A: 

I couldnt take no for an answer :) I have tested the below and it works, I havent checked the SQL generated yet so be careful, I will update this once I have tested more.

var test = ctx.Entries
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
    .MaxOrDefault(x => x.Version);

public static TResult? MaxOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    where TResult : struct
{
    return source
        .Select(selector)
        .Cast<TResult?>()
        .Max();
}
bleevo
This is not an L2E query. It's L2O. That's fine, but it isn't what you asked. :)
Craig Stuntz
Yes your correct back to the drawing board then!
bleevo