Obtener el nombre del workflow padre con ParentContext

Cuando desplegamos un plugin o un workflow en nuestra instancia de CRM Dynamics estamos activando un flujo de trabajo que se ejecutará ante un determinado evento. En el caso del workflow podemos configurar mediante el menú de edición de Dynamics unas condiciones extra que se deben de dar para que se ejecute el código –como podría ser que determinado campo del registro modificado esté valorizado-.

Podría darse el caso de que este registro del que hablamos sea modificado por el usuario mediante un formulario de CRM, y además también podría ser modificado mediante la ejecución de otro workflow o plugin de, por ejemplo, actualización de datos nocturnos. Y rizando el rizo podemos suponer que un workflow puede llamar a otro, y este a otro, y así repetidas veces, pudiendo encontrar problemas si no controlamos bien el contexto en el que se ejecuta nuestro código.

En la ejecución de un Workflow o plugin siempre podemos obtener el objeto Context que vendrá heredado y que contendrá información sobre la profundidad –accesible mediante la propiedad Depth del objeto- además de otros parámetros de interés.

Cuando la profundidad es igual o mayor a uno implica que la ejecución de nuestro código ha sido invocada desde otro workflow o plugin. Cuando esto ocurre,  podremos acceder al ParentContext que será un objeto del mismo tipo pero que contendrá información del contexto del workflow que precede al nuestro que incluso podría darse el caso de que sea el mismo workflow.

La idea es poder obtener el nombre literal de ese workflow padre para poder poder condicionar la ejecución de nuestro workflow mediante un if.

Nombre literal del workflow en la entidad "Trabajos del sistema"

Nombre literal del workflow en la entidad “Trabajos del sistema”

Como hemos dicho, el ParentContext contiene información del contexto del workflow invocador de nuestro workflow, y entre otros datos contiene el parámetro CorrelationId que sería el id único del registro de tarea del sistema creado por este workflow. Con este id seremos capaces de obtener el nombre de éste workflow y condicionar la ejecución, como se indica en el siguiente ejemplo:

String connection CRM

 

String connection

Las string connections son variables de tipo string que contienen los datos necesarios para realizar una conexión a un entorno. Se utilizan mucho para, por ejemplo, conectarse a una base de datos. Estas cadenas de texto contienen todos los datos necesarios para que el cliente que realiza la petición sepa a qué base de datos debe conectarse y bajo qué usuario y password debe realizarlo. Son muy útiles porque nos permiten definir todos los datos necesarios para conectarse en una única variable, lo que simplifica el mantenimiento de la aplicación.

CRM Dynamics no es distinto y nos ofrece la posibilidad de conectarnos a una instancia de CRM únicamente indicando la string connection de esa instancia. El procedimiento ha cambiando un poco desde la versiones antiguas de CRM y la nueva versión 365 pero en esencia es igual. Vamos a ver cómo utilizar una string connection desde una aplicación hecha en C#:

SDK CRM 2011, 2013, 2015 y 2016

 

SDK CRM 365

 

Las diferencias entre las versiones son simplemente en el objeto que se instancia y de qué clase proviene. En las versiones antiguas se generaba la interface IOrganizationService desde el objeto OrganizationService mientras que en la nueva versión 365 el IOrganizationService se instancia desde el objeto CrmServiceClient del namespace Microsoft.Xrm.Tooling.Connector. También observar que la string connection para 365 tiene el parámetro extra de Authtype.

Cómo construir nuestra propia String Connection

Las String Connection por desgracia no son siempre iguales. En función del tipo de instancia que tengamos, o del tipo de autentificación se construirán de un tipo u otro. Vamos a ver qué parámetros se pueden incluir y algunos ejemplos:

 

