Transaction Scope issues in Function to update table

I know what my issue is, but I don’t know how to solve it.

I am trying to use a SvcContract in function C# block to update a tableset. This takes a Ice.IceDataContext, but the “Require Transaction” Db object is of type EFx.LibraryID.Implementation.ILibraryContext instead.

So I declared a context using Ice.Services.ContextFactory.CreateContext()

However, while I can pass this into the SvcContract, if I enable “Require Transaction” I get the complaint about “The underlying provider failed on Open.”

This is likely because it is trying to transact using the included tables and assemblies and references in my Function, and so it is open in both the function’s context and the context I created. (this is my best guess).

If I don’t use Require Transaction, it does not find any records with my declared service.

However, the failed to Open only occurs if I search out my table records using the functions’ context of Db.Table.Where(…

If I change it to my context.Table.Where(… It brings no records error.

I don’t know if I’m explaining this well enough haha, transaction scopes are a little strange to me.

I’ve also tried using the Erp.Internal.Lib.CCredChk.dll as an assembly for .Validate as I saw in another post… (What’s the difference between the IceDataContext context.Validate and the CCredChk’s validate? Should I use both? Just CCredChk? Just the IceDataContext?)

I also tried declaring my own transaction scope, but I kept getting an error about System.Transaction.Local not being included and I couldn’t find it to include it as a reference.

Maybe if you see the relevant code it will be more helpful haha.

Assemblies
image

Tables
image

Services
image

var context = Ice.Services.ContextFactory.CreateContext<ErpContext>(); 

Ice.Contracts.UD24SvcContract UD24Svc = Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD24SvcContract>(context);

var list = Db.UD24.Where(I => I.Key1 == "TIStore").ToList();

if (list != null && list.Count > 0)
  foreach (var row in list)
  {
      UD24Tableset tableset = UD24Svc.GetByID(row.Key1, row.Key2, row.Key3, row.Key4, row.Key5);
      
      tableset.UD24[0].RowMod = "D";
      
      UD24Svc.Update(ref tableset);
      
      context.Validate(row);
  }

Hopefully I made this clear enough, not even sure what to ask other than “how do I make the table update”.

I know I could do it in a widget but I have to use the same service contract later to do some complex new rows to UD24 alongside function calls, so… widgets are off limits :smiley:

Ok, we need to teach you to use CallService. :slight_smile:

and that is definitely not needed.

The simplest:
(It needs some null sanity checks or error catching.)

var list = Db.UD24.Where(I => I.Key1 == "TIStore").ToList();

if (list != null && list.Count > 0)
{
    CallService<UD24SvcContract>(ud24Svc =>
    {
        foreach (var row in list)
        {
            UD24Tableset tableset = ud24Svc.GetByID(row.Key1, row.Key2, row.Key3, row.Key4, row.Key5);
            
            tableset.UD24.First().RowMod = "D";
            
            ud24Svc.Update(ref tableset);
        }  
    });
}
1 Like

This is the key to Efx happiness

1 Like

Not just limited to functions, CallService can be used in bpm as well

1 Like

Amazing new tool. Thanks so much Kevin!

For anyone else wondering, also needs Require Transaction off if you get “The underlying provider failed on Open.” error.

Didn’t need it in my uBAQ version either, but I was grasping at straws haha

1 Like

Thanks for this Kevin! (another great code block I stole)

One downside of the GetByID BO is that it is company specific. I had a function to import employees to a UD table for two different companies and I couldn’t figure out why I got “record not found” errors. This was because I submitted on the first company code and the others were not visible for the second company. I ended up splitting this into two functions and submit them in each company.

1 Like