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!
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.
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.
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
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();
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.
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)
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.
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.
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.
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
}
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