Nombre de parámetro Parámetro Descripción
Server, Url, or Service Uri Url, Server, ServiceUri  La URL de nuestro CRM.

  • Si es una instancia on premise, la URL debería incluir la organización. Por ejemplo http://servidorcrm:puerto/nombre-organizacion.
  • Si es una instancia online, la URL será la que utilizamos para acceder. Por ejemplo: http://nuestra-organizacion.crm4.dynamics.com/

Para la string connection, funcionará igual si utilizamos la palabra Url, Server o ServiceUri

Domain Domain  El dominio de nuestra organización. Estaría relacionado con el Active Directory si estamos hablando de una instancia on premise. Para los online no es necesario incluir este parámetro.
Username or User ID Username Nuestro nombre de usuario.
Password Password Nuestro password. Podemos incluir dobles comillas al inicio y al final si nuetro password incluye espacios u otros caracteres especiales.
Timeout Timeout Por defecto el timeout de las conexiones a CRM son 2 minutos, pero si lo deseamos podemos aumentar o reducir esta cantidad. El formato sería timespan del tipo “hh:mm:ss”.
 Authtype Authtype  Necesario solo para instancias CRM 365. Si tenemos un CRM Dynamics 365 la string connection deberá incluir este parámetro. Los valores que puede tomar son:

  • Authtype=”Office365″ si nuestro CRM 365 es Online
  • Authtype=”AD” si nuestro CRM es on premise y validamos los credenciales contra el Active Directory
  • Authtype=”IFD” si nuestro CRM es on premise y validamos los credenciales mediante IFD

Los parámetros deberán incluirse uno detrás de otro separados por punto y coma en formato texto dentro de nuestra String. No son necesarias las comillas para indicar un parámetro a no ser que el valor de éste incluya carácteres especiales o espacios. Para las versiones on premise es posible también conectarse utilizando seguridad integrada de Windows, no incluyendo los parámetros Username y Password en la string connection. Si hacemos esto, la ejecución de las peticiones se realizará con el usuario que esté ejecutando la sesión de windows.

Algunos ejemplos:

CRM 365 online

“ServiceUri=https://ourorganization.crm4.dynamics.com/; Username=name@ourorganization.onmicrosoft.com; Password=OurPassw0rd; Authtype=Office365;”

 

CRM 365 on premise

“ServiceUri=http://ipcrm:5555/OrganizationName; Domain=ourdomain; Username=username; Password=OurPassw0rd; Authtype=AD;”

 

CRM 2011/2013/2015/2016 online

“ServiceUri=https://ourorganization.crm4.dynamics.com/; Username=name@ourorganization.onmicrosoft.com; Password=OurPassw0rd;”

 

CRM 2011/2013/2015/2016 on premise

“ServiceUri=http://ipcrm:5555/OrganizationName; Domain=ourdomain; Username=username; Password=OurPassw0rd;”

 

CRM 365 on premise con seguridad integrada

“ServiceUri=http://ipcrm:5555/OrganizationName; Authtype=AD;”

 

CRM 2011/2013/2015/2016 on premise con seguridad integrada

“ServiceUri=http://ipcrm:5555/OrganizationName;”

 

Fuentes: Microsoft, Ax3Group

Descargar un Workflow o Plugin de CRM

En este post vamos a ver cómo descargar un Workflow o Plugin registrado en CRM. Sería algo así como el proceso inverso que realiza el Plugin Registration Tool que trae la SDK de CRM. Mediante esta herramienta podemos registrar (o subir) ensamblados DLL a CRM pero no se nos permite descargarlos.

Para hacer la prueba vamos a crear un Workflow muy simple, lo vamos a registrar y a continuación lo vamos a descargar. El Workflow únicamente escribirá un nuevo registro dentro de una entidad de Log denominada new_log.

Además, para tener una pequeña verificación de que el Workflow que nos descargaremos luego sea el mismo que hemos registrado voy a darle una versión de compilado a la DLL con el valor 3.1.4.1.

Versión compilado Visual Studio

Versión compilado Visual Studio

 

