jlembke
jlembke

Reputation: 13517

Linq - pulling a value from a null query result

I have a linq query that needs to pull a date column out of a row. The expression currently looks like this

myObject.OrderByDescending(s=> s.MyDate).Where(s => s.CRAStatus.Description == "CheckedOut").FirstOrDefault().MyDate)

The problem is that if there are no rows that are "CheckedOut", the query will return a null and attempting to get "MyDate" will throw an exception. We have some verbose solutions, like:

.ForMember(dest => dest.CheckOutDate, opt => opt.MapFrom(src => {
    var temp = src.CRAStatusChangeEvents.OrderByDescending(s=> s.MyDate).Where(s => s.CRAStatus.Description == "CheckedOut").FirstOrDefault();
    return temp == null ? temp.MyDate : null;
}));

But it would be nice to find something a little more concise. Any Ideas?

Upvotes: 4

Views: 547

Answers (4)

Ruben
Ruben

Reputation: 15515

Why not

myObject.OrderByDescending(s=> s.MyDate)
        .Where(s => s.CRAStatus.Description == "CheckedOut")
        .Select(s => s.MyDate as DateTime?)
        .FirstOrDefault();

or

myObject.Where(s => s.CRAStatus.Description == "CheckedOut")
        .Max(s => s.MyDate as DateTime?);

Upvotes: 4

bobbymcr
bobbymcr

Reputation: 24167

How about an extension method?

static class MyObjectEnumerableExtensions
{
    public static TMember GetMemberOfFirstOrDefault<TMember>(this IEnumerable<MyObject> items, Func<MyObject, TMember> getMember)
    {
        MyObject first = items.FirstOrDefault();
        if (first != null)
        {
            return getMember(first);
        }
        else
        {
            return default(TMember);
        }
    }
}

Sample usage:

List<MyObject> objects = new List<MyObject>();
objects.Add(new MyObject { MyDate = DateTime.MinValue });

var filteredObjects = from s in objects where s.MyDate > DateTime.MinValue select s;

DateTime date = filteredObjects.GetMemberOfFirstOrDefault(s => s.MyDate);

Console.WriteLine(date);

Upvotes: 1

user1228
user1228

Reputation:

One option is to set the default if empty to an "empty" instance (think of string.Empty--its a known instance that represents an empty result):

var date = (myObject
    .OrderByDescending(s=> s.MyDate)
    .Where(s => s.CRAStatus.Description == "CheckedOut")
    .DefaultIfEmpty(MyObject.Empty)
    .FirstOrDefault()).MyDate;

Here's a snippet that shows how it works:

var strings = new string[]{"one", "two"};
var length = 
    (strings.Where(s=>s.Length > 5)
    .DefaultIfEmpty(string.Empty)
    .FirstOrDefault()).Length;

run that and length is 0. Remove the DefaultIfEmpty line and you get a NRE.

Upvotes: 3

bruno conde
bruno conde

Reputation: 48265

var checkedOut = myObject.Where(s => s.CRAStatus.Description == "CheckedOut");
if (checkedOut.Count() > 0) {
   var result = checkedOut.Max(s=> s.MyDate).MyDate;
}

Upvotes: 1

Related Questions