Labor BPM C#

I have the following code on Labor.Update - Pre-Processing.

This executes fine during the day but at 17:00pm when operators book out I receive deadlock errors.
Is there something wrong in my code or can it be improved to avoid the deadlocks?

    /* TO DO: replace object variables with typed variables. Add reference if necessary.*/
object MESSAGE_WARN = null;
decimal doneqty = decimal.Zero;
bool errqty = false;
decimal remqty = decimal.Zero;
string msg = string.Empty;
Erp.Tables.LaborDtl LaborDtl;
Erp.Tables.JobOper JobOper;
Erp.Tables.OpMaster OpMaster;
var ttLaborDtl_xRow = (from ttLaborDtl_Row in ttLaborDtl
                       where ttLaborDtl_Row.Company == Session.CompanyID && !String.IsNullOrEmpty(ttLaborDtl_Row.RowMod)
                       select ttLaborDtl_Row).FirstOrDefault();
if (ttLaborDtl_xRow != null)
{
    if (!ttLaborDtl_xRow.EndActivity)
    {
        LaborDtl = (from LaborDtl_Row in Db.LaborDtl
                    where LaborDtl_Row.Company == Session.CompanyID && LaborDtl_Row.ActiveTrans == true && string.Compare(LaborDtl_Row.EmployeeNum, ttLaborDtl_xRow.EmployeeNum, true) == 0
                    select LaborDtl_Row).FirstOrDefault();
        if (LaborDtl != null)
        {
            if (string.Compare(ttLaborDtl_xRow.LaborType, "I", true) == 0)
            {
                msg = "Indirect \r\nYou are already booked onto Job" + LaborDtl.JobNum + "\r\nAssembly " + System.Convert.ToString(LaborDtl.AssemblySeq) + "\r\nOperation " + System.Convert.ToString(LaborDtl.OprSeq) + "";
CallContext.Current.ExceptionManager.AddBLException(msg);
            }
            else
            {
                if (string.Compare(LaborDtl.LaborType, "I", true) == 0)
                {
                    msg = "You are already booked onto Indirect";
CallContext.Current.ExceptionManager.AddBLException(msg);
                }
                else
                {
                    JobOper = (from JobOper_Row in Db.JobOper
                               where string.Compare(JobOper_Row.Company, ttLaborDtl_xRow.Company, true) == 0 && string.Compare(JobOper_Row.JobNum, ttLaborDtl_xRow.JobNum, true) == 0 && JobOper_Row.AssemblySeq == ttLaborDtl_xRow.AssemblySeq && JobOper_Row.OprSeq == ttLaborDtl_xRow.OprSeq
                               select JobOper_Row).FirstOrDefault();
                    if (JobOper != null)
                    {
                        OpMaster = (from OpMaster_Row in Db.OpMaster
                                    where string.Compare(OpMaster_Row.Company, JobOper.Company, true) == 0 && string.Compare(OpMaster_Row.OpCode, JobOper.OpCode, true) == 0
                                    select OpMaster_Row).FirstOrDefault();
                        if (OpMaster != null)
                        {
                            if (!(bool)OpMaster["CheckBox01"])
                            {
                                msg = "Multiple Jobs \r\nYou are already booked onto Job" + LaborDtl.JobNum + "\r\nAssembly " + System.Convert.ToString(LaborDtl.AssemblySeq) + "\r\nOperation " + System.Convert.ToString(LaborDtl.OprSeq) + "";
CallContext.Current.ExceptionManager.AddBLException(msg);
                            }
                        }
                    }
                }
            }
        }
    }
    else
    {
        if ((ttLaborDtl_xRow.LaborQty - System.Convert.ToInt32(ttLaborDtl_xRow.LaborQty)) != 0)
        {
            errqty = true;
        }
        if ((ttLaborDtl_xRow.ScrapQty - System.Convert.ToInt32(ttLaborDtl_xRow.ScrapQty)) != 0)
        {
            errqty = true;
        }
        if ((ttLaborDtl_xRow.DiscrepQty - System.Convert.ToInt32(ttLaborDtl_xRow.DiscrepQty)) != 0)
        {
            errqty = true;
        }
        if (errqty)
        {
CallContext.Current.ExceptionManager.AddBLException("Enter Quantity with no decimals please");
        }
        doneqty = ttLaborDtl_xRow.LaborQty + ttLaborDtl_xRow.ScrapQty + ttLaborDtl_xRow.DiscrepQty;
        JobOper = (from JobOper_Row in Db.JobOper
                   where string.Compare(JobOper_Row.Company, ttLaborDtl_xRow.Company, true) == 0 && string.Compare(JobOper_Row.JobNum, ttLaborDtl_xRow.JobNum, true) == 0 && JobOper_Row.AssemblySeq == ttLaborDtl_xRow.AssemblySeq && JobOper_Row.OprSeq == ttLaborDtl_xRow.OprSeq
                   select JobOper_Row).FirstOrDefault();
        if (JobOper != null)
        {
            remqty = JobOper.RunQty;
            foreach (var LaborDtl_iterator in (from LaborDtl_Row in Db.LaborDtl
                                               where string.Compare(LaborDtl_Row.Company, ttLaborDtl_xRow.Company, true) == 0 && string.Compare(LaborDtl_Row.JobNum, ttLaborDtl_xRow.JobNum, true) == 0 && LaborDtl_Row.AssemblySeq == ttLaborDtl_xRow.AssemblySeq && LaborDtl_Row.OprSeq == ttLaborDtl_xRow.OprSeq
                                               select LaborDtl_Row))
            {
                LaborDtl = LaborDtl_iterator;
                doneqty = doneqty + (LaborDtl.LaborQty + LaborDtl.ScrapQty + LaborDtl.DiscrepQty);
                remqty = remqty - (LaborDtl.LaborQty + LaborDtl.ScrapQty + LaborDtl.DiscrepQty);
            }
            if (doneqty > JobOper.RunQty)
            {
                msg = "Booking will exceed Required Quantity of " + System.Convert.ToString(System.Convert.ToInt32(JobOper.RunQty)) + "\r\nBalance Remaining is " + System.Convert.ToString(System.Convert.ToInt32(remqty)) + "";
CallContext.Current.ExceptionManager.AddBLException(msg);
            }
            if (doneqty == JobOper.RunQty)
            {
                ttLaborDtl_xRow.OpComplete = true;
                ttLaborDtl_xRow.Complete = true;
            }
        }
    }
}

Couple things you can try. I see two low hanging fruit, though I haven’t looked at the code in detail. First I would store your queries to variables then loop them do not don’t loop them directly. Also make sure you are using With statements just like you would in SQL to control the locking when you need it and when you don’t need it.

1 Like

Obviously this would be a challange to duplicate the issue; however, if it were me I would attempt to wrap them into a transaction scope and set the isolation level to read uncommitted.

Keep in mind it will be reading uncommitted rows :smile: my rationale is that since it happens when there are multiple users submitting labor hours at the end of their shift.

1 Like