Recordad que además para que un Workflow pueda ser registrado debe contener firma. En las propiedades del proyecto, en el apartado Firma (Signing) añadimos una nueva firma.

Añadir firma a un Workfow

Añadir firma a un Workfow

Compilamos y obtenemos la DLL con nuestro Workflow.

DLL del Workflow compilado

DLL del Workflow compilado

Abrimos el Plugin Registration Tool y registramos nuestro Workflow en nuestra organización.

 Workflow registrado en CRM

Workflow registrado en CRM

Hasta aquí sería el trabajo que solemos hacer para cargar un ensamblado en CRM. Ahora vamos a ver cómo descargarlo. Lo primero de todo es entender cómo se guardan en CRM los ensamblados. Un ensamblado se guarda en la base de datos del mismo modo que cualquier otro registro cuando seleccionamos este modo de registro en el Plugin Registration Tool en la opción Location : Database, que es la opción por defecto. En este caso cuando registramos una DLL en CRM se creará un registro que contendrá, entre otros datos, el contenido del ensamblado. La entidad donde se guardan los ensamblados tiene como nombre lógico (logical name) pluginassembly y, como el resto de entidades, cada registro tendrá un ID único. Para conocer el ID debemos ir a CRM y mediante una búsqueda avanzada a la entidad Ensamblados de Complementos encontraremos el nuestro Workflow. El ID lo podemos obtener abriendo la consola del navegador y localizando la linea en el código.

Obtener un ID de un registro mediante búsqueda avanzada

Obtener un ID de un registro mediante búsqueda avanzada

Ahora, teniendo la entidad y el ID único podemos hacer un Retrieve. Los campos que debemos descargar son el nombre (name) y el contenido del ensamblado (content). Este segundo campo contiene el contenido de la DLL codificado en Base 64. Desde C# es sencillo decodificarlo mediante la clase Convert.

Con todo esto ya podemos descargar y guardar en un archivo la DLL, como en el siguiente ejemplo:

Si ejecutamos ese comando y abrimos la carpeta donde está el ejecutable encontraremos la DLL y podremos comprobar la versión.

DLL descargada con su versión

DLL descargada con su versión

 

Obtener el OrganizationService heredado del USD

Cuando queremos meter algún tipo de funcionalidad específica en nuestro USD lo realizamos mediante un Custom Hosted Control. Este control se realizaría desde Visual Studio extendiendo la clase DynamicsBaseHostedControl del compilado Microsoft.Crm.UnifiedServiceDesk. Esta clase a su vez implementa varias interfaces como son IDesktopUserActionsConsumer, IUsdControl y ISessionInfoConsumer. Estas interfaces incluyen funciones  como el DoAction –que se utiliza para recibir ActionsCalls del USD en nuestra clase- , el DesktopReady –que se ejecuta cuando el USD ha cargado-  o el NotifyContextChange –evento de cambio en alguna variable del contexto- entre otras. Dentro de todas estas funciones es muy probable que necesitemos interactuar con nuestro CRM. Como siempre, la API de CRM nos permitirá hacerlo utilizando el objeto OrganizationService. Como ya sabréis este objeto se puede instanciar utilizando una cadena de conexión con la IP de nuestro CRM y el usuario y contraseña de un usuario del CRM.

Pero puesto que en USD ya nos hemos tenido que identificar para poder acceder al mismo introduciendo unos credenciales al inicio –o en su defecto utilizando los credenciales del Active Directory- no sería práctico volver a instanciar el objecto OrganizationService puesto que deberíamos volver a preguntar al usuario sus credenciales.

El USD por dentro utiliza un objecto OrganizationService de igual modo que cuando realizamos un programa nosotros. Este objeto OrganizationService se instancia de los credenciales iniciales que se introducen en la pantalla de inicio del USD –la clásica pantalla azul de USD- y se utiliza internamente para descargar toda la configuración del agente al PC desde donde se está ejecutando el USD.

