1

I want to return a empty string when the SomethingName is null. plants.Something.Name != null ? plants.Something.Name : string.Empty still gets me a null pointer. I have debugged my application to know this is causing a NullPointerException.

When I set data in the database for SomethingName, I dont get a null pointer exception.

Is there a better way to handle this?

    public FlowerMetaData GetLeafByFlowerId(int flowerId, string flowerName)
    {
        _flowerContext = _contextUtility.GetFlowerContext(flowerName);

        var flowerData = (from flowers in _flowerContext.Flowers
                            where flowers.FlowerId == flowerId
                            join plants in _flowerContext.Plants on flowers.PlantId equals plants.PlantId
                            join leafs in _flowerContext.Leafs on flowers.LeafAk equals leafs.LeafAK
                            select new FlowerMetaData
                            {
                                PlantId = plants.PlantId,
                                PlantName = plants.PlantName,
                                FlowerName = FlowerName.ToUpper(),
                                FlowerNumber = leafs.FlowerNumber,
                                SomethingName = plants.Something.Name != null ? plants.Something.Name : string.Empty,
                                CreatedId = plants.CreatedId,
                            }).FirstOrDefault();

        return flowerData;
    }

StackTrace

    "Message": "An error has occurred.",
    "ExceptionMessage": "No flower found with id = 37.",
    "ExceptionType": "System.NullReferenceException",
    "StackTrace": "   at Flower.Services.FlowerService.GetLeafByFlowerId(Int32 flowerId, String flowerName)
at Flower.Controllers.Controllers.FlowerController.GetFlower(Int32 id, String client)\r\n   at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
  at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
  at Sentinel.Services.AuthFilters.AddChallengeOnUnauthorizedResult.<ExecuteAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.AuthenticationFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
Maddy
  • 1,861
  • 1
  • 20
  • 47

2 Answers2

3

Usually you can use the null-conditional operator from C# 6, combined with the null-coalescing operator from C# 2 to make this really easy:

SomethingName = plants.Something?.Name ?? ""

The expression plants.Something?.Name will evaluate to null (rather than throwing an exception) if plants.Something evaluates to null. The ?? "" part then just says "If the result of the expression is null, use the empty string instead."

If plants itself could be null, you could use:

SomethingName = plants?.Something?.Name ?? ""

... but in this case you shouldn't need to.

However, there's a wrinkle: if you're doing this in an LINQ provider that uses IQueryable<T> (rather than IEnumerable<T> as LINQ to Objects usually does), each part of your query expression will be converted into an expression tree, and the null-conditional operator isn't supported in expression trees. As such, you may need to perform a projection in the query, then do the last part in-process. For example:

var entry = (from flowers in _flowerContext.Flowers
             where flowers.FlowerId == flowerId
             join plants in _flowerContext.Plants on flowers.PlantId equals plants.PlantId
             join leafs in _flowerContext.Leafs on flowers.LeafAk equals leafs.LeafAK
             select new { plants, leafs }).FirstOrDefault();
return entry == null 
    ? null // No entry in the database
    : new FlowerMetaData
      {
          PlantId = entry.plants.PlantId,
          PlantName = entry.plants.PlantName,
          FlowerName = FlowerName.ToUpper(),
          FlowerNumber = entry.leafs.FlowerNumber,
          SomethingName = entry.plants.Something?.Name ?? "",
          CreatedId = entry.plants.CreatedId,
       };
Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • what can I do if `plant` or `something` is not nullable. – Maddy Apr 04 '18 at 17:44
  • @Maddy: Then the compiler won't allow you to use `?.`, but you wouldn't get a `NullReferenceException` anyway for that part, because you couldn't be dereferencing a null value. – Jon Skeet Apr 04 '18 at 17:45
  • `?.` operator cannot be used in expression trees though. – Ivan Stoev Apr 04 '18 at 18:03
  • @IvanStoev: You're absolutely right. Will edit. – Jon Skeet Apr 04 '18 at 18:07
  • @IvanStoev: Have a look now. – Jon Skeet Apr 04 '18 at 18:12
  • Well, I'm not quite sure what do you mean by "out-of-process LINQ provider" - every LINQ query starting with `IQueryable` uses expressions until gets converted to `IEnumerable`. Also, since the question is tagged EF6, accessors like `a.b.d.e` never generate NRE because the whole thing is converted to SQL (there are no "objects"). Anyway, anyone who works with EF6 knows that NRE does not happen from such query, so closing the question as typical NRE dupe seems more appropriate to me, sorry. – Ivan Stoev Apr 04 '18 at 18:25
  • @IvanStoev: I've edited the out-of-process LINQ provider part, but it's not entirely clear to me what *is* going on. I don't think it's definitely a dupe of the canonical NRE at the moment, although of course if Maddy provides more information (e.g. the full stack trace) that would give more cause for reopening. – Jon Skeet Apr 04 '18 at 18:29
  • @DaisyShipton Full stack trace - indeed :) – Ivan Stoev Apr 04 '18 at 18:36
  • @DaisyShipton, I get the following error when I try `Something?.Name` `an expression tree lambda may not contain a null propagating operator` – Maddy Apr 04 '18 at 18:59
  • @Maddy: Please read the whole edited posted, which explains why you're getting that. – Jon Skeet Apr 04 '18 at 19:07
  • @DaisyShipton, I highly appreciate the in depth explanation. Have been stuck on this for so long I did not read it carefully. If you could help with one thing, I get `"Object reference not set to an instance of an object."` when trying to access an element. Would it be possible for you to explain why this might happen? – Maddy Apr 04 '18 at 19:48
  • @Maddy: Well normally because you're dereferencing a null reference. I wouldn't expect that to happen with the code in my answer though. – Jon Skeet Apr 04 '18 at 19:54
  • @DaisyShipton, I thought the same thing as well but I do get that error and its making 0 sense right now. – Maddy Apr 04 '18 at 19:59
  • @DaisyShipton, I figured the error out. I am using code first approach for my EF and I had `HasRequired` for the field that was optional. – Maddy Apr 04 '18 at 20:10
  • @Maddy There you go. This is one of the EF6 smart behaviors. Once you tell EF that the column is not nullable, it will remove any null checks from your code :) https://stackoverflow.com/questions/49537627/incorrect-sql-generation-on-query/49538335#49538335 – Ivan Stoev Apr 05 '18 at 06:24
  • @IvanStoev: I took the opposite result away from the eventual cause - your assertion that "Anyway, anyone who works with EF6 knows that NRE does not happen from such query" was incorrect, as it was EF deliberately creating the NRE due to a misconfiguration effectively. The cause *wasn't* the normal "you're dereferencing a null reference in your code" that the canonical question refers to. – Jon Skeet Apr 05 '18 at 06:34
  • @DaisyShipton Sorry if you misunderstood me or I didn't explain it well (after all, I'm just commenting). What I meant was that NRE cannot be caused by the query written by the user. Hence your general answer does not apply. The cause as you see is quite different. I agree it's not a generic NRE, but to me the canonical question is addressing what should you do in order to identify the source of the problem, rather than how to solve it, what your answer is trying to do. I think the more important is that the question lacks [mcve], so probably that would have been a better close reason. – Ivan Stoev Apr 05 '18 at 06:50
1

Not tested but

SomethingName = plants.Something?.Name ?? string.empty

should do what's required. What Daisy said!

Peter Smith
  • 5,321
  • 8
  • 46
  • 72