Parte III BTS, .NET y COM+: Realizando transacciones en una orquestación con el componente construido

Podemos decir que esta es la parte mas sencilla de hacer, pero la mas interesante por la parte de las conclusiones y una importante característica que lamentablemente en estos momentos no pude hacer funcionar.

Pueden bajar el código aquí. Pueden ver la estructura aquí.


Construyendo la orquestación

La orquestación recibe un documento por medio del FILE Adapter. Dicho documento contiene todas las claves que serán insertadas a la tabla en la base de datos. La orquestación se encarga de iterar por el documento para obtener todos los ID e invocar el método de inserción. De esta manera podemos controlar la transacción al momento de mandar un documento con ID que ya existan en la tabla y generar la excepción que necesitamos.

La orquestación luce así:

La estructura del documento XML es la siguiente:

<

ns0:IDs xmlns:ns0="https://CallingEntServComponents.Activar">
<ID value="1" />
<ID value="2" />
<ID value="3" />
....
<ID value="n" />
</ns0:IDs>

Lo mas importante es que la orquestación esta configurada como Long Running Transaction (LRT) (ver imagen) y que en el Scope_1 donde se encuentra nuestro proceso de inserción esta configurado como Atomic Transaction (ver imagen).

Esto trae consigo el entender algunos de los términos mas importantes dentro de BTS, por lo que no esta por demás explicarlos. Vamos a suponer un ambiente donde se generan 100,000 documentos de manera diaria y que tiene que ser procesados por una orquestación. En dicha orquestación tenemos un paso que requiere por medio de un adaptador ir a un sistema externo que nos puede responder en 1 segundo o en 1 semana. Si analizamos el peor de los casos, en 1 semana podríamos llegar a tener hasta 700,000 orquestaciones (en memoria) esperando respuesta del sistema externo y las cuales están consumiendo recursos de nuestro servidor.

Por esto existe un proceso que se llama Dehydrate, el cual permite bajo ciertas reglas el "mover" una orquestación de la memoria a el MessageBox y así poder optimizar los recursos. Una vez que la orquestación ha sido guardada (con todo y su estado) en el MessageBox, existen ciertas condiciones que harán que la orquestación regrese a la memoria a esto se le llama Rehydrate y así poder continuar con su procesamiento en el punto exacto donde se había quedado, ver mas detalle sobre el proceso de Dehydrate/Rehydrate. En este proceso existen 2 importantes elementos:

1. Serialización. Este es un proceso que tiene como objetivo el poder transformar información de un formato de una fuente a otro formato para un destino, de tal forma que si tenemos un objeto en memoria, al momento de serializarlo podemos guardar la información de objeto en otro medio como lo puede ser una base de datos. El ejemplo por excelencia de la serialización son los Web Services. En este aspecto para poder guardar una orquestación, se realiza el proceso de serializar toda la información de la orquestación que esta en memoria para poder guardarla en el MessageBox (Dehydrate), para posteriormente poderla recrear nuevamente en memoria cuando se necesite (Rehydrate). Como una orquestación puede contener objetos previnientes de clases que nosotros definimos, debemos habilitar la serialización para esas clases y para eso existen 2 tipos de serialización:
a. Automática. Empleando el atributo Serializable a nivel de la clase. Como la información que deseamos guardar de la clase es su estado y su estado esta contenido en las variables miembro, al momento de aplicar este atributo automáticamente todos las variables miembro publicas y privadas se serializan por default a menos que se indique lo contrario empleando serialización manual o que se use el atributo NonSerialized a nivel variable miembro
b. Manual. Implementando la interfaz ISerializable en la clase. Básicamente lo que se tiene que haces es añadir un método llamado GetObjectData, con el cual nos damos a la tarea de guardar un diccionario de datos (llave-valor) y algunas otras operaciones. ej.

void

ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { // Serialize the desired values for this class info.AddValue("title", title); ....

Para mayor información acerca del proceso de serialización, pueden emplear las siguientes ligas

2. Persistencia. Se le llama persistencia al proceso de actualización del estado de un orquestación en el MessageBox. En este aspecto las persistencia es uno de los proceso mas costosos para escenarios de baja latencia, es decir para escenarios que requieren una respuesta en muy poco tiempo hablando en el orden de milisegundos, pues las persistencia consume mucho tiempo. Los puntos de persistencia están establecidos por la lógica que es implementada en la orquestación, pero que se pueden llegar a controlar hasta cierto punto. Si desean analizar cuantos puntos de persistencia tiene una orquestación, pueden emplear los Performance Counters. Dando click en Start, luego en Administrative Tools y luego en Performance. Dando click en el botón de Add, seleccionar XLANG/s Orchestrationsdel drop-down list de Performance Object y dar doble click en Persistence points (ver imagen).

Si por algún motivo no aparecen los Performance Counters, con el siguiente comando pueden registrarlos (tienen que reiniciar después de ejecutar el comando)

lodctr C:\Program Files\Microsoft BizTalk Server 2006\BTSPerfMonExt.ini

Ahora que establecimos elementos importantes dentro de BTS, explicaremos como es que el componente transaccional esta involucrado en todo eso.

Cuando se emplea un shape de Scope y se configura para emplear Atomic Transactions lo que sucede es que no hay puntos de persistencia dentro de ese scope y en ese aspecto nuestra clase no requiere ser serializada. Un pequeño quiz: cual es la orquestación que se ejecuta mas rápido A, B o C. Aunque por lo explicado anteriormente es sencillo ver cual correo mas rápido, el segundo lugar puede ser engañoso.

Si queremos emplear el componente pero no de manera transaccional dentro de nuestra orquestación y no queremos cambiar el código y esto es factible. Recordando la opción TransactionOption del atributo Transaction con el valor igual a Supported nos ofrece la flexibilidad de que si el componente encuentra una transacción participará en ella, de lo contrario no será transaccional. El shape de Scope permite establecer el valor de Transaction Type = None y de esta manera ya no existiría una transacción. El compilador marcará un error si se encuentra con una clase que no es serializable y no se encuentra en un scope con Atomic Transaction por las razones que acabamos de explicar. Solo faltaría incluir el atributo de Serializable en nuestra clase y modificar TransactionOption igual a Supported, pero el compilador de todas maneras les marcará error. Lo primero es darse cuenta de que nuestra tiene una herencia (ServicedComponent) y en esta situación la clase padre también necesita ser serializable. Si revisamos la clase ServicedComponent con click derecho sobre la clase con la opción de Go To Definition verán que si esta marcada como serializable, entonces que sucede??? Esto tendremos que dejarlo pendiente por el momento.

Solo nos falta un paso para poder probar nuestra orquestación con el componente transaccional. En la Parte II registramos el componente en COM+ y lo probamos con una aplicación de WinForms. No tuvimos que registrar en el GAC el componente transaccional debido a que el WinForm tiene la referencia y hace una copia local en el directorio de ejecutable, así pudo ubicar el componente, pero en este caso BTS necesita que registremos el componente en el GAC porque esa es la única manera en que los puede localizar. También pueden programar el comando del GAC en External Tools. Cuando corran el comando asegúrense de que este seleccionado el proyecto del componente que quieren registrar (ver imagen).

NOTA. Si se dieron cuenta el método que estamos empleando para la inserción pudo haber sido estático, pero habría algunas características como el pooling de objetos que ofrece COM+ que no podrían aprovecharse.


Probando el componente en la orquestación

Deben realizar el proceso de deployment de la orquestación, revisando el nombre del servidor y de la aplicación dentro de las propiedades del proyecto de BTS para no tener ningún problema para poder empezar la prueba.

Estoy incluyendo 2 botones, los cuales tiene como objetivo generar los 2 documentos con los que probaremos la orquestación. Para esto tiene que cambiar la path de los directorios de entrada. El primer documento no tendrá ningún error (la tabla debe estar vacía) porque contiene los IDs 1,2 y 3 y el segundo documento que contiene los IDs 4,5 y 1 y en el cual se generará un error por que el ID = 1 esta duplicado y por lo cual los IDs 4 y 5 no serán insertados.

Pueden ver el monitor de transacciones para ver como se va a comportando la transacción, tanto cuando la transacción es exitosa como cuando falla. Mandando varios documentos con ID 4,5 y 1 verán que el contador de transacciones abortadas se incrementa.

Notas sobre la orquestación

1. Estoy incluyendo un puerto de salida, donde lo único que hace es escribir exactamente el mismo documento del puerto de recepción.
2. Pueden observar la manera en que se obtiene el ID cuando se itera por el documento. De la expresión de xpath que se emplea

xpathstring = System.String.Format("string(/*[local-name()='IDs' and namespace-uri()='https://CallingEntServComponents.Activar']/*[local-name()='ID' and namespace-uri()=''][{0}]/ @ *[local-name()='value' and namespace-uri()=''])",i);

Lo mas importante es la función de string debido a que así obtenemos el valor del atributo value. Si omiten esa función, la expresión de xpath regresará el atributo como tal y no su valor. Para ver mas detalles acerca de xpath dar click aquí.

Para obtener la expresión de xpath para el atributo pueden emplear el esquema, en la ventana de propiedades buscar Instance Xpath (ver imagen).

3.

Cuando se genere un error, el mensaje se suspenderá y habrá una entrada en el Event Log. Deben considerar que no estamos incluyendo un manejo de excepciones dentro de la orquestación y debido a que en nuestro método de inserción estamos arrojando la excepción, por eso se suspende el mensaje.

4.

Coloque una entrada en el Evento Log para ver el ID que se esta insertando durante la iteración en shape que se llama Inserta Registro.


Adaptadores vs Componentes

Cuando platicamos del porque la necesidad de las transacciones, pudo haber traído la duda: y los adaptadores soportan transacciones? Los adaptadores están construidos en .NET y en se sentido queda del lado del desarrollador el incluir dicha funcionalidad. Si el adaptador si soporta transacciones entonces lo que sucederá es que si tenemos por ejemplo un adaptador que se comunica con una base de datos como Oracle y el puerto participa en una transacción (LRT o Atómica) y realiza operaciones como insertar, borrar o modificar registros entonces tendrá un comportamiento como el de nuestro componente dentro de un scope transaccional.

Existen cierto escenarios donde existirá la opción de emplear un adaptador o un componente cuando estemos hablando de un puerto de salida. No podemos establecer una regla absoluta en cuando usar un adaptador o un componente, pero hay varias cosas que pueden ayudar a responde la pregunta, por mencionar algunas:

  • Necesitas monitoreo?
  • Necesitas escalabilidad
  • Necesitas baja latencia
  • Hay adaptador para el sistema externo?

 

Con esto concluimos nuestro laboratorio para emplear componentes transaccionales con .NET en BTS.

Ver: