Function works from BPM and not from Schedule

Here is working code when I run this Function from a BPM. However, when I run it from the Schedule Function screen, it does not work. I do not have access to the logs, FYI.

var shipHeadList = Db.ShipHead.Where(S => S.Company == CompanyID && S.TrackingNumber == "").ToList();

foreach (var shipHead in shipHeadList)
{
	shipHead.TrackingNumber = shipHead.PackNum;
}

I assume CompanyID is assigned (or just a placeholder in your post for the static value).

Any way to write debugging info, like write to a file on the server - which you could then use the “Download File from Server” program.

First thing I’d write would be the length of shipHeadList to see if it is in your query expression. Then add a write within the foreach loop

I tried Hard Coding the Company ID (and even removing that filter), that did not solve it. I will see if I can get to any logs.

Well, I tried

Ice.Diagnostics.Log.WriteEntry("Stuff");

But the problem is I don’t have access to the ServerLog.txt file. Do you know how to write the the Company/Log directory without knowing the path on the server?

Additionally, I can send an email from within the loop and it sends fine both from a BPM and from the Scheduled Function. But still, the Scheduled function won’t update the DB.

Here is my current code, FYI:

var shipHeadList = Db.ShipHead.Where(S => S.Company == CompanyID && S.PackNum == 700016).ToList();

  foreach (var shipHead in shipHeadList)
  {
    string trackNum = DateTime.Now.ToString();
    shipHead.TrackingNumber = trackNum;
        
    var mailer = this.GetMailer(async: true);
    var message = new Ice.Mail.SmtpMail();
    message.SetFrom("test@test.com");
    message.SetTo("jason@jasonwoods.me");
    message.Subject="Test Subject";
    message.Body = shipHead.TrackingNumber; //This is always correct, but the value only gets saved in BPM.
    mailer.Send(message);
  }

Eureka!
I needed to add:

Db.SaveChanges();
3 Likes

I was about to say that you’re only updating the Tracking number element of the variable shipHead. Not the actual ShipHead DB table.

And I’m surprised that adding Db.SaveChanges() worked.

I don’t understand LINQ very well, but would have assumed that shipHeadList is a variable that is populated with data from the ShipHead DB table. And the variable shipHead is just one record from shipHeadList.

Unless those variables are just references, I don’t see changes to shipHead can propagate back to Db.ShipHead.

It would be like

Int i = 0;
Int j = i;
Int k;
k = 20;

And expecting j or i to equal 20 after setting k to 20.

Since the item is part of the Entity, it knows more than if it was a simple DataRow.
In a BPM, I would use Db.Validate but in this I guess I use Db.SaveChanges() instead.

Db.Validate is Epicor-ish way.
The standard for EF is Db.SaveChanges(). Since functions use the own kind of DB model, I decided to use an industry standard method name.

3 Likes

Maybe someday it will all be standard naming :smiley:

1 Like

And then a new standard will be proposed …

I still don’t follow this. The Entity you speak of, is the ShipHead table in the DB, the Db itself, or some other abstraction that is going over my head?

shipHeadList is a local variable that ends up being a List. But the elements in the list aren’t linked to the DB table, are they? Once that variable is set by that LINQ expression, the changes to the records in Db.ShipHead aren’t reflected in shipHeadList, correct?

Maybe a code example would be clearer.

var shipHeadList = Db.ShipHead.Where(S => S.Company == CompanyID && S.PackNum == 700016).ToList(); // say the TrackingNumber of Packer 700016 was "ABC-123"
foreach (var shipHead in shipHeadList)
  {
    // something that outputs shipHead.TrackingNumber happens
    // the output value is "ABC-123"
   }
// some code that takes a while to run,
// and while it does, some other processes changed 
// the TrackingNumber of Packer 700016 to "XYZ-987"

// the shipHeadList is looped through again
foreach (var shipHead in shipHeadList)
  {
    // something that outputs shipHead.TrackingNumber happens
    // the output value is ????
   }

Wouldn’t that second foreach loop also output “ABC-123”, and not “XYZ-987”?

Also, where did you put Db.SaveChanges(); ??

And your code in post #6 (with the Db.SaveChanges(); added) actually updated the ShipHead table?

I likely will be wrong on my terminology, but “Db” is an object that is populated from the database, but is not the database itself. In this case, I would assume it is an entity, but I could be wrong there.

It is an object that represents a context of the database. It’s powered by the data model regen. That creates a single DLL that represents the whole DB that’s why it takes so long. Not sure exactly how in functions it’s scoped down the way it is but I assume that’s epimagic.

this.Db is not an entity, it is model or context.
It is not populated actually, it is a tool to get data from DB and put them back.

An item like OrderHed that you can get with Db.OrderHed.First() is an entity.

Also, I forgot explain why your code worked in BPM but not in the case of scheduling (also it may not work in the case of REST call).

EFx DB context is not ERP context, but currently it is a wrapper around ERP one.
When you call function from the BPM, then it works inside the method call, and in the most cases service method calls Db.Validate and this call stores your changes too.

However, proper use of the Db in EFx requires explicit Db.SaveChanges call independently from the future use of the funciton.

1 Like