Kinetic Code Camp - Bring your skills, or lack thereof.. :dumpster_fire:

Transactions

What are transactions?

In short, it’s a way to bind multiple programming instructions that must succeed or fail together.

Transactions are, ultimately, a feature of the database. A SQL transaction block lets you wrap any number of commands together into a single set. If any of the commands fail, ALL of the commands within this block can be rolled back. This is very important for data integrity. Imagine you were creating a PO, and there was a failure to add the row to POHeader table (e.g. you assigned the VendorName to the VendorNum field, which doesn’t like letters). You would not want the system to proceed to write the line records to PODetail. They would point to a PO number that doesn’t exist in the header table. This is the definition of an orphaned record.

images

The TransactionScope class extends this protection and functionality to the business layer as well. Failure of any method call within a transaction will roll back all changes within said transaction, even the ones that succeeded prior to the error.

When should you use a Transaction?

The glib answer is “every time the system nags you for one.” Note that it won’t do that every time, since many method calls create an internal transaction. If you’re doing something simple, that is, changing one field in one record (e.g. writing to a UD field), you can probably skip it if the system doesn’t demand it.

A better answer is “every time you’re doing something that depends on the success of previous and/or subsequent updates to work correctly”.

How do you use a Transaction?

When creating a function, there is a checkbox on the right side of the function tab labelled “Requires Transaction”. Check it and your whole function is now (mostly) protected. If you run into one failure, it’s going to try and roll back everything.

image

What if I’m comfortable with a little bit of failure?

Let’s say you’re building a batch job that’s going to loop through a whole bunch of records and try to update them. Any one of those updates can fail, but you want it to keep going because the next record is independent. The answer is to create a transaction for each iteration through your loop.

The basic template for such a function is below.

CallService<Erp.Contracts.SomeModuleSvcContract>(svc =>
{
    foreach(var thing in things)
    {
        try //So the whole thing doesn't die on a single error.
        {
            using (var txScope = IceContext.CreateDefaultTransactionScope())
            //Creates a Transaction for just this one iteration.
            {
                svc.MethodCall();
                svc.AnotherCall();
                txScope.Complete(); //Close out transaction
            }
        }
        catch (Exception e)
        { 
            ErrMsg += SomeKeyField + ":" + e.Message + Environment.NewLine;
            //ErrMsg would be an output on your function.
            //You could log any number of ways too.
            //This was just what I used on the code I copied this from, which was a batch process.
        }
    }
}

Note that if you’re going to declare a TxScope in your code, make sure to uncheck the box on the function tab. Use one or the other, not both. Avoid transactions within transactions.

6 Likes

THREE MONTHS LATER…

So, you’d all be proud: I hacked at some lambda notation all by myself (for this project). I sure don’t understand it, but I think I can use it. (Not all that different from a lot of other “skills” I have…)

@jtownsend, This pseudocode is perfect, by the way:

Something I discerned along the way: Count has different personalities.

  1. Count all rows of a table:
    ds.table.Count
    (Notice the lack of parentheses)

  2. Count only the rows of the table returned by the Where clause:
    ds.table.Where(x => x.field1 == value).Count()
    (Parentheses after Count but nothing inside)

  3. Sum a field of the data returned by the where clause:
    ds.table.Where(x => x.field1 == value).Sum(y => y.field2)
    (Parentheses after Sum and need lambda expression inside the parentheses)

I kept trying to do Count(x => x.something) a la #3. But that is not real.

(Yes #3 is exactly what @jtownsend posted earlier. It’s an example, not my example.)

4 Likes

Robert Carradine 80S GIF

4 Likes

Yeah, object.Count and object.Count() are slightly different things.

Count (no parens) is a property of the object, like the table name.

Count() is a LINQ method that first looks for an object.Count property and returns that if it finds it. If it doesn’t find a Count property it will iterate through all of the child objects (rows) of your called object (table) and return a count of everything it finds.

Does any of this make a practical difference? Nah…probably not in Epicor land. The main takeaway is that “no parens” == “property lookup”, while “yes parens” == “method call” (and therefore a more complex routine).

4 Likes

This is strangely a propos. I’m afraid to ask, but what movie is this?

3 Likes

Hint, you’re in it.

3 Likes

Well, the source code says Robert Carradine. So Google tells me it’s the Lizzie McGuire Movie.

Or Revenge of the Nerds.

I’m assuming Hillary Duff.

1 Like

The actual scene…

3 Likes