Microsoft Dynamics AX 2012, servicios web, .NET Interop, cliente-servidor y arquitectura de aplicación

¡Vaya título! ¿Qué tienen que ver todos estos conceptos y por qué debería tenerlos en cuenta? Es posible que no sea una situación que se nos presente todos los días, pero hay veces donde hay que tener todos esos conceptos en cuenta para hacer que un fragmento de X++ funcione correctamente. Este ha sido mi caso: Tengo que consumir un servicio web externo desde Microsoft Dynamics AX 2012. Parece fácil, ¿no?. El servicio web se va a consumir en un proceso por lotes (servidor), pero también debe poder llamarse manualmente desde formularios (cliente).

En Microsoft Dynamics AX 2009, para utilizar un servicio debíamos añadir una referencia de servicio al AOT. En la versión 2012 creamos un proyecto Visual Studio de tipo Librería de clases. En ese proyecto de Visual Studio agregamos una referencia de servicio y agregamos el proyecto al AOT. No voy a entrar en detalle sobre ésto porque esta bien explicado por ejemplo en este white paper.

Una de las cosas a tener en cuenta acerca de los conceptos del título de este post está en las propiedades del proyecto en Visual Studio:

Propiedades Proyecto Visual Studio

Lo relevante aquí es marcar Deploy to Client si queremos que la DLL se despliegue a los clientes y Deploy to Server si queremos que se despliegue al servidor. De esta manera, el sistema copiará la librería cuando sea necesario a la carpeta correspondiente, descargándola de la base de datos (de la Model Store) donde está almacenada. Dependiendo de cómo se ejecute el código X++ que utiliza esta librería podemos marcar uno u otro o los dos.

Un problema con el que me he peleado muchas veces (también ocurría en 2009) y que, para mi, es el más probable que se nos pase por alto, es el que devuelve este error (no muy descriptivo):

No se hace referencia al ensamblado que contiene el tipo <namespace>.<service reference>.<cliente>.

Al encontrarnos este error, lo primero que pensamos es que la librería no se ha desplegado bien. Pero si vamos a la carpeta correspondiente en el cliente (C:\Users\<USUARIO>\AppData\Local\Microsoft\Dynamics AX\VSAssemblies) o en el servidor (C:\Program Files\Microsoft Dynamics AX\60\Server\<AOS>\bin\VSAssemblies) podemos ver que la librería se ha copiado correctamente (si hemos configurado bién las opciones de la imagen anterior). ¿Entonces qué ocurre?

La explicación la encontramos en otro de los conceptos del título de este post. El servidor AOS es un servicio de 64 bits mientras que el cliente es una aplicación de 32 bits, por lo que es necesario configurar la librería que hemos generado en Visual Studio para que funcione en ambas arquitecturas. Si la librería se compila sólo para 32 o 64 bits, sólo funcionará en el cliente o en el servidor, pero no en los dos:

Configuración Proyecto Visual Studio

Y por último, como siempre que utilizamos librerías externas, es posible que tengamos que asignar manualmente permisos de ejecución de código manejado, por lo que el código resultante tendrá una estructura parecida a la siguiente:

[namespace].ServiceRef.serviciosSoapClient          cli;
System.Type                                         type;
System.ServiceModel.EndpointAddress                 endpointAddr;
System.ServiceModel.Description.ServiceEndpoint     endpoint;
System.Exception                                    exception;

new InteropPermission(InteropKind::DllInterop).assert();

try
{
    type = CLRInterop::getType('[namespace].ServiceRef.servicioswebSoapClient');
    cli  = AifUtil::createServiceClient(type);

    endpointAddr = new System.ServiceModel.EndpointAddress('http://url...');
    endpoint     = cli.get_Endpoint();
    endpoint.set_Address(endpointAddr);

    // Llamada al servicio!
    cli.WebMethod(<parametros>);
}
catch (Exception::CLRError)
{
    ex = CLRInterop::getLastException();
    error(ex.ToString());
}

CodeAccessPermission::revertAssert();

Espero que a alguien le sirva. Lo dejo aquí para encontrarlo fácilmente cuando me vuelva a hacer falta 🙂