Automatic Class Pack/Unpack

A common task associated with creating objects in Dynamics AX is giving them the ability to be packed into a container for storage as a SysLastValue entry.

The common methodology used for this is to declare a version and a list macro in the class declaration and to add the standard Pack and Unpack methods to your class. The problem with this standard methodology is that you must constantly update the version and list macros to add new parameters that you want to store. You then have to deal with the various issues related to tracking all of these version changes, and unpacking them accordingly.

I have developed a version independent method for automatically generating the packed class that makes code maintenance simplistic. The unpacking of the class is just as simple and isn’t subject to the issues associated with tracking packed class versions.

The following code will demonstrate this new methodology for packing and unpacking. It also demonstrates the usage of some of the components of reflection available in the Dynamics AX system.

This static method is used to pack the class into a container object:

/*
 * Utility class for packing class parameters into a
 * container that contains the parameters in a Map object.
 *
 * All non-static class methods that begin with 'parm'
 * and that have a return value are included.
 *
 * The return value is a packed map object:
 *   [Map(Types::String,Types::Container)]
 * It contains the method name as key, and the method's
 * return object in a container as a value.
 *
 * If the method returns a Class object, it is packed
 * before being placed into the value container.
 */
static container PackClassParms(ClassId _classId, object _class)
{
    SysDictClass    sdc  = new SysDictClass(_classId);
    SysDictClass    tsdc;
    Map             vars = new Map(Types::String,Types::Container);

    int             i;
    container       c;
    str             mName;
    Types           mType;
;

    try
    {
        for(i=1 ; i<= sdc.objectMethodCnt() ; i++)
        {
            mName   = sdc.objectMethod(i);
            //Method MUST begin with 'parm' and be non-static
            if(!sdc.objectMethodObject(i).isStatic() && strscan(mName,'parm',1,4))
            {
                mType   = sdc.objectMethodObject(i).returnType();
                c       = connull();
                switch(mType)
                {
                    case Types::void:
                        break;
                    //Pack parameters that return packable objects
                    case Types::Class:
                        tsdc = new SysDictClass(sdc.objectMethodObject(i).returnId());
                        if(tsdc.hasStaticMethod('pack'))
                            c = [sdc.callObject(mName,_class).pack()];
                        break;
                    //All others fit in container
                    default:
                        c = [sdc.callObject(mName,_class)];
                        break;
                }
                vars.insert(mName,c);
            }
        }
    }
    catch (exception::Error)
    {
        return connull();
    }

    return vars.pack();
}

This static method is used to unpack the container object into a class:

/*
 * Utility class for unpacking parameters into a class object.
 *
 * The input container is a packed map object:
 *   [Map(Types::String,Types::Container)]
 * It contains the method name as key, and the method's
 * return object in a container as a value.
 *
 * If the method returns a Class object, it is
 * either unpacked or created by calling the
 * object's unpack or create method.
 *
 */
static void UnpackClassParms(ClassId _classId, object _class, container _values)
{
    SysDictClass    sdc = new SysDictClass(_classId);
    SysDictClass    tsdc;

    Object          o;
    Map             vars;

    int             i;
    container       c;
    str             mName;
    Types           mType;
;
    try
    {
        vars    = Map::create(_values);

        for(i=1 ; i<= sdc.objectMethodCnt() ; i++)
        {
            mName   = sdc.objectMethod(i);
            if(vars.exists(mName))
            {
                o       = null;
                c       = vars.lookup(mName);
                mType   = sdc.objectMethodObject(i).returnType();
                switch(mType)
                {
                    case Types::Class:
                        tsdc = new SysDictClass(sdc.objectMethodObject(i).returnId());
                        if(tsdc.hasStaticMethod('create'))
                            o = tsdc.callStatic('create',conpeek(c,1));
                        else if(tsdc.hasObjectMethod('unpack'))
                            o = tsdc.callObject('unpack',tsdc.makeObject(),conpeek(c,1));

                        if(o)
                            sdc.callObject(mName,_class,o);
                        break;
                    default:
                        sdc.callObject(mName,_class,conpeek(c,1));
                        break;
                }
            }
        }
    }
    catch
    {
        info('Error unpacking values');
    }
}

There are many ways that this code can be optimized to fit a particular environment but this example should present the developer with a generic starting point.

Soon I will be adding an xpo containing a class that demonstrates this functionality.

Share/Bookmark
This entry was posted in AX 2009, AX 4.0, Microsoft Dynamics AX and tagged , , , . Bookmark the permalink.

One Response to Automatic Class Pack/Unpack

  1. Pingback: Automatic Class Pack/Unpack Part 2 | SLC Consulting, LLC

Comments are closed.