Probablemente existan varios métodos para obtener este objeto instanciado en nuestro código. Yo únicamente conozco dos.

El primero es heredar en nuestra clase la clase AifServiceBase. Esta clase que está en la librería Microsoft.Uii.AifServices, en el contexto de USD hereda el objeto OrganizationService para poder utilizarlo. El esquema que yo utilizaría sería, por un lado, la clase principal de la dll -la que incluiremos como Hosted Control en nuestra configuración- que hereda del  DynamicsBaseHostedControl  y por otro lado una clase que actúe de interface entre USD y CRM que herede del AifServiceBase. Dentro de la primera clase instanciaremos la segunda una sola vez pudiendo acceder al objeto a partir de ese momento.

Ejemplo estructura clases Custom Hosted Control con AifService

Ejemplo estructura clases Custom Hosted Control con AifService



Como lado negativo de este método comentar que a mi únicamente me ha funcionado con la versión 1.2 de USD y sospecho que es debido a que el AifService requiere la dll Microsoft.Crm.Sdk.Proxy que únicamente está incluida en la SDK de 2015 y por algún motivo no funciona en versiones de compilados superiores.

El segundo método y el más sencillo es obtener el IOrganizationService directamente del objeto Global Manager.

El USD siempre requiere de tres objetos principales y que son, podríamos decir, obligatorios: El Global Manager, el  Connection Manager y el Panel Layout. La idea es obtener en nuestra clase el Global Manager del USD y extraer el OrganizationService de dentro del objeto.

Para ello únicamente necesitamos conocer el nombre del Hosted Control de tipo Global Manager. El nombre lo obtenemos de la configuración del Unified Service Desk de nuestro CRM. Si durante la instalación del USD seleccionamos la opción de incluir datos de ejemplo, este Hosted Control ya estaría creado en nuestro primer USD. Si por el contrario hicimos una instalación limpia, tuvimos que crearlo nosotros mismos en algún momento. Lo que necesitamos es el nombre del Hosted Control, esto es, el string que se ve en la configuración.

Nombre del Hosted Control Global Manager

Nombre del Hosted Control Global Manager

En nuestro caso el Hosted Control se llama “Global Manager” por lo que para obtener el OrganizationService simplemente deberíamos de ejecutar la siguiente línea:

 

 

Relacion entre datos cargados y entidad ImportFile

La funcionalidad que dispone CRM para importar archivos es bastante atractiva para los clientes que compran el producto. El poder cargar de modo automático grandes archivos de datos en la base de datos de CRM es una funcionalidad que se valora, sobre todo cuando el CRM es un sistema complementario a otros como podría ser un SAP, para tener los entornos alineados.

Lo único que es necesario configurar para automatizar la tarea de importación es un mapping de datos. Este mapping se tendrá que hacer con el primer archivo que se cargue y en él se indicará qué columnas del archivo Excel se cargarán en qué campos de la entidad de CRM. Posteriormente, el usuario de CRM únicamente tendrá que seleccionar el archivo que quiera importar y el mapping que se creó relativo a ese archivo. Como punto negativo únicamente decir que CRM admite de modo nativo importaciones de archivos con extensión CSV. Desde el propio Excel es relativamente sencillo exportar nuestro archivo con extensión .XLSX/.XLS a un archivo .CSV.

Desde el menú de Excel Guardar como podremos seleccionar la opción “CSV (delimitado por comas)” que nos generará el archivo con la estructura adecuada para Dynamics.

Exportar Excel como CSV

Exportar Excel como CSV

Para quien le interese el tema, si abrimos el archivo CSV con un editor de texto plano nos daremos cuenta que la estructura del propio archivo es muy simple, siendo la primera línea el nombre de las columnas separadas por punto y coma, y el resto los valores para cada columna, separados con el mismo carácter. Con esta estructura, no sería muy complicado escribir un programa que generase archivos CSV de cualquier base de datos, listos para importar en nuestro Dynamics.

Volviendo a la importación, una vez seleccionado el archivo y el mapping, el CRM empezará a cargar los datos. Nuestro trabajo terminaría aquí si únicamente nos interesase cargar los datos sin ningún tipo de procesado.
No obstante, no son pocos los clientes que se interesan por no solo cargar datos, sino también aplicarle algún tipo de lógica. Me viene a la mente por ejemplo una lógica de sustitución de un código cargado en el archivo por otro que esté ya en la base de clientes de CRM. O sencillamente relacionar cada registro cargado con un lookup a otra entidad mediante algún campo de identificación.

La idea de este post no es proponer una arquitectura de workflow o programa que aplique esta lógica puesto que en función de la cantidad de datos cargados se elegirá una u otra. El propósito de este post es descubrir de qué modo quedan relacionadas cada una de los registros creados en nuestra entidad con el archivo cargado.

Partimos por tanto de un dato conocido: el archivo cargado en la entidad ImportFile así como todos los datos asociados a él (número de líneas procesadas, fecha de inicio de carga, fecha de fin de carga, etc). Para complicarlo vamos a ponernos la restricción de que todos los archivos cargados van a la misma entidad. Sea cual sea el tipo de archivo se cargará en una entidad personalizada para la carga, escribiendo solo en unos campos en función del maping. El objetivo es conociendo únicamente el archivo de carga, obtener todos los registros creados en CRM a partir de él.

Logical name Schema name Label Type Descripción
importfileid ImportField Import Uniqueidentifier Id unico del registro
createdby CreatedBy Created By Lookup Usuario que cargo el file
completedon CompletedOn Completed On DateTime Fecha en la que se completó la importación
createdon CreatedOn Created On DateTime Fecha en la que se creó el registro
failurecount FailureCount Errors Integer Numero de lineas con error
partialfailurecount PartialFailureCount Partial Failures Integer Numero de lineas con error parcial
successcount SuccessCount Successes Integer Numero de lineas importadas correctamente
totalcount TotalCount Total Processed Integer Total de lineas (debería ser igual a la suma de los tres valores anteriores)
source Source Source String Nombre del file fuente
name Name Import Name String Nombre del registro en CRM. Coincide con el source
targetentityname TargetEntityName Target Entity String Entidad donde se cargarán los datos (en el ejemplo new_uploadentity)

Campos de la entidad ImportFile de CRM

Una primera idea sería obtener las fechas de inicio y final de carga del archivo y realizar un Retrieve Multiple de datos creados en ese intervalo de tiempo en nuestra entidad. Esta solución tiene un problema que es que funcionará siempre y cuando no se carguen dos –o más- archivos al mismo tiempo. Como lado positivo, esta es una query rápida que no requiere de joins y que si no mezclamos archivos al mismo tiempo obtiene buenos resultados.

Pero los usuarios no entienden de restricciones y si existe la posibilidad de importar files, seguro que más de una vez nos encontramos con que se solapan cargas. Para solucionar esto vamos a realizar una búsqueda de registros insertados en nuestra entidad partiendo del ID del archivo cargado.

Dynamics ha realizado un gran trabajo en este aspecto implementando un método de carga en paralelo a dos entidades. Por cada línea de nuestro archivo CSV se cargarán en CRM dos registros. El primero será siempre el registro que contiene la información de nuestros datos del Excel con el mapping aplicado en nuestra entidad de carga. Esta sería la importación misma. El segundo registro se almacena en la entidad ImportData. Este segundo registro no incluye información del archivo sino más bien información de la línea en bruto. Algunos datos que podemos encontrar en esta entidad son la posición de la línea en el file, los datos sin procesar de la línea o el ID de la línea en nuestra entidad de carga. Además en la entidad ImportData existe un campo lookup a la entidad ImportFile. El esquema de entidades sería el siguiente:

