Method Directive Error, why does the 2nd one not work?

I have been chasing this one for a couple of weeks now, standard support directed me here.

I have 2 BPMs the first updates the TaxRates file from Vertex Data. That one works.

foreach (var County in Db.UD39.Where(County =>
                          County.Company == callContextClient.CurrentCompany &&
                          County.Key1    == callContextBpmData.ShortChar01 &&
                          County.Key2    == callContextBpmData.ShortChar02))
{
  string tKey = County.Key1 + County.Key2 + "0000";

// this.PublishInfoMessage(tKey, Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual,"","");

  if (callContextBpmData.Checkbox02)
  {   // Use Tax rate has changed
    if (callContextBpmData.Checkbox08)
    { // Eff Date change, make new record
      using (var txScope = IceContext.CreateDefaultTransactionScope())
      {
        var Rate           = new Erp.Tables.TaxRate();
        Rate.Company       = County.Company;
        Rate.TaxCode       = tKey;
        Rate.RateCode      = "DEF";
        Rate.EffectiveFrom = County.Date02;
        Rate.RateType      = 0;
        Rate.TaxPercent    = County.Number02;
        Rate.CurrencyCode  = "USD";
        Rate.DeductPercent = 100;
        Db.TaxRate.Insert(Rate);
        Db.Validate();
        txScope.Complete();
      } // using txScope
    } else
    { // Update existing rate
      using (var txScope = IceContext.CreateDefaultTransactionScope())
      {
        foreach (var Rate in Db.TaxRate.Where(Rate =>
                                Rate.Company       == County.Company &&
                                Rate.TaxCode       == tKey &&
                                Rate.RateCode      == "DEF" &&
                                Rate.EffectiveFrom == County.Date02))
        {
          Rate.TaxPercent = County.Number02;
        } // foreach Rate 
        Db.Validate();
        txScope.Complete();
      } // using TxScope
    } // if Eff Date Changed
  } // if Rate changed
}
Db.Validate();[/code]

Then a VERY similar BPM to update the TaxRgnSalesTax file, which does NOT work.

foreach (var County in Db.UD39.Where(County =>
                          County.Company == callContextClient.CurrentCompany &&
                          County.Key1    == callContextBpmData.ShortChar01 &&
                          County.Key2    == callContextBpmData.ShortChar02))
{
  string tKey = County.Key1 + County.Key2 + "0000";
  if (callContextBpmData.Checkbox05)
  { // if an override changed
    decimal d = 0;
    if (County.Number02 > 0)
    { // the rate must be more than 0 before Epicor can override it.
      if (County.CheckBox02)
        d = ((County.Number02 - County.Number12) / County.Number02);
    } // if Use Tax > 0
    using (var txScope = IceContext.CreateDefaultTransactionScope())
    {
      foreach (var L in Db.TaxRgnSalesTax.Where(L =>
                              L.Company == County.Company &&
                              L.TaxCode == tKey))
      {
          L.ExemptPercent = d * 100;
          L.ExemptType = 1;
      } // foreach
      Db.Validate();
      txScope.Complete();
    } // using TxScope
  } // if an override changed
}

The 2nd BPM gets a server side error:

Server Side Exception

BPM runtime caught an unexpected exception of 'EntityException' type.
See more info in the Inner Exception section of Exception Details.

Exception caught in: Epicor.ServiceModel

Error Detail 
============
Description:  BPM runtime caught an unexpected exception of 'EntityException' type.
See more info in the Inner Exception section of Exception Details.
Inner Exception:  The partner transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D025)
The partner transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D025)
Program:  EntityFramework.dll
Method:  EnlistTransaction
Original Exception Type:  EntityException
Framework Method:  A001_CustomCodeAction
Framework Line Number:  0
Framework Column Number:  0
Framework Source:  A001_CustomCodeAction at offset 2217 in file:line:column <filename unknown>:0:0

Client Stack Trace 
==================
   at Epicor.ServiceModel.Channels.ImplBase`1.ShouldRethrowNonRetryableException(Exception ex, DataSet[] dataSets)
   at Ice.Proxy.BO.UD39Impl.Update(UD39DataSet ds)
   at Ice.Adapters.UD39Adapter.OnUpdate()
   at Ice.Lib.Framework.EpiBaseAdapter.Update()
   at Script.UD40Form_BeforeToolClickForUD39(Object sender, BeforeToolClickEventArgs args)
   at Ice.Lib.Framework.BeforeToolClickEventHandler.Invoke(Object sender, BeforeToolClickEventArgs e)
   at Ice.Lib.Framework.EpiBaseForm.OnBeforeToolClick(String tKey, ToolClickEventArgs ea)
   at Ice.Lib.Framework.EpiBaseForm.handleToolClick(String tKey, ToolClickEventArgs ea)

Inner Exception 
===============
The underlying provider failed on EnlistTransaction.

Inner Exception 
===============
The partner transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D025)

Inner Exception 
===============
The partner transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D025)

Any ideas as to why the 2nd one generates that error? It occurs on
foreach (var L in Db.TaxRgnSalesTax.Where(L =>
L.Company == County.Company &&
L.TaxCode == tKey))
(I used info messages to figure that out)
Why is that creating an error when a very similar statement does not?

Sorry for the wall of text, the jist of it can be summed up 7 lines up :smiley:

1 Like

Hey @MLamkin, welcome to the forum. To format a code block you can use 3 back ticks before and after the code. That will help someone be able to read your code and hopefully help you better.

With this example, remove the / and put the 3 ticks on their own line before and after your code.

/ ```
some code here
/ ```
1 Like

Hmm, I tried that and I think the tag looked better :slight_smile:

remove the / I can’t type it without the slash (to show you) or it makes a code block.

Ah, much better. Thank you! :slight_smile:

1 Like

I don’t know for sure what would be causing that specific error, but one thing I do notice is the last Db.Validate() that you have in the code. You are calling that within your other blocks as well, so I’m wondering if that is causing you problems.

edit I was looking at them in the wrong order. The first one is the one with the extra Db.Validate(). Seems odd, but maybe that’s the difference.

Don’t loop on the Db object. This causes locks and all sorts of gnarly stuff.
Change your code so that the LINQ queyr runs (toList) first then loop through the result.

//Original
foreach (var County in Db.UD39.Where(County =>
                          County.Company == callContextClient.CurrentCompany &&
                          County.Key1    == callContextBpmData.ShortChar01 &&
                          County.Key2    == callContextBpmData.ShortChar02))
{
//Change to 
var listOfStuff = Db.UD39.Where(County =>
                          County.Company == callContextClient.CurrentCompany &&
                          County.Key1    == callContextBpmData.ShortChar01 &&
                          County.Key2    == callContextBpmData.ShortChar02).ToList();
foreach (var County in listOfStuff)
{
5 Likes

Same for this guy

foreach (var L in Db.TaxRgnSalesTax.Where(L =>
                              L.Company == County.Company &&
                              L.TaxCode == tKey))
      {
1 Like

This should be the solution. I’ve never seen a foreach around a linq query. The linq shouldn’t need one if you’re doing it right :slight_smile:

1 Like

I have but it is ALWAYS (read almost always) a horrible idea. Particularly like (with the example above) when you are writing and creating transactions. Creates whole row level locks and gnarly gremlins just dont do it! Like you said there’s no need.

2 Likes

Okay well I’ll consider myself lucky that this is the first time seeing it haha :sweat_smile:. I love this

Creates whole row level locks and gnarly gremlins just dont do it! 
1 Like

Thank you so much, I never knew about this. Seems like I may need to change some other BPMs too. :smiley:

2 Likes

@josecgomez, can you still update the table if you use a “ToList()”?
I know this should be obvious to me, but I am needing to update the PartPlant table recursively and am running into issues. I will ask separately for my specific issue when I have more details. :slight_smile:

1 Like

Yes you can absolutely still update the record it just closes the cursor. In SQL but it keeps the record set so you can update it.

This is huge if I understand correctly.
Does this mean I can change this:

using (var txScope = IceContext.CreateDefaultTransactionScope())
{
  foreach (var tt in ttResults.Where(tt => tt.Updated())
  {
    foreach (var I in Db.InvcHead.Where(I => I.Company == tt.Company && I.InvoiceNum == tt.InvcDtl_InvoiceNum))
    {
      q.DocumentPrinted = false;
    }
  }
  Db.Validate();
  txScope.Complete();
}

To this:

using (var txScope = IceContext.CreateDefaultTransactionScope())
{
  foreach (var tt in ttResults.Where(tt => tt.Updated())
  {
	var invList = Db.InvcHead.Where(I => I.Company == tt.Company && I.InvoiceNum == tt.InvcDtl_InvoiceNum).ToList();
    foreach (var I in  invList)
    {
      q.DocumentPrinted = false;
    }
  }
  Db.Validate();
  txScope.Complete();
}

Will I get the same results without the ugly locking (even deadlocks)?

1 Like

Yes!

1 Like

Well now I’ve got some work ahead of me!

1 Like

Me too, I have used a number of loops within loops in many BPMs, though most don’t use the .Where() concept. I do think that the ones that select new {field1, field2} were probably efficient. I only tried the .Where clause when I saw it in your code :smiley: But it didn’t work with the structure I used either. I’m glad it works with this new concept :slight_smile:
It should let me sign off on the Vertex info being passed to Epicor project :slight_smile:

You may want to consider Db.Validate(YourSpecificRow).