Quantcast
Channel: Microsoft Dynamics 365 Community
Viewing all articles
Browse latest Browse all 10657

Possibly the last service you’ll need?

$
0
0
 
If you’re a developer working on AIF in a shared environment then you’ll invariably run into the problem of not being able to deploy your new integration simply because the incremental CIL won’t generate. This is not because of anything you’ve done… it’s down to work-in-progress elsewhere or synchronisation problems or just simply bugs from other peoples code. This is an annoying restriction to say the least and often requires you to either wait patiently or force the issue and fix other people’s problems (grrrrr!)
 

·         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.

 
There are also other development scenarios where the complete integration requirement is unknown and it needs to be prototyped with the customer and developed in an “agile” fashion. The overhead of generating custom-classes, data-contracts, service-interfaces, CIL rebuilds and AIF port deployments (with the standard shutdown and startup of all services every time) is simply not viable in a shared development environment because you lose too much time and end-up impacting everyone else you’re working with.
 
I decided to overcome the issue by implementing “one” service that would satisfy all of my needs. Yes, you heard right… “1”. I took the concept that I started 6 months ago with the dynamic script class that I wrote [http://community.dynamics.com/ax/b/dynamicsax_wpfandnetinnovations/archive/2013/03/10/limitations-of-the-office-connector.aspx#.UjQ-fMbkuHM] and extended it to work in AIF. This class uses the “runBuf” and “evalBuf” functionality to compile and execute dynamic X++ code. The problem with these functions is that they are not supported in CIL [http://msdn.microsoft.com/en-us/library/hh397320.aspx].
 
So instead, I had to switch my code to use the XppCompiler class [http://msdn.microsoft.com/en-us/library/xppcompiler.aspx]. The end result was something extremely similar to my original class:
 
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.

 
The method accepts a string which should be a well-formed X++ code-block. This code-block should be wrapped up as a function that returns a “string” value.
 

·         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.

 
An initial attempt is made to “compile” the code. If compilation fails then an exception is returned with a “compiler-error-message” to the caller. If the code compiles then an attempt is made to “execute” it. If execution fails then an exception is returned with an “execution-error-message” to the caller. The net result of this is that you can actually “script”, “debug”, “compile” and “execute” X++ code completely outside the MorphX environment from a 3rd party application where you are attempting to feed data into and extract data out of MS Dynamics AX. Quite remarkable and quite fast (I was impressed)!
 
After deploying the custom class to an AIF port you can consume it within your 3rd party application using standard WCF services. Here is an example of getting hold of a CustTable record:
 
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();
            }
        }
    }
}
 
 
…and here is an example of updating an exchange rate value:
 
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();
            }
        }
    }
}
 
 
In both cases the 3rd party application used the same integration port to pull and push two completely different types of data “from-and-to” Dynamics. It’s both fast and reliable (no framework authority is bypassed) and most importantly (for me at least), I did not have to “code-and-deploy” all the AOT artifacts that accompany two different sets of integrations. The time-saving on this is huge, especially when you are working in a “code-freeze” environment or more frequently when you are dealing with a customer with constantly changing requirements. Once the data-exchange “prototyping” is complete, you can then begin the task of retro-fitting properly designed AIF components.
 

·         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.

 
NOTE: If you plan to perform “large” data exchanges using this mechanism then you need to “bump-up” the standard WCF limits within your [app.config] file. Note, the 2Gb limit placed in the “maxStringContentLenth” property below:
 
<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>
 
 
Drop me a message or comment if you manage to do anything interesting (or amazing) with this code…
 
REGARDS
 
 

Viewing all articles
Browse latest Browse all 10657

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>