Relación entre entidad ImportFile, entidad ImportData y nuestra entidad de carga

Relación entre entidad ImportFile, entidad ImportData y nuestra entidad de carga

Como vemos, cuando cargamos un file se crea un registro en la entidad ImportFile que contiene la información de cabecera del file. En el proceso de importación, por cada línea del Excel se creará un registro en la entidad ImportData y otro registro en nuestra entidad de carga, en el esquema new_uploadentity.  El modo en que quedan relacionadas son que en el campo recordid del registro creado en ImportData  se incluye el id único del registro new_uploadentityId.

Ya tenemos todo para realizar la query y obtener los resultados. Las dos opciones que propongo son QueryExpression y Linq. Por mi parte recomiendo Linq puesto que no tiene la limitación de los 5000 registros recibidos.

En ambos casos la función nos devolverá los datos cargados en nuestra entidad new_UploadEntity dado el ID de un file de la entidad ImportFile.

En QueryExpression el código será similar a:

Si utilizamos Linq el código sería

En ambos casos he utilizado una clase de definición como la siguiente:

 

 

Error al recuperar DateTime: nos devuelve un día menos

Algo que puede producirte un gran quebradero de cabeza tiene que ver con los objetos tipo DateTime, con la zona horaria y con los objetos Retrieve que ejecutes desde tu código C#.

Resulta que existe un problema en CRM que por el cual al hacer una petición de un objeto DateTime para posteriormente ser utilizado en código, la fecha que recibimos es distinta a la que hay en el CRM guardada. Cuando pidamos una fecha veremos que nos devuelve un día menos del que hay en CRM guardado. Esto podrá variar en función de la hora local que tengamos configurada en el servidor y la hora local del cliente que realice la petición.

Vamos a ver un ejemplo de código que selecciona un contacto de nuestro CRM y nos devuelve la fecha de nacimiento:

Para la prueba he creado un contacto con mi nombre y fecha de nacimiento de “21/04/1959”.

Fecha devuelta equivale a un día menos

Fecha devuelta equivale a un día menos

Nota: en el CRM, la fecha que se ve es “04/21/1959”. Esto tiene que ver con la configuración de mi usuario que tiene por defecto idioma ingles y formato de fecha de USA, sin embargo esta fecha es exactamente la misma que “21/04/1959”.

Como vemos en la imagen, la fecha obtenida tiene un día menos que la fecha guardada en el CRM. ¿Como solucionamos esto?. Bueno, seguramente existan muchos métodos que tienen que ver con la configuración horaria de los DateTimes devueltos por el OrganizationService u otros parámetros. La forma fácil y que yo utilizo siempre desde que la aprendí es la siguiente:

Una vez tengamos la fecha en un String, le concatenamos una “Z” al final del mismo, y volvemos a convertir a DateTime. ¿Y esto por qué es así? bueno por lo que he podido leer, al incluir una “Z” al final de una hora equivale a convertirla a hora local UTC. La Z viene de ZULU, utilizado por la marina estadounidense.

Con todo esto, nuestro código debería quedar del siguiente modo:

Con este cambio, el resultado que obtenemos pasará a ser el siguiente:

Fecha devuelta correcta

Fecha devuelta correcta

 

Cómo saber a que DLL corresponde un Hosted Control

Existe un método muy sencillo y que no requiere de ninguna instalación especial para conocer el namespace y el tipo de cada uno de los hosted controls que hay definidos en la configuración de USD en CRM.

Muchas veces necesitaremos en nuestro código hacer referencia a uno de estos objetos especiales para modificar algún atributo o para captar un evento. Estos ojbetos especiales son los que aparecen listados en la configuración de USD cuando creamos un nuevo hosted control, en el campo USD Component Type.

Tipos de hosted controls predefinidos

Tipos de hosted controls predefinidos

