1/2/10

MSMQ Custom Workflow Activity

En cualquier proyecto de CRM que se precie tiene que haber algo de integración, ¿no? Pues lo que me cuelgo aquí es un ladrillo para la integración. También es un ladrillo de post, por cierto. La idea es permitir que, desde el Workflow estándar, un usuario poderoso (power user) sea capaz de enviar una entidad de CRM a una cola de MSMQ en respuesta a algún evento de CRM. Puede que sea una idea disparatada, no lo sé, pero permite flexibilidad, es básica y se explica en un párrafo.




using System;
using System.Messaging;
using System.Web.Services.Protocols;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
// Microsoft Dynamics CRM namespaces
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Workflow.Activities;

namespace CrmADiario.Crm.Workflow
{

public class SendToQueueBase : SequenceActivity
{

public static void SendEntityToQueue(
ActivityExecutionContext ctx,
string queueAddress,
Type activityType,
string entityName,
Guid entityId)
{
try
{
var crmContext =
(IContextService)ctx.GetService(typeof(IContextService));
var crmService =
crmContext.Context.CreateCrmService();

var resp = (RetrieveResponse)crmService.Execute(
new RetrieveRequest
{
Target = new TargetRetrieveDynamic
{
EntityId = entityId,
EntityName = entityName
},
ReturnDynamicEntities = true,
ColumnSet = new AllColumns()
});

var message = new Message(resp.BusinessEntity);
var q = new MessageQueue(queueAddress);
q.Send(message, activityType.ToString());
}
catch (SoapException soapEx)
{
var errorMessage = String.Format(
"A CRM error occurred executing {0} in queue {1}\r\n{2}",
activityType,
queueAddress,
soapEx.Detail.OuterXml);
throw new StopWorkflowException(WorkflowCompletionStatus.Failed, 1024, errorMessage);
}
catch (Exception ex)
{
var errorMessage = String.Format(
"An error occurred executing {0} in queue {1}\r\n{2}",
activityType,
queueAddress,
ex.Message);
throw new StopWorkflowException(WorkflowCompletionStatus.Failed, 1024, errorMessage);
}


}
}
}



La clase base SendToQueueBase contiene la funcionalidad de ejecución de la actividad en el método SendToQueue que es usado desde los métodos Execute de las subclases. Por cierto, no he sido capaz de fabricar una sola clase que haga el trabajo así que he tenido que fabricar (copiar y pegar, vamos) tantas clases de actividad como entidades de CRM se quieran enviar a MSMQ.




using System;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Workflow;

namespace CrmADiario.Crm.Workflow
{
[CrmWorkflowActivity("Send contact to queue", "MSMQ Utilities")]
public class SendContactToQueue : SendToQueueBase
{

public static readonly DependencyProperty ContactProperty =
DependencyProperty.Register("Contact", typeof(Lookup), typeof(SendContactToQueue));

public static DependencyProperty QueueAddressProperty =
DependencyProperty.Register("QueueAddress", typeof(String), typeof(SendContactToQueue));

[CrmInput("Queue address")]
public String QueueAddress
{
get { return (String)GetValue(QueueAddressProperty); }
set { SetValue(QueueAddressProperty, value); }
}

[CrmReferenceTarget("contact")]
[CrmInput("Contact")]
public Lookup Contact
{
get { return (Lookup)GetValue(ContactProperty); }
set { SetValue(ContactProperty, value); }
}

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
SendEntityToQueue(executionContext, QueueAddress, GetType(), "contact", Contact.Value);
return base.Execute(executionContext);
}
}
}


Esta clase derivada envía un contacto serializado como una DynamicEntity a la cola de MSMQ que se indique. Y para el resto de entidades, copiar, pegar y cambiar donde dice "contact" por el nombre de la entidad que sea. No es muy elegante, pero es hasta donde he llegado. Si a alguien se le ocurre cómo generalizar esto un poco, ¡adelante!

1 comentario:

Zayas dijo...

Viendo el código me he dado cuenta que heredas de SequenceActivity en dos niveles:

SequenceActivity -> CustomBaseClass -> ActivityClass

Es curioso pero yo he probado ha realizar esta herencia en dos nieveles para crear una clase general para actividades de workflow. Cuando he ido a registrar estas actividades he recibido un error de la platarforma de CRM y he tenido que optar por heredar directamente la actividad de workflow desde la clase SequenceActivity para que pudiera ser registrada en CRM.