· This scenario definitely warrants the need for a fix from Microsoft or perhaps a more flexible mechanism within AIF to pull-and-push data from-and-into MS Dynamics AX that doesn’t rely on a rebuilds of CIL.
X++ |
class DynamicScriptClass { } |
[AifCollectionTypeAttribute('return', Types::String), SysEntryPointAttribute(true)] publicstr runScript(str _script) { XppCompiler xpp; ExecutePermission perm; str result; ; try { perm = new ExecutePermission(); if (perm != null) { perm.assert(); xpp = new XppCompiler(); if (xpp.compile(_script)) { try { result = xpp.execute(); return result; } catch (Exception::Error) { return"Error: execution failure, please check script.\n" + xpp.errorText(); } } else { return"Error: compilation failure, please check script.\n" + xpp.errorText(); } CodeAccessPermission::revertAssert(); } else { return"Error: no permission to execute scripts."; } } catch (Exception::Error) { return"Error: general exception in routine, please check script."; } return result; } |
· The class requires code-access-security and as a result needs to be set to run on “Server” only. The “true” setting on the “SysEntryPointAttribute” will perform authorisation checks on the calling user for all tables accessed by the dynamic code.
· Theoretically, the “xpp.execute()” method will return an “anytype”, however, on experimentation with this, I found it very problematic. It was far safer to return a string and then cast it to the desired type.
C# |
namespace ConsoleApplication { classProgram { staticvoid Main(string[] args) { // Aif Service Client DynamicScriptClassClient client = newDynamicScriptClassClient(); // Create an instance of the CallContext class. CallContext context = newCallContext(); // dynamic script string str1 = ""; str1 = "str dynamicScript()\n"; str1 += "{\n"; str1 += " CustTable objCustTable;\n"; str1 += " ;\n"; str1 += " select * from objCustTable where objCustTable.AccountNum == 'C1-0000001';\n"; str1 += " return strFmt('%1',objCustTable.xml());\n"; str1 += "}\n"; // attempt remote execution try { Console.WriteLine("Executing:\n" + str1); string result = client.runScript(context, str1); Console.WriteLine("result:\n" + result); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine(ex.Message + ": " + ex.StackTrace); Console.ReadKey(); } } } } |
C# |
namespace ConsoleApplication { classProgram { staticvoid Main(string[] args) { // Aif Service Client DynamicScriptClassClient client = newDynamicScriptClassClient(); // Create an instance of the CallContext class. CallContext context = newCallContext(); // dynamic script string str1 = ""; str1 = "str dynamicScript()\n"; str1 += "{\n"; str1 += " ExchangeRate objExchangeRate;\n"; str1 += " objExchangeRate.initValue();\n"; str1 += " objExchangeRate.ExchangeRate = 139.35;\n"; str1 += " objExchangeRate.ValidFrom = today();\n"; str1 += " objExchangeRate.ValidTo = maxDate();\n"; str1 += " objExchangeRate.ExchangeRateCurrencyPair = (select RecId from ExchangeRateCurrencyPair where ExchangeRateCurrencyPair.FromCurrencyCode == 'EUR' && ExchangeRateCurrencyPair.ToCurrencyCode == 'AUD').RecId;\n"; str1 += " objExchangeRate.insert();\n"; str1 += " return 'Done';\n"; str1 += "}\n"; // attempt remote execution try { Console.WriteLine("Executing:\n" + str1); string result = client.runScript(context, str1); Console.WriteLine("result:\n" + result); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine(ex.Message + ": " + ex.StackTrace); Console.ReadKey(); } } } } |
· If you enable “logging” on the integration port, you can track all the scripts passing between systems. This helps identify problems and audits data exchanges. The context of the “caller” will be used as the UserId that appears in the AIF tracking log.
<bindingname="NetTcpBinding_DynamicScriptClass"closeTimeout="00:01:00" openTimeout="00:01:00"receiveTimeout="00:10:00"sendTimeout="00:01:00" transactionFlow="false"transferMode="Buffered"transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard"listenBacklog="10"maxBufferPoolSize="524288" maxBufferSize="65536"maxConnections="10"maxReceivedMessageSize="65536"> <readerQuotasmaxDepth="32"maxStringContentLength="2147483647"maxArrayLength="16384" maxBytesPerRead="4096"maxNameTableCharCount="16384" /> <reliableSessionordered="true"inactivityTimeout="00:10:00" enabled="false" /> <securitymode="Transport"> <transportclientCredentialType="Windows"protectionLevel="EncryptAndSign" /> <messageclientCredentialType="Windows" /> </security> </binding> |