Ehsan Akbar
Ehsan Akbar

Reputation: 7301

Avoiding null exception in EF that is thrown by your query

I have a query like this :

        result =
            firstIdeaRepository.FindBy(
                i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
                               .AsEnumerable()
                               .Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
                                   {
                                       Name =
                                           companyRepository.FindBy(i => i.UserId == j.UserId)
                                                            .FirstOrDefault()
                                       DateOfMeeting =
                                           calenderRepository.ConvertToPersianToShow(
                                               meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
                                                                .FirstOrDefault()
                                                                .Date),
                                       DateOfExit =
                                           calenderRepository.ConvertToPersianToShow(j.DateOfExit.Value),
                                       ReasonOfExit = j.ReasonOfExit,
                                   }).ToList();

    return result;

As you can see i use FirstOrDefault() and j.DateOfExit.Value and sometimes my Date doesn't have any values or sometime my other variables are null too because i use firstordefaut() like

companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault().

So my query throws a null exception and the result can't be created ,how can i handle this exception and for example if the .NET detects the null value ignores it by default or uses a default values for that ?

Best regards.

Upvotes: 0

Views: 811

Answers (3)

psaxton
psaxton

Reputation: 1843

The broadest solution would be to use the idea of an null object with the DefaultIfEmpty<T>(T DefaultValue) method in your query. An example would be:

var defaultMeeting = new Meeting() { Date = new DateTime() };

var dateOfMeeting = meetingRepository.FindBy(s => s.FirstIdeaId == j.Id)
        .DefaultIfEmpty(defaultMeeting)
        .FirstOrDefault()
        .Date;

Upvotes: 0

Andrew Whitaker
Andrew Whitaker

Reputation: 126052

I would make the following changes:

result =
    firstIdeaRepository.FindBy(
        i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
            .AsEnumerable()
            .Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
            {
                Name =
                    companyRepository.FindBy(i => i.UserId == j.UserId)
                        .FirstOrDefault()
                DateOfMeeting =
                    callenderRepository.ConvertToPersianToShow(
                        meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
                            // project a new sequence first, before calling `FirstOrDefault`:
                            .Select(s => s.Date)
                            .FirstOrDefault(),
                DateOfExit =
                    j.DateOfExit.HasValue ? 
                        callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value) :
                        null,                       
                   ReasonOfExit = j.ReasonOfExit,
               }).ToList();

When you use FirstOrDefault, there's a possibility that you'll get null back (in the case of reference types), and so you need to plan for that in your code.

For example, when assigning DateOfMeeting, you could project the results (using .Select) before using .FirstOrDefault, so that you're not ever accessing the Date property on what could be a null value.

As for DateOfExit, I've used the conditional operator to determine whether to call the calendarRepository's method at all. This assumes that DateOfExit is nullable.

Unrelated: "Calendar" is spelled with one "l" and not two.

Upvotes: 1

Mrchief
Mrchief

Reputation: 76218

Since you're using a nullable date, you can try filtering by values that have date, something like:

.FindBy(s => s.FirstIdeaId == j.Id && s.Date.HasValue)

This will ensure that you don't get any records with null date.

As I mentioned in comments, other cases need to be handled on case-by-case basis. Judging by the code you've shown, maybe you can handle Name as:

 Name = companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault() ?? "anonymous";

and so on.

Another example:

If you do want to get the record even if DateOfMeeting is null, then add a check for HasValue in subsequent part or default it to some date:

 DateOfExit = j.DateOfExit.HasValue ? 
              callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
              : (DateTime)null,   // you need to make `DateOfExit` nullable and then handle that downstream

// or (default with current date)
 DateOfExit = j.DateOfExit.HasValue ?
              callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value) 
              : callenderRepository.ConvertToPersianToShow(DateTime.Now),

// or (default with empty date)
 DateOfExit = j.DateOfExit.HasValue ?
              callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value) 
              : callenderRepository.ConvertToPersianToShow(new DateTime()),

Moral of the story: figure out what the default value should be in case of null and then substitute that accordingly in the query when calling FirstOrDefault().

Upvotes: 1

Related Questions