C# Task - LazyAsyncResult - NullReferenceException

Sal Datoccio 96 Reputation points
2021-01-22T14:44:52.577+00:00

There is something wrong with the following method:

   public async Task<ExtendedProductDTO> GetProductByUniqueIDAsync(string id)  
            {  
                var product = await _productRepo.FindFirstOrDefaultAsync(i => i.UniqueID == id).ConfigureAwait(false);  
                return new ExtendedProductDTO(product != null, product);  
            **}**  

product is populated correctly and using a debugger I can reach the final } without any problems, but after that I got a weird NullReferenceException.
The exception happens after:
59560-pic-2.png

Notes:

  • ExtendedProductDTO just sets 2 fields (a boolean "Found" property and the actual product)
  • product is correctly retrieved by FindFirstOrDefaultAsync
  • In order to understand what's happening I am invoking the method as follows, but the execution halts after the first call. So product4 = ... and the catch clause are never reached
    59682-pic-3.png
  • I also have a sync method (very similar, just sync) and it works without any problems
  • There are also references to .NET Framework calls, see images below:
    59683-pic-1.png
    59684-pic-4.png

I really don't understand what's happening here...

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,307 questions
.NET Runtime
.NET Runtime
.NET: Microsoft Technologies based on the .NET software framework.Runtime: An environment required to run apps that aren't compiled to machine language.
1,126 questions
{count} votes

Accepted answer
  1. Sal Datoccio 96 Reputation points
    2021-01-26T16:00:24.917+00:00

    I found the root cause of this issue.

    It was not in the:

    • MongoDB driver (async)
    • My repository (async)
    • Application layer (async)
    • ASP.NET controller (async)

    The async controller method was invoked synchronously by the caller and that caused the exception: invoking it in the proper way (=asynchronously) avoids the exception.

    Thank you for your help!

    0 comments No comments

10 additional answers

Sort by: Most helpful
  1. Sal Datoccio 96 Reputation points
    2021-01-22T14:47:49.907+00:00

    System.NullReferenceException
    HResult=0x80004003
    Message=Object reference not set to an instance of an object.
    Source=<Cannot evaluate the exception source>
    StackTrace:
    <Cannot evaluate the exception stack trace>

    Your app has entered a break state, but there is no code to show because all threads were executing external code (typically system or framework code).

    59652-vs.png

    0 comments No comments

  2. Michael Taylor 48,826 Reputation points
    2021-01-22T14:52:48.677+00:00

    You are using async/await so when something goes wrong inside the auto-generated code to support this the debugger throws you into the framework code as you're seeing. Unfortunately debugging is harder with async/await right now.

    Nevertheless the issue is that you're referencing an instance member on a null object. Given the code you posted the only case I see where that happens is on the _productRepo instance. Therefore I assume it is null and causes the exception. You set a breakpoint on that line but keep in mind it is running async so it may not fire until later.

    You also create an instance of ExtendedProductDTO. That too accepts a reference object, Product, and can blow up. If you look at the callstack for the NullReferenceException it should identify exactly where the exception is being thrown. Since we cannot see what your ExtendedProductDTO is doing you might start with it. Maybe the product you're passing it has nulls where it doesn't expect them and it blows up.

    0 comments No comments

  3. Sal Datoccio 96 Reputation points
    2021-01-22T15:05:36.913+00:00

    var product = await _productRepo.FindFirstOrDefaultAsync(i => i.UniqueID == id).ConfigureAwait(false);

    is always populated correctly:

    59663-test.png

    and ExtendedProductDTO is simply:

    public class ExtendedProductDTO  
        {  
            public bool Found { get; }  
      
            public ExtendedProduct Product { get; }  
      
            public ExtendedProductDTO(bool found, ExtendedProduct product)  
            {  
                Found = found;  
                Product = product;  
            }  
        }  
    

    System.NullReferenceException
    HResult=0x80004003
    Message=Object reference not set to an instance of an object.
    Source=<Cannot evaluate the exception source>
    StackTrace:
    <Cannot evaluate the exception stack trace>

    Your app has entered a break state, but there is no code to show because all threads were executing external code (typically system or framework code).
    59692-vs.png


  4. Sal Datoccio 96 Reputation points
    2021-01-22T15:49:49.103+00:00

    This does not work:

      public Task<T> FindFirstOrDefaultAsync(Expression<Func<T, bool>> predicate)
                {
                    return _collection.Find(predicate).FirstOrDefaultAsync();
                }
    

    This works:

     public T FindFirstOrDefault(Expression<Func<T, bool>> predicate)
            {
                return _collection.Find(predicate).FirstOrDefaultAsync().Result;
            }
    

    That method is invoked by:

    public async Task<ExtendedProductDTO> GetProductByUniqueIDAsync(string id)
            {
                var product = await _productRepo.**FindFirstOrDefaultAsync**(i => i.UniqueID == id).ConfigureAwait(true);
                return new ExtendedProductDTO(product != null, product);
            }
    

    And GetProductByUniqueIDAsync is invoked as:

     try
                {
                    var product3 = await _productService.GetProductByUniqueIDAsync("9988ad0a66f28fe8611962a06a593865c1c8103f87258e3920986fc2a4ba2d0e");
                    var product4 = await _productService.GetProductByUniqueIDAsync("6641020d80e10877a13a973592d934165de39c743f7a2fa3f78a8671ac2e9c5b");
                }
                catch (Exception ex)
                {
                    Logger.Fatal(ex.Message, ex);
                }
    

    As said, I can reach the } of GetProductByUniqueIDAsync and all data is there (product and then ExtendedProductDTO).

    When the exception is triggered (=always), the execution of the caller method is interrupted: no lines after " var product3 = ..." is reached.

    0 comments No comments