Como ya hemos visto en otras entradas a veces necesitamos hacer referencia a alguno de estos objetos (en esta entrada al Debugger por ejemplo). Para capturar la instancia del objeto necesitamos dos cosas: el nombre del hosted control y el tipo. El nombre es el que elijamos en la configuración del USD en CRM. El tipo es en principio desconocido.

Bien pues vamos a ver cómo consultar el tipo de la manera más sencilla. Lo primero de todo es irnos a la configuración del USD en CRM y crear un nuevo hosted control (como en la imagen de arriba).

Luego en el desplegable de USD Component Type seleccionamos el objeto que queremos averiguar. En nuestro caso queremos averiguar por ejemplo la DLL y el tipo del objeto Debugger:

Selección de Hosted Control Debugger

Selección de Hosted Control Debugger

A continuación lo único que deberemos hacer es volver a pinchar sobre el campo USD Component Type y seleccionar la opción USD Hosted Control. Como veremos cuando hagamos esto, aparecerán debajo dos campos: Assembly URI y Assembly Type. Además aparecerán rellenos con el tipo del objeto que hayamos seleccionado en primer lugar (en nuestro caso el objeto Debugger).

Selección de USD Hosted Control

Selección de USD Hosted Control

Una vez que conocemos el tipo, en nuestro Custom Hosted Control podremos obtener la instancia del objeto del siguiente modo:

Con esto ya somos capaces de modificar cualquier atributo de la instancia cargada en nuestro USD de este objeto.

El motivo por el que ocurre esto es que al seleccionar cualquier tipo especial en el campo USD Component Type los campos Assembly URI y Assembly Type se auto rellenan y además se ocultan. El fallo está en que al cambiar el desplegable no se resetean estos valores.

Unified Service Desk (USD): Utilizar debugger para trazas

Todos los que hemos tenido que desarrollar un USD sabemos que es una tarea bastante engorrosa. Algunos motivos son que no somos dueños del proyecto y por lo tanto no podemos añadir trazas donde queramos. Otro motivo es que el USD tiene unos tiempos de inicialización bastante grandes y todo lo que sea evitar cerrar la aplicación es bienvenido.

Para facilitarnos un poco la vida, en Microsoft desarrollaron un objeto llamado Debugger. Este objeto se añade por configuración a nuestro USD mediante un Hosted Control de tipo Debugger. Lo ideal sería ponerlo en el MainPanel como se muestra en la imagen:

Declaración de Debugger en configuración del USD en CRM

Declaración de Debugger en configuración del USD en CRM

Ya hemos hablado sobre las bondades del Debugger y cómo configurarlo en nuestra aplicación para poder acceder a él en esta otra entrada. En este caso nos centraremos en una de las pestañas, la de Resultados de Depuración:

Pestaña de Resultados de Depuración del Debugger

Pestaña de Resultados de Depuración del Debugger

Dentro de esta pestaña tenemos dos objetos: un Textbox multiline y un Botón para borrar el contenido. En general en este campo de texto se añadirán algunos resultados de depuración así como errores referentes a hosted controls. Este Textbox es el que voy a utilizar para monitorizar nuestras variables.

Para acceder a este Textbox vamos a utilizar la librería Reflection (System.Reflection).  Con esta librería seremos capaces de acceder a atributos privados de objetos para instancias previamente creadas. Básicamente nos permite saltarnos la decisión de un programador de declarar un objeto private o public. Es importante entender que si un programador decidió poner un objeto como private existiría un buen motivo por lo que este procedimiento es totalmente desaconsejable para entornos de producción.

No obstante antes de llegar al entorno de producción pasaremos por muchas etapas en nuestro USD en entornos de laboratorio, de desarrollo o incluso de pre producción. Con esta idea seremos capaces de obtener muestreo de datos en ejecución de un modo sencillo y que pueda ser invocado desde cualquier elemento del USD.

Dicho esto, vamos a pasar a ver el código.

