Thursday, May 19, 2016

.Net Development in Ax 2012 R2


Painful...


I have been developing under .Net since the initial beta (ca. 2000).  So I would think that I would know a little bit about that environment.  We currently use Ax 2012 R2 CU6.  Lately, we have a had a few issues where using .Net processing would be easier than using X++ classes.  One of these involved a little bit of xml file creation.  The initial design involved calling a stored procedure that would format the data and the writing the result to an xml file.  Using C#, this is a trivial exercise and took a short while to complete.  I tried to deploy the result to my dev AOT (Client and Server).  Back in the AOT, the MorphX environment could find references to the classes created.  However, I couldn't compile the results.  I kept getting errors.  In frustration, I decided to rewrite the classes in X++.
Here are a couple  working samples of what I ended up with for calling the store procedure:


public System.String getDataAsXMLString(System.Data.SqlClient.SqlCommand command)
{
    System.Text.StringBuilder result = new System.Text.StringBuilder();
    System.Data.IDbConnection connection;
    System.Xml.XmlReader xmlr;
    new InteropPermission( InteropKind::ClrInterop ).assert();
    try
    {
        command.set_CommandTimeout(0);
        connection = command.get_Connection();
        connection.Open();
        xmlr = command.ExecuteXmlReader();
        xmlr.Read();
        while (xmlr.get_ReadState() != System.Xml.ReadState::EndOfFile)
        {
            result.Append(xmlr.ReadOuterXml());
            result.Append(System.Environment::get_NewLine());
        }
        connection.Close();
    }
    catch
    {
        if (connection.get_State() != System.Data.ConnectionState::Open)
        {
            connection.Close();
        }
    }
    CodeAccessPermission::revertAssert();
    if(xmlr)
        xmlr.Dispose();
    if (command)
        command.Dispose();
    if (connection)
        connection.Dispose();
    return result.ToString();
}


public System.String getData()
{
    System.Data.SqlClient.SqlCommand command;
    System.String result, spName;
    PC_CalcDataAccess da = new PC_CalcDataAccess();
    System.Data.SqlClient.SqlParameterCollection parameters;
    System.Data.SqlClient.SqlParameter parameter;
    spName = this.parmSPName();
    da.parmDb(db);
    da.parmInstance(instance);
    command = da.getCommand(spName);
    parameters = command.get_Parameters();
    parameter = parameters.Add("@parmCatalogId", System.Data.SqlDbType::NVarChar);
    parameter.set_Value(catalogCode);

    result = da.getDataAsXMLString(command);
    return result;
}



First note: X++ provides no facility for identifying name spaces: there are no using or imports statements.  As a result every reference to a framework class must be fully qualified.  Second: chaining "." operators does not work.  So, the statement command.get_Parameters().Add("@parmCatalogId", System.Data.SqlDbType::NVarChar).set_Value(catalogCode) does not work.  It had to be written line by line as indicate above to work.  Notice that the naming conventions used for getters and setters appear to be based on the IL usages for the class.  Third:  Marshalling between X++ and .Net does not work as advertised.  According to the literature, str in X++ is supposed to work interchangeably with System.String.  It does not.  You cannot use an str member as a parameter when System.String is indicated.  Rather, the str member must be directly assigned to a System.String member and the System.String member may be used as a parameter.  That is why we have the line spName = this.parmSPName().   This is also true of primitive types as well.
Does any know if this is fixed in R3 or AX7?

No comments:

Post a Comment