Mostrando entradas con la etiqueta Metadata. Mostrar todas las entradas
Mostrando entradas con la etiqueta Metadata. Mostrar todas las entradas

miércoles, 15 de septiembre de 2010

Buenas prácticas con los metadatos

Casi siempre en nuestros desarrollos necesitamos ir a los metadatos del CRM, para ver determinados atributos, tamaños, personalizaciones, etc.
Esto nos hará nuestros desarrollos mas dinámicos y adaptables, además de ser mas "inteligentes".
Para esto hay varias formas de acceder a los metadatos, y es necesario que se haga de una forma que afecte lo menos posible al rendimiento tanto de nuestro desarrollo como del servidor de CRM.
Es por esto que me pregunté que diferencias de rendimiento podría haber si en vez de recoger un atributo mediante el mensaje "RetrieveAttribute", recojo la entidad completa mediante el mensaje "RetrieveEntity" o incluso recogiendo todas las entidades con el "RetrieveAllEntities".
Para esto me desarrollé la siguiente aplicación de consola que me muestra las diferencias de tiempos de respuesta:
CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.OrganizationName = "AdventureWorks";

            token.AuthenticationType = 0;

            MetadataService metadataService = new MetadataService();
            metadataService.Url = "http://192.168.1.22:5555/MSCRMServices/2007/MetadataService.asmx";
            metadataService.CrmAuthenticationTokenValue = token;
            metadataService.Credentials = new System.Net.NetworkCredential("usuario", "contraseña", "CRM");
            
            RetrieveAllEntitiesRequest allEntitiesRequest = new RetrieveAllEntitiesRequest();
            allEntitiesRequest.RetrieveAsIfPublished = false;
            allEntitiesRequest.MetadataItems = MetadataItems.EntitiesOnly;

            DateTime dtinicioall = DateTime.Now;
            RetrieveAllEntitiesResponse allEntitiesResponse = (RetrieveAllEntitiesResponse)metadataService.Execute(allEntitiesRequest);
            TimeSpan dtall = DateTime.Now.Subtract(dtinicioall);
            Console.WriteLine("Tiempo en ejecutar RetrieveAllEntitiesResponse:"+dtall.TotalMilliseconds.ToString());

            RetrieveEntityRequest entityRequest = new RetrieveEntityRequest();
            entityRequest.RetrieveAsIfPublished = false;
            entityRequest.LogicalName = "contact";
            entityRequest.EntityItems = EntityItems.All;

            DateTime dtiniciocontact = DateTime.Now;
            RetrieveEntityResponse entityResponse = (RetrieveEntityResponse)metadataService.Execute(entityRequest);
            TimeSpan dtcontact = DateTime.Now.Subtract(dtiniciocontact);
            Console.WriteLine("Tiempo en ejecutar RetrieveEntityResponse de contact:" + dtcontact.TotalMilliseconds.ToString());


            DateTime dtinicioaccount = DateTime.Now;
            entityResponse = (RetrieveEntityResponse)metadataService.Execute(entityRequest);
            TimeSpan dtaccount = DateTime.Now.Subtract(dtinicioaccount);
            Console.WriteLine("Tiempo en ejecutar RetrieveEntityResponse de account:" + dtaccount.TotalMilliseconds.ToString());


            RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest();
            attributeRequest.EntityLogicalName = "contact";
            attributeRequest.LogicalName = "firstname";
            attributeRequest.RetrieveAsIfPublished = false;

            DateTime dtiniciofirstname = DateTime.Now;
            RetrieveAttributeResponse attributeResponse = (RetrieveAttributeResponse)metadataService.Execute(attributeRequest);
            TimeSpan dtfirstname = DateTime.Now.Subtract(dtiniciofirstname);
            Console.WriteLine("Tiempo en ejecutar RetrieveAttributeResponse de firstname de contact:" + dtfirstname.TotalMilliseconds.ToString());


            attributeRequest = new RetrieveAttributeRequest();
            attributeRequest.EntityLogicalName = "account";
            attributeRequest.LogicalName = "name";
            attributeRequest.RetrieveAsIfPublished = false;

            DateTime dtinicioname = DateTime.Now;
            attributeResponse = (RetrieveAttributeResponse)metadataService.Execute(attributeRequest);
            TimeSpan dtname = DateTime.Now.Subtract(dtinicioname);
            Console.WriteLine("Tiempo en ejecutar RetrieveAttributeResponse de name de account:" + dtname.TotalMilliseconds.ToString());

            Console.ReadLine();

Bien, esto me devuelve como resultado lo siguiente (los tiempos están en milisegundos):


Lo que se recoge claramente allí es que por ejemplo es mucho mas óptimo el recoger un solo atributo que recoger toda la entidad de "account", pero que pasaría si tenemos que recoger 15 atributos diferentes de "account"?. Allí ya nos convendría recoger la entidad completa...

Conclusión: es claramente recomendable el ejecutar los mensajes mas "ajustados" a lo que se quiera realizar, pero teniendo en cuenta la escalabilidad de nuestra aplicación, y el funcionamiento que se quiere como resultado.

Un abrazo,

lunes, 6 de septiembre de 2010

Como funciona la eliminación de un valor de un Picklist

En este post intentaré explicar lo que ocurre cuando se elimina un valor de un atributo de tipo "Picklist" (desplegable).
Es un poco curioso el funcionamiento, incluso un poco incompleto para mi gusto.
Vamos a ver un ejemplo e iremos revisando como queda la información en el SQL Server.
Primero vamos a crear un atributo nuevo en la entidad de "Caso" y lo añadimos al formulario. Será un desplegable con el nombre "new_categoria" con los valores "Uno", "Dos", "Tres":
Atributo picklist

Creamos un registro de "Caso" con el valor de Categoría en "Dos" y luego hacemos un par de consultas en el SQL Server para ver que nos queda almacenado físicamente en la base de datos y en la FilteredView:

SELECT * FROM IncidentExtensionBase
SELECT incidentid,New_Categoria,New_Categorianame FROM FilteredIncident

Resultados consulta

Hasta ahora, todo normal, ahora bien, imaginemos que necesitamos eliminar el valor del desplegable "Dos". Al eliminarlo nos aparece el siguiente mensaje:
"Si algún registro usa el valor que va a eliminar, actualice el registro de modo que use otro valor para poder guardar este cambio. Una vez quitado este valor, no puede usarlo para buscar este registro en la aplicación. ¿Desea continuar?"

Al leerlo parecería ser que lo que va a hacer será reemplazar los registros con este valor y dejarlo en NULL, pero en realidad no es así.
Borramos el valor y volvemos a hacer la misma consulta que antes:
Resultados consulta 2

Como se ve, en realidad en la base de datos seguimos teniendo el mismo valor, pero lo que ocurre, es que no puede recoger el texto del valor en la FilteredView, cosa que es normal ya que el valor se ha eliminado.

Imagínense que pasado el tiempo, añadimos un nuevo valor en este desplegable con el mismo valor (2) como índice, pero con otro texto que significa otra cosa:
Atributo Picklist 2

Volvemos a hacer la consulta y obtenemos:
Resultados Consulta 3

Con esto sacamos como consecuencia, que al eliminar un valor de un desplegable, los registros relacionados con este valor, siguen teniendo el mismo, y no se eliminan, de hecho si hacemos una búsqueda avanzada por ese atributo filtrando por que "Contiene datos", lo encontrará ya que tiene datos, pero es un dato "inexistente". Además hay que tener en cuenta que si creamos nuevos elementos en el desplegable en índices ya existentes, podríamos estar reemplazando y modificando un valor antiguo que ya no nos interesaba.

La conclusión final y consejo que dejo es que se tenga mucho cuidado con los desplegables y en especial al eliminar los valores, creo que sería una buena práctica no permitir a usuarios finales gestionar este tipo de atributos.

Un abrazo

martes, 16 de febrero de 2010

Como recoger todas las entidades del CRM

Al trabajar con CRM, muchas veces nos desarrollamos nuestras propias herramientas para hacer las tareas rutinarias mas sencillas, o para ofrecer a los usuarios la posibilidad de hacer cosas que el CRM de forma estandar no recoge.
Para esto puede ser que necesitemos a veces conocer todas las entidades que hay en el CRM, como por ejemplo para rellenear una lista o un desplegable.
Aquí les dejo un pequeño código en donde se recogen todas las entidades del CRM y se devuelven en un ArrayList:

public ArrayList GetEntidades(string servidor, string empresa, string usuario, string password, string dominio)
{
ArrayList arrEntidades = new ArrayList();

MetadataService metadataService = ConectaMetadata(servidor, empresa, usuario, password, dominio);

RetrieveAllEntitiesRequest allEntitiesRequest = new RetrieveAllEntitiesRequest();
allEntitiesRequest.RetrieveAsIfPublished = false;
allEntitiesRequest.MetadataItems = MetadataItems.EntitiesOnly;

RetrieveAllEntitiesResponse allEntitiesResponse = (RetrieveAllEntitiesResponse)metadataService.Execute(allEntitiesRequest);

foreach (EntityMetadata entity in allEntitiesResponse.CrmMetadata)
{
arrEntidades.Add(entity.SchemaName);
}
arrEntidades.Sort();
return arrEntidades;
}

MetadataService ConectaMetadata(string servidor, string empresa, string usuario, string password, string dominio)
{
//1) Recojo los datos de la entidad original
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.OrganizationName = empresa;
token.AuthenticationType = 0;

MetadataService metadataService = new MetadataService();
metadataService.Url = "http://" + servidor + "/MSCRMServices/2007/MetadataService.asmx";
metadataService.CrmAuthenticationTokenValue = token;
if (usuario != "")
{
metadataService.Credentials = new System.Net.NetworkCredential(usuario, password, dominio);
}
else
{
metadataService.Credentials = System.Net.CredentialCache.DefaultCredentials;
}
metadataService.PreAuthenticate = true;
return metadataService;
}

A la funcion "GetEntidades" hay que pasarle todos los datos de conexión y de autenticación, y dicha funcion devolverá el listado de todas las entidades del CRM.
La función "ConectaMetadata" conecta con el web service de metadatos, si el usuario es "vacío", se conectará con las credenciales por defecto (DefaultCredentials).

Un saludo!

jueves, 8 de octubre de 2009

Como copiar una entidad con todos sus atributos

Muchas veces necesitamos copiar toda la estructura de una entidad, incluyendo sus atributos, relaciones, etiquetas, etc.
Esto suele ocurrir cuando se tiene una entidad a la que no le hemos definido Notas o Actividades, y luego se desea modificar dicha propiedad (que solo se puede definir en la creación de la entidad).
He desarrollado una pequeña función que lo que hace es copiar toda la información de esquema (es decir atributos, relaciones y la definición general de la entidad) de una entidad original, a una nueva. Este desarrollo esta basado en la documentación que aparece en la SDK y es totalmente soportado.
Simplemente la llamada a la misma podria ser:

CopiarEntidad("localhost","Contoso","new_entidadoriginal", "new_nuevaentidadoriginal", true,true);
Los parámetros serían los siguientes:
1) Ip del servidor
2) Empresa
3) Nombre de esquema de la entidad original
4) Nombre de esquema de la nueva entidad a crear
5) true/false si la nueva entidad contendrá notas
6) true/false si la nueva entidad contendrá actividades

El código de la función es un poco largo así que no lo pego directamente aquí, sino que dejo este enlace para descargárselo.

El funcionamiento del código es bastante sencillo. En primer lugar, se recoge dinámicamente los metadatos de la entidad original. Luego creo la nueva entidad y le creo todos los atributos relacionados recorriendo los atributos de la entidad original. Finalmente recorre las relaciones de N:1 de la entidad (campos de lookup de la entidad) y crea las relaciones.

Si utilizan la función deben tener en cuenta:
1) La nueva entidad que se crea, se crea con el nombre para mostrar "copia de ". Esto es así ya que el CRM no permite tener 2 entidades con los mismos nombres para mostrar.
2) Utilizo la clase CrmServiceUtility para la creación de los "CrmLabel". Esta clase esta documentada en la SDK.
3) Sólo esta preparado para copiar una entidad personalizada.
4) Por ahora esta función no copia ni vistas ni personalizaciones de formulario.
5) Se deben añadir las referencias a los web services de CRM ("CrmSdk" y "metadataService")

Espero les sirva este código, intentaré ir mejorandolo de a poco en siguientes artículos.

Un saludo

domingo, 16 de agosto de 2009

Valores de los "Picklists" desde SQL Server

A veces tenemos la intención de acceder a los metadatos de CRM, pero simplemente para identificar los valores posibles de un campo de tipo "Picklist" o desplegable.
Esta información además de poder accederse con la propia SDK a través de los métodos disponibles de el MetadataService, también pueden ser accedidos de forma mas rápida y sencilla desde el propio SQL Server.
La consulta es muy simple (sólo habría que modificar los campos del "WHERE"):


SELECT AttributeValue, Value
FROM StringMap
WHERE AttributeName='accountcategorycode' AND ObjectTypeCode=1
ORDER BY DisplayOrder