How do you test a null var when the act of testing returns a NullReferenceException?

I have a BPM in which I’m trying to test if an order (IE an entry in ttOrderHed) has any lines in it (IE entries in a database table Db.OrderDtl).

I’m fairly new to C# and Linq.

When I test with an order that is known to have lines, this works; and when I use an order that doesn’t, it returns a NullReferenceException:


var oh = (from o in ttOrderHed
          select o).FirstOrDeafault();

var od = (from d in Db.OrderDtl
           where d.Company == oh.Company &&
                 d.OrderNum == oh.OrderNum
           select d).FirstOrDefault();

if ( od != null)
    {
      //do some Things and Stuff
    }

I thought maybe I was testing something null and therefore dropping out. I tried to get around it like this:

if (!(od == null))
     {
       //do stuff
     }

And got the same exception.

I also tried using ttOrderDtl instead of Db.OrderDtl. No difference, so I’m pretty convinced that my for a null var is the issue.

I have got debugging variables sprinkled throughout and I can say for sure that any attempt to do anything with var od makes me drop out of warp. For example, if I try to assign a value from it to a debugging variable that is already declared elsewhere b1 = od.OrderNum.ToString(); I get a NullReferenceException here too, just earlier.

Any feedback or suggestions would be appreciated - thanks very much in advance!

Check out Tim’s post on the .Any property in LINQ:

https://www.epiusers.help/t/c-linq-query-what-is-the-best-way-which-is-more-efficient/64887

1 Like

I see a typo in the first query “FirstOrDeafault” but I’m gonna assume that was made when posting here.

Try checking if oh is null before trying to do the rest, so:

if(oh != null)
{
//Everything starting from your second query or “var od”
}

1 Like

Or this thread …

https://www.epiusers.help/t/simple-one-liner-c-to-retrieve-data-from-a-table/44627?u=ckrusen

It includes having your query return a value if no records found, by using:
.DefaultIfEmpty(-1).FirstOrDefault();

Then you could test for (oh.OrderNum == -1) prior to trying to find the OrdDtl record.

1 Like

good point, however yes, I have my debug code sending oh.OrderNum to the server’s event viewer using Ice.Diagnostics.Log.WriteEntry and it’s returning an order number.

Does the error only occur when there aren’t any OrderDtl records? (Like when an Order is created, but no lines added.)

Or does it happen regardless of matching OrderDtl records existing?

Have you tried this syntax:

int  ohOrdNum = ttOrderHed.Select( r =>r.OrderNum).FirstOrDefault();
string  ohCompany = ttOrderHed.Select( r =>r.Company).FirstOrDefault();
// I'm sure it's pretty inefficient to make that call twice, as opposed to fetching the who record, then pulling the specific fields.  But I too am a LINQ noob.

int odOrdLine = Db.OrderDtl.Where( r =>r.Company == ohCompany && r.OrderNum == ohOrdNum).Select( r =>r.MyUDField_c ).DefaultIfEmpty(0).FirstOrDefault();

Then test odOrdLine for > 0. If it’s zero then no OrderDtl records selected.

1 Like

Correct.

That’s helpful, I’ll try that. So far, any use of DefaultIfEmpty() has given me

and I wasn’t using an int.

I’ll retreat and come back…

After you pointed out that DefaultIfEmpty(), causes an error, I’m starting to think that can’t be used unless the parameter to it is of the same type as od.

Or try …

DefaultIfEmpty(null)

1 Like

So you are saying that putting “b1 = od.OrderNum.ToString();” right after the “if (od != null)” will still trigger the NullReference error?

This doesn’t make sense considering OrderNum is a non nullable integer, the default value should be 0.

Do you still get the error running below code? Delete everything else.
If you don’t get an error I think your issue might be in the “do some Things” part.
If you get an error I don’t know what the hell is going on :stuck_out_tongue:

var oh = (from o in ttOrderHed
select o).FirstOrDeafault();

var od = (from d in Db.OrderDtl
where d.Company == oh.Company &&
d.OrderNum == oh.OrderNum
select d).FirstOrDefault();

if ( od != null)
{
var tmp = od.OrderNum;
}

1 Like

No - so far the errors are before if(od != null). However, I THINK I tried commenting out all the \\stuff but it’s worth checking again. Will come back.

So yup, this returns the error:

var oh = (from o in ttOrderHed select o).FirstOrDefault();

var od = (from d in Db.OrderDtl
               where d.Company == oh.Company &&
                          d.OrderNum == oh.OrderNum
               select d).FirstOrDefault();

if ( od != null)
   {
      var tmp = od.OrderNum;
   }

but this does not!

var oh = (from o in ttOrderHed select o).FirstOrDefault();


if ( oh != null)
   {
      var tmp = oh.OrderNum;
   }

And I haven’t been able to get a DefaultIfEmpty() to work anywhere.

Try breaking everything apart and putting logging between each operation.
Replace Log with Ice.Diagnostics.Log.WriteEntry or something else that you can use to debug.

Log(Get Header)
var oh = (from o in ttOrderHed select o).FirstOrDefault();
Log(Save Company)
string company = oh.Company;
Log(Save OrderNum)
int orderNum = oh.OrderNum;
Log(Temp OrderDtl Context)
var orderDtlCollection = Db.OrderDtl;
Log(Get Order Line)
var od = (from d in orderDtlCollection
where d.Company == company &&
d.OrderNum == orderNum
select d).FirstOrDefault();
Log(Finished)

1 Like

Does the following generate an error:

var oh = (from o in ttOrderHed select o).FirstOrDefault();

var od = (from d in Db.OrderDtl
               where d.Company == oh.Company &&
                          d.OrderNum == oh.OrderNum
               select d).FirstOrDefault();

if ( od != null)
   {
      //var tmp = od.OrderNum;
   this.PublishInfoMessage("od != null", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", "");
   }

That will see if it is the comparison of ( od != null), that is causing the error.

If no error, and you get the message box, that means that od is not null, and your issue is with the data in var od.

1 Like

In the end I got a lot of help from everyone on this, but the .Any() piece ended up being the solution.

I think I definitely proved to my satisfaction that on an order header with no lines, Epicor’s C# compiler will not let me test a null var, at least not this one. I can, however, test for .Any() being true. If anyone comes after me trying to solve this, by the way, I found a helpful article here: http://geekswithblogs.net/BlackRabbitCoder/archive/2011/04/21/c.net-little-wonders-any-and-all.aspx

In the end, this is working:

    if (ttOrderHed.Any())
      {
      var oh = (from o in ttOrderHed select o).FirstOrDefault();
      
      if  ((from d in Db.OrderDtl where d.OrderNum == oh.OrderNum select d).Any())
           {
             var od = (from d in Db.OrderDtl
                        where   d.OrderNum == oh.OrderNum
                        select  d).FirstOrDefault();
        
             var tmp = od.OrderNum;
             
             // do stuff
             
           } // end if OrderDtl
      
      } // end if ttOrderHed

Now I’ll see if I can put the //stuff back in without //stuffing it up…

Maybe your’e already aware of it… But your scheme (original and final), just ends up only grabbing the first OrderDtl record (if it exists).

Since your // do stuff was in the block where the OD record does exist, it is only going to know about that first record. Do you really mean to cycle through all the OrderDtl records for the order?

And others may disagree, but the “First” record isn’t always guaranteed to be one you think it is. I had a BAQ in V8 that used “First”, but it didn’t actually retrieve the first record in question. There’s probably a thread about it on here.

2 Likes

That’s a good point and thanks for the reminder. I used .ToList(); in the version that I first test on the SalesOrder.Update method, but it’s the first thing I took off when troubleshooting and I had forgetten.

We almost never have multiple releases but the system is designed to support them, obviously, so I need to loop through them.

Don’t worry I tried that too, it didn’t work. Don’t know why but solved it as noted :slightly_smiling_face:

I think that your problem was that OrderHed was not always delivering back a full record. you were not looking for the Modified or Added record. Becaues of this, you might get a blank record without any data.
you should look for the ttRecord with something in RowMod.
THEN, just in case, you should do a check to make sure that you found something… because your next query is currently assuming that there was an order header found, and simply uses the values from OH (which might be null).
this should correct this:

var oh = ttOrderHed.Where(o => o.RowMod != "");
if (oh != null) {
    var od = Db.OrderDtl.Where(d => d.Company == CompanyID && d.OrderNum == oh.OrderNum).FirstOrDefault();

    if (od != null) {
        //do some Things and Stuff
    }
}

BUT… if you simply want to know IF there was an order detail but you are not really going to do anything with it… you should use the “any” query as someone else already mentioned… you can also shorten down the amount of code by including the ANY query into the first if statement (as long as you check for the null first, it will not do the second check on an AND statement).

var oh = ttOrderHed.Where(o => o.RowMod != "");
if (oh != null && Db.OrderDtl.Any(d => d.Company == CompanyID && d.OrderNum == oh.OrderNum)) {
    //do some Things and Stuff
}
1 Like

I had a similar problem today, where I thought it should have been returning no results it was returning a nullable error, so what I ended up doing… puff… after a lot of hunting is

(where ttDtls is the list of results)

if (ttDtls.Count() > 0 )
{
/// do stuff here
}

It does a count of the results