Recuperar colecciones con los Servicios Web – RetrieveMultiple y Fetch

La última vez que hablamos sobre los métodos de los servicios web del CRM dejamos a un lado dos métodos que nos permiten recuperar colecciones de entidades, los métodos RetrieveMultiple() y Fetch(). Como vamos a ver, los dos nos permiten recuperar colecciones de objetos de Microsoft Dynamics CRM, aunque con unos matices y filosofía de utilización muy distintos.


RetrieveMultiple


El método RetrieveMultiple es un método “fuertemente tipado” que nos permite recuperar una colección de registros de una sola entidad del CRM, es decir nos permite recuperar una colección de objetos del mismo tipo. Esta es una de las grandes diferencias con Fetch, ya que el lenguaje de consulta FetchXML utilizado por el método Fetch no es fuertemente tipado con lo que podemos recuperar atributos de distintos tipos de entidad al igual que hacemos en sentencias SQL. Por poner un ejemplo, si queremos recuperar varias columnas de la entidad contactos y el nombre de la cuenta a la que pertenecen en una sola consulta tendremos que usar FectchXML, ya que con RetrieveMultiple solo podemos recuperar columnas de una entidad con lo que no podríamos incluir el nombre de la cuenta.


La otra característica distintiva de este método es que nos permite construir las consultas de manera programática, utilizando objetos QueryExpression o QueryByAttribute. ¿Cuándo utilizamos uno u otro? Pues la respuesta es más o menos sencilla. Si lo que necesitamos es recuperar un conjunto de registros de una entidad en los que un conjunto de atributos tome un determinado valor, utilizaremos QueryByAttribute. Como por ejemplo, recuperar todas las cuentas cuya ciudad sea Madrid y su país España.











// Create the QueryByAttribute object.


QueryByAttribute query = new QueryByAttribute();


query.ColumnSet = new AllColumns();


query.EntityName = EntityName.account.ToString();


 


// The query returns all accounts where address1_city is Madrid


// and address1_country is España.


query.Attributes = new string [] {“address1_city”, “address1_country”};


query.Values = new string [] {“Madrid”, “España”};


 


Si necesitamos condiciones más complejas tendremos que usar un objeto del tipo QueryExpression. Este tipo de objetos de consulta dispone de una propiedad Criteria, que nos permite utilizar objetos FilterExpression para crear condiciones de filtrado más complejas. Y de la propiedad LinkEntities que nos permite establecer filtros con Joins sobre otras entidades. Por ejemplo, podemos crear una consulta con la que obtener el nombre y el identificador de las cuentas cuyo dueño no tenga como apellido Amoedo.









 


// Return Accounts whose owner’s last name is not Amoedo


// Create a column set that holds the names of the columns to be retrieved.


ColumnSet cols = new ColumnSet();


 


// Attributes that we want to retrieve.


cols.Attributes = new string [] {“name”, “accountid”};


 


// Create the condition expression.


ConditionExpression condition = new ConditionExpression();


 


// Set the condition for the retrieval to be when the last name of the account’s owner is not Amoedo.


condition.AttributeName = “lastname”;


condition.Operator = ConditionOperator.NotEqual;


condition.Values = new string [] {“Amoedo”};


 


// Build the filter based on the condition.


FilterExpression filter = new FilterExpression();


filter.FilterOperator = LogicalOperator.And;


filter.Conditions = new ConditionExpression[] {condition};


 


// Create a LinkEntity to link the owner’s information to the account.


LinkEntity link = new LinkEntity();


 


// Set the properties of the LinkEntity.


link.LinkCriteria = filter;


 


// Set the linking entity to be the account and set the linking attribute to be the owninguser.


link.LinkFromEntityName = EntityName.account.ToString();


link.LinkFromAttributeName = “owninguser”;


 


// Set the attribute and entity being linked to.


link.LinkToAttributeName = “systemuserid”;


link.LinkToEntityName = EntityName.systemuser.ToString();


 


// Create the query.


QueryExpression query = new QueryExpression();


 


// Set the properties of the query.


query.EntityName = EntityName.account.ToString();


query.ColumnSet = cols;


query.LinkEntities = new LinkEntity[] {link};


 


Sobre QueryExpression y QueryByAttribute hay que comentar un par de cosillas más. Como la existencia en ambos tipos de objeto de la propiedad PageInfo, que permite recuperar los resultados página a página según el tamaño de página que indiquemos. Y también, la posibilidad de utilizar el método Execute para realizar consultas mediante el mensaje RetrieveMultipleRequest.


 


Fecth


Este método, al contrario que RetrieveMultiple, no utiliza tipado “fuerte” para consultar datos. En vez de utilizar objetos para construir las consultas, utilizamos un lenguaje de consulta llamado FetchXML, y en vez de obtener una BusinessEntity Collection como resultado, obtenemos un documento XML. Con esto conseguimos una mayor flexibilidad, a cambio de una mayor “complejidad” a la hora de crear las consultas. Digo mayor complejidad, porque normalmente es más sencillo construir una consultar mediante objetos query que mediante FetchXML, y por que puede resultar más cómodo obtener los resultados como una colección de objetos que como un documento XML.


Sin embargo, gracias a la ausencia de tipado fuerte, tenemos algunas ventajas como el poder recuperar columnas de más de una entidad. Es decir, el resultado de la consulta puede mezclar datos de varias entidades, cosa que no se puede hacer con RetrievMultiple. Vamos, que tenemos las mismas posibilidades para hacer joins que en SQL.


Por ejemplo, veamos como quedaría la consulta que anteriormente hacíamos con una QueryExpression utilizando FetchXML.









// Retrieve all accounts where the last name is not Amoedo.


string fetch2 = @”


<fetch mapping=’logical’>


<entity name=’account’>


<attribute name=’accountid’/>


<attribute name=’name’/>


<link-entity name=’systemuser’ to=’owninguser’>


<filter type=’and’>


<condition attribute=’lastname’ operator=’ne’ value=’Amoedo’ />


</filter>


</link-entity>


</entity>


</fetch>


“;


 


// Fetch the results.


String result2 = service.Fetch(fetch2);


 


Evidentemente, el código es menos extenso, pero como veis ahora la consulta no es más que un string con XML, con lo que perdemos las comprobaciones en tiempo de compilación propias del tipado fuerte. Además, los resultados también son un string con XML, con lo que para manejarlos tendremos que incluir más lógica.


Otra cosa muy interesante de FetchXML, que ya comentamos en el artículo sobre RSS, es que Microsoft Dynamics CRM almacena todas las consultas avanzadas y las consultas de las vistas del sistema como FetchXML, y como estas consultas son accesibles a través de los servicios web (entidad savedquery) tenemos un montón de ejemplos de consultas incluidas de serie en nuestro sistema.


Destacar también, que al igual que con los objetos Query, con FetchXML también es posible utilizar paginado.


RetrieveMultiple vs Fecth


Bueno, y después de todos esto ¿Cual usamos? Pues como buen gallego que soy ya sabéis la respuesta… Depende.


Vamos a repasar las principales diferencias primero entre ambos métodos:






















RetrieveMultiple


Fetch


Valores de retorno “fuertemente tipado”


Valores de retorno NO fuertemente tipados


Consultas construidas con objetos tipo Query


Consultas construidas con FecthXML


Centrado en Business Entities, devuelve valores de una única entidad


Business Entities agnóstico, puede devolver valores procedentes de atributos de varias entidades


Soporta Join (pero solo para filtrado)


Soporta Join


 


A la vista de las diferencias, la idea queda más o menos clara. Debemos tratar de utilizar RetrieveMultiple cuando queramos obtener resultados de fuertemente tipados, de una única entidad, y queramos realizar alguna operación sobre los objetos de negocio obtenidos, por ejemplo, recuperar las cuentas de una ciudad para asignárselas a un determinado comercial mediante los servicios web.


Sin embargo, Fetch será adecuado cuando necesitemos realizar alguna consulta para obtener una combinación de datos de varias entidades, cuando queramos guardar la consulta en un fichero de manera sencilla, cuando no necesitemos resultados fuertemente tipados, o incluso cuando obtener los resultados en XML sea una “obligación” para poder interoperar con otro sistema. Por poner un ejemplo de uso de Fetch, imaginad que disponemos de un servicio web que proporciona información sobre los productos en oferta a nuestros mayoristas, aquí podríamos utilizar un servicio web que utilice una consulta FetchXML, almacenada en un fichero XML, para obtener un documento XML para enviar como resultado a través del Servicio Web.


Compatibilidad de FetchXML y los objetos Query


Finalmente, decir que existe la posibilidad de hacer conversiones entre ambos formatos. Para ello, disponemos de los mensajes FetchXmlToQueryExpressionRequest y QueryExpressionToFetchXmlRequest, que utilizados con el método Excute de los servicios web nos permitirán convertir de un formato a otro. Aunque hay que tener cuidado, ya que la conversión no siempre es directa.


Para terminar


Existen trucos para construir consultas en FetchXML, como utilizar el editor de consultas avanzadas del propio Microsoft CRM, guardar la consulta, y luego recuperar el FetchXML a través de los servicios web o de las vistas Filtradas. Pero, no os preocupéis, próximamente veremos que existen herramientas para crear consultas FecthXML de manera sencilla.


Para obtener más información sobre todo esto, os recomiendo que echéis un vistazo al SDK. En especial a la sección Building Queries del apartado de desarrollo con los Servicios Web de Microsoft CRM.


Bueno, espero que este “rollo” haya sido de utilidad para alguien. Dejad los comentarios, dudas, sugerencias, quejas y demás que queráis.


Un Saludo,


Marco Amoedo Martínez




3 comentarios en “Recuperar colecciones con los Servicios Web – RetrieveMultiple y Fetch”

  1. Hola Marco,

    me gustaría saber si con FetchXML se pueden hacer, lo que en SQL Server se conoce como CROSS JOIN,

    CROSS JOIN permite en SQL Server obtener como resultado la unión de 2 tablas que no guardan relación entre sí,

    lo necesito, o necesitaría buscar algo parecido para que las ofertas que necesito imprimir salgan con unas condiciones comunes a todas ellas,

    si no se puede tendremos que pensar en una solución alternativa,

    Saludos,

    Alejandro

  2. Hola Marco,

    estoy intentando un retrieve multiple pero el campo que tengo en el conditionExpression es de tipo PickList, como hago???

    He intentado varias maneras pero siempre me da el mismo error de SDK “Server was unable to process…”, estoy trabajando con .NET 2005 en C#…

    Gracias,

    Alexander Murillo.

  3. hola,

    estoy probando el retrieve multiple con tu codigo utilizando el link y me va muy bien, lo que pasa es que necesito hacer una UNION entre queries, como podria hacer eso? no como utilizar el link.joinOperator, puedes explicar algo de esto?

    gracias 😛

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *