Ya hemos cubierto la teoría del contexto de ejecución. Ahora es momento de profundizar en cómo usar IOrganizationService para realizar operaciones reales en Dataverse. Este servicio es tu interfaz principal para crear, leer, actualizar y eliminar registros desde tus plugins.
Objetivos de aprendizaje
- Realizar operaciones CRUD completas con IOrganizationService
- Dominar QueryExpression para consultas complejas
- Aplicar mejores prácticas de rendimiento en operaciones
- Evitar errores comunes que afectan al sistema
Obteniendo el servicio de organización
El IOrganizationService no viene directamente del IServiceProvider. Primero obtienes una fábrica, y luego usas la fábrica para crear el servicio indicando en contexto de qué usuario quieres operar.
// Obtener la fábrica de servicios
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)
serviceProvider.GetService(typeof(IOrganizationServiceFactory));
// Crear servicio con permisos del usuario que ejecuta el plugin
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
Esta distinción es importante. El servicio que obtienes respeta los permisos del usuario especificado. Si pasas context.UserId, las operaciones que hagas estarán limitadas a lo que ese usuario puede hacer. Si el usuario no tiene permiso para ver ciertos registros, tu plugin tampoco podrá verlos usando este servicio.
El servicio SYSTEM
Si pasas null al crear el servicio, obtienes un servicio con permisos de SYSTEM:
// Servicio con permisos SYSTEM - ignora seguridad de usuario
IOrganizationService systemService = factory.CreateOrganizationService(null);
Este servicio puede hacer cualquier cosa, ignorando roles de seguridad y restricciones de usuario. Es útil para procesos administrativos, pero úsalo con cuidado. Documenta siempre por qué necesitas SYSTEM en lugar del usuario normal.
Operaciones básicas: CRUD
Create: Crear registros
Entity nuevaCuenta = new Entity("account");
nuevaCuenta["name"] = "Contoso Corporation";
nuevaCuenta["telephone1"] = "+1 555 123 4567";
nuevaCuenta["creditlimit"] = new Money(50000m);
nuevaCuenta["industrycode"] = new OptionSetValue(6); // Business Services
Guid nuevaId = service.Create(nuevaCuenta);
trace.Trace($"Cuenta creada con ID: {nuevaId}");
Retrieve: Obtener un registro
// SIEMPRE especifica solo las columnas que necesitas
ColumnSet columnas = new ColumnSet("name", "telephone1", "primarycontactid");
Entity cuenta = service.Retrieve("account", cuentaId, columnas);
string nombre = cuenta.GetAttributeValue<string>("name");
string telefono = cuenta.GetAttributeValue<string>("telephone1");
EntityReference contacto = cuenta.GetAttributeValue<EntityReference>("primarycontactid");
Nota el uso de GetAttributeValue en lugar de acceder directamente al diccionario. Este método devuelve null si el atributo no existe, evitando KeyNotFoundException.
Update: Actualizar registros
// Solo incluye los campos que cambian
Entity actualizacion = new Entity("account", cuentaId);
actualizacion["telephone1"] = "+1 555 987 6543";
actualizacion["creditlimit"] = new Money(75000m);
service.Update(actualizacion);
Es importante incluir solo los campos que modificas. Si incluyes campos sin cambios, el sistema igual los procesa, disparando plugins innecesariamente y creando registros de auditoría vacíos.
Delete: Eliminar registros
service.Delete("account", cuentaId);
Consultas con RetrieveMultiple
Para obtener múltiples registros, usas RetrieveMultiple con QueryExpression:
QueryExpression query = new QueryExpression("contact");
query.ColumnSet = new ColumnSet("fullname", "emailaddress1", "telephone1");
// Filtro: solo contactos de una cuenta específica
query.Criteria.AddCondition("parentcustomerid", ConditionOperator.Equal, cuentaId);
// Ordenar por nombre
query.AddOrder("fullname", OrderType.Ascending);
// Limitar resultados
query.TopCount = 50;
EntityCollection contactos = service.RetrieveMultiple(query);
foreach (Entity contacto in contactos.Entities)
{
string nombre = contacto.GetAttributeValue<string>("fullname");
trace.Trace($"Contacto: {nombre}");
}
Mejores prácticas de rendimiento
Nunca uses ColumnSet(true). Esto recupera TODOS los campos del registro, incluyendo campos calculados, campos grandes, y campos que no necesitas. Es una de las causas más comunes de plugins lentos.
// MAL - trae todos los campos
Entity cuenta = service.Retrieve("account", id, new ColumnSet(true));
// BIEN - solo lo que necesitas
Entity cuenta = service.Retrieve("account", id, new ColumnSet("name", "telephone1"));
Evita Retrieve dentro de loops. Si necesitas datos de múltiples registros, usa una sola consulta con IN:
// MAL - N llamadas a la base de datos
foreach (Guid id in listaDeIds)
{
Entity cuenta = service.Retrieve("account", id, columnas);
}
// BIEN - una sola llamada
QueryExpression query = new QueryExpression("account");
query.ColumnSet = columnas;
query.Criteria.AddCondition("accountid", ConditionOperator.In,
listaDeIds.Cast<object>().ToArray());
EntityCollection todos = service.RetrieveMultiple(query);
Solicita solo las columnas necesarias. Ya lo mencioné, pero vale la pena repetirlo. ColumnSet(true) es el enemigo del rendimiento.
Limita los resultados de consultas. Si solo necesitas verificar si existe algún registro, usa TopCount = 1. No traigas mil registros para comprobar que hay al menos uno.
Considera el impacto de tus operaciones. Un Update dentro de un loop puede disparar plugins en cada iteración. Si vas a actualizar cientos de registros, considera usar ExecuteMultipleRequest para agrupar operaciones.
Puntos clave
- Obtén IOrganizationService del factory, especificando el userId para respetar permisos
- CreateOrganizationService(null) da acceso SYSTEM, úsalo con precaución
- Siempre especifica ColumnSet con las columnas necesarias, nunca ColumnSet(true)
- En Update, incluye solo los campos que cambian, no todo el registro
- Usa RetrieveMultiple con IN en lugar de loops con Retrieve
- Limita resultados con TopCount cuando no necesites todos los registros