La idea es introducir unas lineas de código en cualquiera de nuestros Custom Hosted Control (o hosted control creados por código). A mi personalmente me gusta tener siempre un hosted control global, situado en el HiddenPanel que sirva de controlador central de funciones, FireActions y en general de gestionar la interacción con el usuario. En el ejemplo llamaré a este hosted control como Controlador Central.  No obstante podemos declarar la función en cualquier otro hosted control que hayamos programado.

En este hosted control vamos a añadir unas lineas al DesktopReady y una UIAction en la función DoAction.

Recordar que para que funcione esta UIAction es necesario declararla en la configuración del USD en CRM para el hosted control Controlador Central.

Para probar que funciona correctamente podremos hacerlo de dos modos: mediante una ActionCall declarada en la configuración del USD en CRM que apunte al hosted control “Controlador Central”, la UIAction “addTraza” y como datos “Prueba traza”; o ejecutando en nuestro código un FireRequestAction como el siguiente:

El resultado será el siguiente:

Resultado al ejecutar el FireRequestAction

Resultado al ejecutar el FireRequestAction

Como vemos ya tenemos acceso libre a este TextBox para introducir lo que deseemos (variables, respuestas de servicios, estado de conexion etc) y poder consultar en cualquier momento de la ejecución del USD.

 

Option Set: Obtener Label/Etiqueta en C#

Obtener la etiqueta de un Option Set de una entidad de CRM por código C# no es trivial. Para ello podemos utilizar dos métodos como veremos a continuación: descargar el meta o utilizar la propiedad formattedvalues

Lo que en otros lenguajes y entornos es conocido como ComboBox en CRM se le denomina Option Set. Se trataría de un desplegable de opciones disponibles que puede adoptar como valor un campo de un formulario.

La estructura de este tipo de campo constaría de una lista de parejas denominadas Label y Value. El Label correspondería a la etiqueta visible que se muestra en el formulario. Esta etiqueta será  con la que el usuario de CRM deberá interactuar y en general será de tipo String.

Además, por detrás, tenemos el Value. Esta variable identifica de modo único cada una de las opciones disponibles en el Option Set. No podrá haber dos Values iguales en el mismo Option Set puesto que este valor es el que se utiliza como clave primaria para la lógica que corre por detrás.

Campo Option Set CRM

Campo Option Set CRM

Por defecto CRM insertará los Values siguiendo una serie de reglas internas como que el prefijo sea único para cada solución. En general este dato no deberá preocuparnos y si nuestro trabajo termina en crear el formulario nos dará igual el valor de este campo aunque siempre podremos actualizarlo al valor que deseemos -cumpliendo que este valor entero sea único-.

Una aplicación en donde deberíamos cambiar los Values generados por CRM sería por ejemplo un listado de prefijos de teléfonos. En este caso sí que convendría actualizar cada item con su determinado valor (España – 34, Italia – 39, etc). En general se deberá de crear la correspondencia de Label – Value en función del alcance de nuestro desarrollo.

El motivo por el cual es bueno realizar esta correspondencia es que si accedemos mediante C# al valor de un Option Set obtendremos por defecto el Value, y no el Label. Para muchas aplicaciones con el Value será suficiente, pero para otras necesitaremos también conocer el Label del campo. Para acceder a este valor normalmente se utilizan dos métodos

El primero -y más lento- sería descargar previamente el Option Set que se desea analizar y guardarlo en un diccionario. A continuación se accederá al Value del campo en cuestión y se cruzará el valor obtenido con el diccionario obteniendo el Label correspondiente.

 

La segunda opción -más rápida- sería obtener directamente el Label del Option Set mediante la propiedad FormattedValues.

Ambas tienen sus pros y sus contras y en función de la aplicación que se esté desarrollando el programador deberá escoger cual le conviene más.

Las librerías necesarias para los objetos mostrados en los ejemplos se encuentran en la SDK 2015 de CRM. En particular hemos utilizado: