BPM Tables Practice

Hello, i’m in need of some input on LINQ coding for BPMs. I’m currently building a large BPM that uses a UDTable, over and over again and updates it at various points. Let’s say I use the code:

Ice.Tables.UD20 UD20;

using (var txScope = IceContext.CreateDefaultTransactionScope())
{
if (Thing == “WhatIWant”)
{
foreach (var UD20_iterator in (from UD20_Row in Db.UD20
where UD20_Row.Company == Session.CompanyID &&
UD20_Row.Key1 == “asdf”)
select UD20_Row))
{
UD20 = UD20_iterator;
UD20.Key1 = “asdf”;
Db.Validate();
}
}
txScope.Complete();
}

Simple enough, but let’s say I use UD20 twice here for various reasons. I know I’m oversimplifying, but i’m doing it on purpose because it’s hard for me to explain.

Ice.Tables.UD20 UD20;

using (var txScope = IceContext.CreateDefaultTransactionScope())
{
if (Thing == “WhatIWant”)
{
foreach (var UD20_iterator in (from UD20_Row in Db.UD20
where UD20_Row.Company == Session.CompanyID &&
UD20_Row.Key1 == “asdf”)
select UD20_Row))
{
UD20 = UD20_iterator;
UD20.Character01 = “xyz”;
Db.Validate();
}
}

if (Thing == "a different thing")
{
	foreach (var UD20_iterator in (from UD20_Row in Db.UD20
								 where UD20_Row.Company == Session.CompanyID &&
								 UD20_Row.Key1 == "doa")
								 select UD20_Row))
	{
	  UD20 = UD20_iterator;
	  UD20.Character01 = "qwer";
	  Db.Validate();
	}
}
txScope.Complete();

}

In the code above, i’m using the “UD20” variable twice. I believe (but i’m not sure) this will cause conflicts eventually. I’m already using the variable for the data retrieved on the query for “asdf” and then I use it again for the data for “doa”. I’m sure my simplistic example wont cause issues, but what if I’m using my Ice.Tables.UD20 UD20 variable 10 or more times in my code? Is there a simple way to clear the UD20 variable each time before I use it? Do I need to clear it? Am I worrying about something I shouldn’t? :joy: I want to avoid having to do setup multiple variables as the same thing. Something like this:

Ice.Tables.UD20 UD20a;
Ice.Tables.UD20 UD20b;

using (var txScope = IceContext.CreateDefaultTransactionScope())
{
if (Thing == “WhatIWant”)
{
foreach (var UD20_iterator in (from UD20_Row in Db.UD20
where UD20_Row.Company == Session.CompanyID &&
UD20_Row.Key1 == “asdf”)
select UD20_Row))
{
UD20a = UD20_iterator;
UD20a.Character01 = “xyz”;
Db.Validate();
}
}

if (Thing == "a different thing")
{
	foreach (var UD20_iterator in (from UD20_Row in Db.UD20
								 where UD20_Row.Company == Session.CompanyID &&
								 UD20_Row.Key1 == "asdf")
								 select UD20_Row))
	{
	  UD20b = UD20_iterator;
	  UD20b.Character01 = "asdf";
	  Db.Validate();
	}
}
txScope.Complete();

}

I hope this makes sense. Thanks for your input. I’m hoping i’m just being silly.

A few different things you can do here and some may be version dependent (i.e. using Functions in 10.2.500). You can do something like this - basically one function that you can call anywhere in the code and pass in the value and the key you want to update. Just a quick example. You could also even pass in the name of the column you wanted to update (i.e. Character01, Character02, whatever_c). You could also us the BO to perform the updates as well vs. this method.

/*** Functions ***/
Func<string, string, bool> updateUD = (in_key, in_value) => {
  bool UpdateSuccess = true;
  foreach (var UD108 in (from UD108Rows in Db.UD108 where UD108Rows.Company == callContextClient.CurrentCompany && UD108Rows.Key1 == in_key
  select UD108Rows))
  {
      UD108.Character01 = in_value;
  
  }
  return UpdateSuccess;
};

/*** Main Logic ***/

updateUD("22","Gold");  //updateUD(Key_To_Update,Value)
updateUD("7003", "Red"); //updateUD(Key_To_Update,Value)
2 Likes

I have finally gotten so that I can write nearly any query “from my head”… There are good ways and bad ways.
Here is your code with how I would do this.

  1. your concern about reusing the variables is not really a problem IF you have them inside curly brackets… any new variables you define are disposed of at the end of the curly bracket. C# is very good about telling you if you tried to redeclare something that is already declared.
  2. you do NOT need to declare the “ice.tables.ud20” line at the top… when you do the query it automatically does this.
  3. It is best to do the query first, and THEN do a foreach with the results. Note that you also do not need to use that old-style “iterator” logic where you assign an iterator, and then reassign it to a second variable. This came from old conversions from ABL.
  4. OH, and I converted the queries into Lambda statements because the code becomes a lot shorter… Yes, these short one-line statements return the same exact thing as your 4 line statement. Also, CompanyID is the same shortcut to Session.CompanyID.
//Ice.Tables.UD20 UD20; <-This is not needed

using(var txScope = IceContext.CreateDefaultTransactionScope()) {

    if (Thing == “WhatIWant”) {        
        var ud20RowsToUpdate = Db.UD20.Where(x => x.Company == CompanyID && x.Key1 == "asdf");
        foreach (var ud20 in ud20RowsToUpdate) {
            UD20.Character01 = “xyz”;
            Db.Validate();
        }
    }

    if (Thing == "a different thing") {
        var ud20RowsToUpdate = Db.UD20.Where(x => x.Company == CompanyID && x.Key1 == "doa");
        foreach (var ud20 in ud20RowsToUpdate) {
            UD20.Character01 = "qwer";
            Db.Validate();
        }
    }
    txScope.Complete();
}

ALSO… lets say that you wanted to do multiple queries and reuse variables, but they were not inside if statements like above… here is what it would look like… simply leave off the “var” at the beginning of the second query:

using(var txScope = IceContext.CreateDefaultTransactionScope()) {

    var ud20RowsToUpdate = Db.UD20.Where(x => x.Company == CompanyID && x.Key1 == "asdf");
    foreach (var ud20 in ud20RowsToUpdate) {
        UD20.Character01 = “xyz”;
        Db.Validate();
    }

    ud20RowsToUpdate = Db.UD20.Where(x => x.Company == CompanyID && x.Key1 == "doa");
    foreach (var ud20 in ud20RowsToUpdate) {
        UD20.Character01 = "qwer";
        Db.Validate();
    }

    txScope.Complete();
}
3 Likes

No body likes a showoff… :wink:

Tim,

Regarding #3, why is it best to do the query first then the foreach? Not questioning you, just trying to expand my knowledge…

Thanks.
Kevin Simon

Some tips from @Bart_Elia

BPMExample.docx (233.4 KB)

4 Likes

You guys are all great! Thank you; I’m happy I overthought it! I was fearful of having to go back and add an absurd amount of variables. I’m in love with the function route! Fantastic stuff for heavy hitting BPMs.

I LOVE this!

Just a couple more variations of what could be used. The transaction scoping could be whatever you wanted.

using(var txScope = IceContext.CreateDefaultTransactionScope()) {
	// Assumes Key and Value was already declared
    var ud20RowsToUpdate = Db.UD20.Where(ud => ud.Company == CompanyID && ud.Key1 == Key).ToList(); // ToList for query execution
	// var ud20RowsToUpdate = Db.UD20.Where(ud => ud.Company == CompanyID && ud.Key1 == Key); // Deferred Query Execution Method if you want
	Key = "asdf"; 
	Value = "xyz";
	ud20RowsToUpdate.ForEach(ud => { ud.Character01 = Value;});
	// ud20RowsToUpdate.ToList().ForEach(ud => { ud.Character01 = Value;});  Used with Deferred Query Execution Method from above
	Db.Validate();
	
	Key = "doa"; 
	Value = "qwer";
	ud20RowsToUpdate.ForEach(ud => { ud.Character01 = Value;});
	Db.Validate();
	txScope.Complete();
}

Another way

Func<string, string, bool> updateUD20 = (in_Key, in_Value) => {
  var ud20RowsToUpdate = Db.UD20.Where(ud => ud.Company == CompanyID && ud.Key1 == in_Key).ToList();
  ud20RowsToUpdate.ForEach(ud => { ud.Character01 = in_Value;});
  return true; // Could be set on try / catch to show update error
};

/*** Main Logic ***/
using(var txScope = IceContext.CreateDefaultTransactionScope()) {
  // Assumes Key and Value was already declared
  
  updateUD20("asdf","xyz"); 
  Db.Validate();
  
  updateUD20("doa","qwer");
  Db.Validate();
  txScope.Complete();
}
1 Like

Hey… if I said “I figured it out in a day”… THAT would be showing off… It took me months to get my head wrapped around this vs the old way we did this in Progress ABL code. hahah :wink:

2 Likes

The “Bart Files” has a nice ring to it…Experts corner perhaps?

Dang that was 5 years ago.