Archivo de la categoría: C#

¿Integración con Microsoft Dynamics AX? (II)

En mi anterior post, os explicaba las diferentes opciones que tenemos para poder conectar con Microsoft Dynamics AX. En esta ocasión, y siguiendo el hilo, nos centraremos en ver cómo podemos hacer eso con el .NET Business Connector que no es mas que una librería que nos permite acceder a AX.

Para poder hacer uso de este conector deberemos proceder a realizar la instalación del BC en la maquina cliente de dónde vamos a desarrollar y en la máquina de dónde vamos a desplegar nuestro software. Si debemos desplegar un software cliente/servidor la comunicación debería ser a través de WCF y centrar toda la conexión hacía AX desde el servicio web en caso contrario, si nuestro software esta hospedado en un IIS por ser entorno web, el propio IIS es quien hará la conexión hacía AX. El entorno, en este caso, es muy importante y debemos tener este en cuenta antes de realizar cualquier solución conectada con AX pues el Business Connector tan solo funciona con un usuario del Active Directory desde dentro del dominio del Active Directory, de otra forma, no encontraría la instancia de AOS con la que trabaja Microsoft Dynamics AX.

Al momento de realizar la instalación en la parte del servidor, teniendo en cuenta que la instalación de Dynamics esta realizada correctamente y sobretodo el Active Directory configurado en modo nativo. Por defecto la instalación del Business Connector en el servidor de AX ha creado un usuario domain\bcproxy. Es recomendable indicar a este usuario que su contraseña no caduque nunca.

Si procedemos a realizar la instalación en el servidor o bien en el ordenador de desarrollo deberemos instalar el siguiente cliente,

Install_BC_AX

 

 

 

 

 

 

 

Si nuestra aplicación es una aplicación web, deberemos añadir la cuenta proxy al grupo local de Windows IIS. Si estás usando el BC desde SharePoint, deberás agregar la cuenta al grupo local de Windows SharePoint y así, con todos los diferentes grupos que tengas y que quieran conectar con AX desde un IIS de la misma red y dominio dónde esta instalado el AX. El usuario debe estar configurado en el Application Pool que queramos usar.

El último paso es configurar el usuario de negocios de proxy de conector dentro de la configuración AX para asegurarnos que tiene el usuario correctamente y el AOS funcionará correctamente,
1. Inicio Microsoft Dynamics AX (Inicio> Todos los programas> Microsoft Dynamics> Microsoft Dynamics AX)
2. Abra el cuadro de diálogo de negocios de proxy de conector: Administración> Configuración> Seguridad> Business Connector Proxy.
3. En el cuadro Alias​​, escriba el alias. En el cuadro de dominio de red, introduzca el dominio del usuario y luego cerrar el cuadro de diálogo.

Ahora ya lo tenemos todo, tenemos el BC instalado en la parte cliente, el BC configurado dentro de AX y el identity en el application pool para conectar con el AX en caso que sea web o bien sea un WCF. Para poder probar si todo va bien, en la máquina cliente, instala un script de power shell dónde podréis ver toda la información de vuestra instalación. Lo puedes descargar en el siguiente enlace PS BC. Si lo ejecutamos lo que veremos es el nombre de nuestro AOS y los datos de ejemplo,

BC_PS

 

 

 

 

Si esto funciona significa que desde el servidor dónde desplegaremos nuestra aplicación hasta la instancia de AOS, todo esta funcionando correctamente.
En nuestro anterior post hemos creado una clase que es la que nos servirá de intercambio entre Microsoft Dynamics y .NET a través del Business Connector.

En esta misma solución lo que haremos será agregar la librería Microsoft.Dynamics.BusinessConnectorNet.dll que se encuentra en C:\Program Files\Microsoft Dynamics AX\50\BusinessConnector\Bin en el caso de 64 bits o Program Files (x86) en el caso de 32 bits. Perfecto, ahora escribiendo unas pocas líneas de C# o de VB, podremos consumir nuestro método de AX.

using System;
using Microsoft.Dynamics.BusinessConnectorNet;
using DataExchange;

namespace ConsoleForAx
{
    class Program
    {

        static void Main(string[] args)
        {
            Axapta axClient = null;
            axClient = new Axapta();
            axClient.Logon(null, null, null, null);
            object customerResult = axClient.CallStaticClassMethod("DataExchange", "GetCustomer");
            axClient.LogOff();
            Customer cust = (Customer)customerResult;

            Console.WriteLine(cust.Name);
            Console.WriteLine(cust.Nif);
            Console.WriteLine(cust.Email);

            Console.ReadLine();
        }
    }
}

Por orden, tendremos el nombre de la clase de AX, el nombre del método a llamar de AX y por último simplemente obtenemos el objeto customerResult.
Si os fijáis en ningún momento hago ningún parser entre AX y .NET si no que el BC ya se encarga de obtener los datos de AX y devolver la clase de .NET cómo object y lo único que debemos hacer es indicar que nos convierta este object a un objeto conocido cómo Customer.

Aquí podéis obtener todo el código fuente de estos dos post; AX_Integration_NET.

Ya hemos realizado una pequeña integración entre Microsoft Dynamics y .NET :) Espero que os haya gustado y en el siguiente veremos cómo consumir listados y devolver los mismos a través del BC.

¿Integración con Microsoft Dynamics AX? (I)

Muchas son las veces que en los diferentes proyectos que nos solicitan o en los que participemos nos debemos integrar con el ERP del cliente.

En ocasiones esta fachada para podernos integrar nos puede venir preparada y en otras ocasiones no. Dada la complejidad de un ERP y la de multitud de procesos que puede llevar a cabo para simplemente guardar un cliente, se desaconseja hacer operaciones de escritura en sus DB de SQL directamente.

Por este motivo y también por seguridad para todos, en Microsoft Dynamics AX, existen diferentes mecanismos de integración,

–  Services and Application Integration Framework (AIF): Expone la lógica de negocio interna a sistemas externos para que pueda ser consumida mediante servicios de Windows Communication Foundation (WCF).

–  .NET Business Connector: Mediante la librería permite a otras aplicaciones obtener y enviar datos además de poder usar la lógica de negocio.

–  .NET Framework: Con su integración con X++, permite llamar a ensamblados manejados por el CLR y proviene de clases generadas con C# o VB para ser usadas desde X++. Estas clases las debes generar con Microsoft Visual Studio.

En este primer capítulo explicaremos cómo crear una librería para que esta pueda ser usada posteriormente desde AX.

Desde AX, si eres programador, vas a usar X++ para escribir las diferentes instrucciones que necesites para poder implementar la lógica de negocio dentro del ERP y una vez realizadas las diferentes operaciones, deberás devolver un resultado.
Para los sistemas externos que se quieren integrar con el ERP, y más en la actualidad dónde todo lo que es tipado es mucho más simple de manejar, es muy probable que se te solicite que los datos que devuelvas sean objetos de .NET.

Lo primero que necesitáis, además del método desarrollado en X++, es tener la librería de objetos a devolver. Para ello lo primero que debemos hacer, es desarrollar esta librería en Visual Studio con C# o VB. En ella definiremos los objetos POCO que se usarán simplemente cómo objetos de transferencia de objetos (Si, DTO’s).
Si usas Microsoft Dynamics AX 2009 debes usar el Framework 2.0 y si usas Microsoft Dynamics AX 2012 debes usar el Framework 4.0.

Como veis tan solo se trata de crear una nueva biblioteca de clases con la definición de objetos que necesitemos con sus propiedades  get y set. Dada mi experiencia con estos sistemas, os aconsejo que hagáis heredar todas las clases de intercambio de datos de otra clase  para que esta pueda contener códigos de error, mensajes de error o mensajes informativos. Esto es debido a que el manejo con la primera forma que veremos de hacer la integración devuelve excepciones de la propia librería  pero no puede especificar errores personalizados de la lógica de negocio, por ejemplo en un método de autentificación, informar de un error  personalizado con código “10001” que signifique que la contraseña es incorrecta, de ahí, la clase propuesta de ResultData.

DataExchange_Solution

Una vez tengamos la librería realizada la debemos compilar y desde el AOT de AX agregar esta referencia.
Es muy importante que esta librería para que pueda ser cargada correctamente se registre en el GAC de los ensamblados de Windows  y se firme con una clave segura para garantizar la existencia única de la misma.

Custom_Reference

Posteriormente, para usar la misma deberemos ir al AOT en nuestras Classes y crear una Nueva Class poniendo su declaración cómo  public.

Custom_Class

Por último, dentro, crearemos un método llamado GetCustomer que hace uso de nuestra DLL de .NET desde dentro de X++.

Custom_Method

Está claro que dentro de este método deberemos realizar la lógica de negocio que necesitemos para devolver el cliente o incluso pasar parámetros.

¿Fácil verdad?
En el próximo capítulo veremos cómo llamar este método desde .NET mediante el Business Connector.

Servicios de almacenamiento en Microsoft Azure: Colas

Muchos son los servicios de Microsoft Azure tanto para PAAS, SAAS cómo para IAAS. En este post me voy a centrar en una pequeña parte de PAAS cómo son los servicios de almacenamiento en esta paltaforma.

Este servicio proporciona varias soluciones,

Blobs: Almacenamiento de grandes bloques de texto o ficheros de gran tamaño; texto, vídeos, audio e imágenes accesible fácilmente mediante una API REST.

Tablas: Almacenamiento NOSQL para datos preparado para que se puede escalar automáticamente y dar cabida a una enorme cantidad de datos. Los datos son accesibles mediante cualquier lenguaje de programación .net, java, node.js, python, rest…

Colas: No son mas que un sistema de almacenamiento FIFO (First In First Out) donde podemos guardar mensajes que debe ser procesadas en segundo plano o en un momento concreto y diferente del proceso principal.

Cabe destacar que todos los servicios de almacenamiento de Azure nos permiten tener una redundancia de datos local (varias réplicas en la misma región), redundancia de datos geográfica (varias réplicas en diferentes regiones)  para dar más eficiencia en la trasferencia de datos y su durabilidad.

Cómo toda interacción con Azure, es sencillo su uso aunque debemos tener en cuenta varios aspectos. Lo primero que haremos será conectar y crear un cliente para conectar con nuestra cola de Azure,

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference("nombredelacola");

Estas líneas anteriores nos permiten tener conexión con nuestra cuenta de Storage de Azure, tener un cliente para la conexión con ella y un cliente para manejar la cola. Para manejar la inserción de datos en ella debemos tener en cuenta algunos campos,

Dequeue Count: Número de veces que el mensaje ha sido leído de la cola.
Expiration Time: Momento en el que el mensaje expirará y será eliminado automáticamente por Azure.
Next Visible Time: Momento en el que el mensaje dejará de ser invisible y volverá a poder ser leído al obtener el siguiente mensaje de la cola.
Message: El mensaje con su contenido dentro de la cola.

CloudQueueMessage msg = new CloudQueueMessage("RowKey: 1, RowValue: 55, Action: Send");
queue.AddMessage(msg);

El mensaje es totalmente personalizado, es decir, el Message que hemos creado con “RowKey: 1, RowValue: 55, Action: Send” es porqué el lector de la cola sabrá interpretar este mensaje y podría ser totalmente otro formato y de mayor tamaño, por ejemplo, “PK:1#Value:55#Accion:S” u cualquier otro. Además el Message puede ser texto o byte[] por lo que podréis decidir que almacenar en él.

Si no se especifica parámetros en el Expritation Time ni en el Next Visible Time estos son el de 60 minutos para el primero y un DateTime.Now para el segundo.

Para obtener los mensajes de la cola únicamente es necesario hacer un GetMessage,

CloudQueueMessage msg = queue.GetMessage();

El GetMessage permite pasar cómo parámetro un TimeStamp que es el tiempo que va aparecer oculto el mensaje en la cola y nadie podrá leerlo, si no se especifica ningún tiempo el mensaje leído permanece 3 segundos oculto de la cola.

Tenemos que gestionar un aspecto muy importante, la visibilidad del mensaje.  La lectura de un mensaje no destruye el mensaje y puede ser leído desde diferentes sitios u otra vez por el mismo desencolado y puede ser un problema. Esto es debido a que un mensaje al ser leído es ocultado pero no eliminado por si sucede algún error en la lectura de ese mensaje, en caso de error, el mensaje volvería aparecer en la cola para ser procesado otra vez.

Por eso, es muy importante gestionar el tiempo de ejecución de las tareas y tener en cuenta el tiempo en que es invisible una tarea (un mensaje). Un mensaje debe ser eliminado de la cola únicamente cuando esta se ha procesado correctamente o cuando su Dequeue Count sea de un valor especificado, es decir, lo hemos leído demasiadas veces.

Si procesamos un mensaje y este no se ha podido procesar o no es su momento; imaginar una cola que debe ejecutar el mensaje en un momento determinado y al procesar su verificación determina que precisa un minuto más. Lo que debemos hacer es actualizar el mensaje,

queue.UpdateMessage(msg, TimeSpan.FromSeconds(60), MessageUpdateFields.Visibility);

Esta actualización lo que hace es realizar una actualización en el mensaje y le indicamos que queremos que durante un minuto más ese mensaje no sea visible y por lo tanto, hasta pasado ese tiempo no se podrá obtener y su Dequeue Count augmentará en una unidad. Esta también lo hara por cada GetMessage.

Como veis el último parámetro es una enumeración que es Visbility o bien Content dónde indicamos si estamos cambiando el contenido del mensaje o bien la visibilidad. En este caso la visibilidad.

Cuando el mensaje sea procesado y la tarea ejecutada correctamente, debemos eliminar ese mensaje de la cola para que no vuelva a ser procesado,

queue.DeleteMessage(msg);

Bien, con esta explicación, podemos gestionar de forma muy fácil, sencilla y rápida nuestra cola mediante un WebRol con una tarea en segundo plano que gestione la cola en de Microsoft Azure.

Este sistema es altamente recomendable en sistemas con muchas usuarios dónde necesitamos procesar tareas en segundo plano, que tengan una complejidad de procesamiento alta o bien necesitemos que esas sean procesadas y escalables rápidamente y de forma seguro.

Happy enqueue & dequeue! :)

El uso de Data Transfer Objects (DTO’s)

En muchas de nuestras aplicaciones, estamos usando ORM cómo Entity Framework. Los objetos (clases) de nuestra modelo E/R (DB) son mapeados mediante un modelo orientado objeto que representa sus relaciones físicas en formato lógico.

Estos objetos, que se pueden abstraer cómo entidades POCO de este ORM, pueden llegar a contener muchas relaciones entre los diferentes objetos dependiendo de la complejidad de nuestro modelo E/R.

En las tecnologías visibles para nuestros usuarios, es decir, las interfaces de usuario (UI) cómo puede ser ASP.NET MVC, Aplicaciones móviles… entre otros, necesitamos dar una experiencia rica, ágil y rápida teniendo en cuenta que en muchas ocasiones nuestro modelo de datos es complejo y la interfaz del usuario es sencilla.

Justo en este momento es dónde entran en contexto los Objeto de transferencia de datos (DTO con sus siglas en inglés) dónde nace (no es nuevo, este concepto lleva años con nosotros pero gana peso en las aplicaciones actuales :)) con el objetivo de minimizar el coste de la comunicación entre procesos y por lo tanto, la transferencia de datos entre estos en cada una de sus llamadas cliente – servidor minimizando así su tiempo de respuesta y transportando únicamente los datos necesarios para la operación destino.

La diferencia entre los objetos DTO y los objetos de negocio es que los primeros únicamente tienen el comportamiento de transformar los datos desde el origen y entregar los datos a su destino. Estos objetos son simples y sin lógica de negocio, es decir, son objetos que no están contenidos en nuestro dominio si no que se nutren de su resultado.

A la conclusión que podemos llegar es que si usamos las entidades POCO cómo DTO nos saltamos justo la S (SPR) de los principios SOLID que os explicava en un anterior post y le damos 2 responsabilidades diferentes a nuestro objeto: sirve de entidad del ORM y por otro de transferencia de datos.

Además darle dos responsabilidades a un objeto, obligas a que los procesos envíen gran parte la información que almacenas en la base de datos, y quizá solo hace falta una parte de esa información. Y con eso estarás enviando demasiada o poca información creando procesos muy poco eficientes.

¿Cuándo se debe usar DTO?
– Cuándo debamos reducir el nombre de llamadas al servidor.
– Cuándo debamos considerar el rendimiento y la latencia de las comunicaciones de la red.
– Cuándo debamos minimizar la transferéncia de datos.

¿Cómo se usan los DTO?
Los DTO no son mas que clases sencillas con propiedades get y set. Estas clases son llenadas en desde la capa de aplicación que estas obtienen los datos desde el repositorio a través de una llamada a nuestra DB.

Los DTO deben ser definidos en una librería a parte dentro de nuestro dominio pero sin formar parte de él pues carecen de comportamiento y en una capa superior, dónde recibimos la respuesta de la aplicación, debemos realizar la conversión de nuestra respuesta de entidad POCO a nuestro DTO.

Para ello podemos usar diferentes métodos pero en .NET y con las últimas versiones podemos usar dos formas de hacer esta conversión,
– La primera de ellas es mediante Automapper que es una librería que es capaz de convertir un objeto a otro mediante un mapeo (Lo podéis instalar mediante Nugget). Si en ambos objetos, las propiedades get y set tienen el mismo nombre, el mapeo es muy sencillo pero si no, nos permite manejar esa conversión de forma muy sencilla.

public static ContactDto Binding(Contact contact)
{
  ContactDto contactDto = new ContactDto();
  Mapper.CreateMap<Contact, ContactDto>()
        .ForMember(c => c.Name, dto => dto.MapFrom(co => co.Person.Name))
        .ForMember(c => c.FirstName, dto => dto.MapFrom(co => co.Person.FirstName))
        .ForMember(c => c.LastName, dto => dto.MapFrom(co => co.Person.LastName))
        .ForMember(c => c.Gender, dto => dto.MapFrom(co => co.Person.Gender))
        .ForMember(c => c.IdPerson, dto => dto.MapFrom(co => co.Person.IdPerson))
        .IgnoreAllNonExisting();
  Mapper.AssertConfigurationIsValid();
  contactDto = Mapper.Map<Contact, ContactDto>(contact);
  return contactDto;
}

A grandes rasgos, lo que nos permite hacer el objeto Mapper es crear un mapeo entre dos clases (Contact y ContactDto), le indicamos mediante .ForMember cómo debe ser la conversión (solo es necesario si es diferente) y le indicamos que IgnoreAllNonExisting, es decir, que no mapea lo que no exista. Finalmente hacemos un AssertConfigurationIsValid  que nos devuelve una excepción si no puede hacer la conversión.

Para terminar, hacemos el Map y obtenemos nuestro DTO con los datos que vamos a usar o representar y descartamos el resto.
En otro post, explicaré cómo usar Automapper a fondo pues da para escribir más de un post.

– La segunda de ellas, es mediante una consulta Linq to Objects y escojer que queremos devolver y que no.

var report = await _ourLayerApplication
.Contact
.Where(cnt => cnt.SequenceNumber == contactCode)
.Select(cnt => new ContactDTO
{
  Name = cnt.Person.Name,
  FirstName = cnt.Person.FirstName,
  LastName = cnt.Person.LastName,
  Gender = cnt.Person.Gender,
  IdPerson = cnt.Person.IdPerson
}).SingleOrDefaultAsync();

¿Cuál usar?
Personalmente, ambas formas nos proporcionan la misma potencia aunque al estar contextualizado en el entorno de EF y .NET la segunda forma no necesita del uso de librerías de terceros y la primera, debemos usar una librería externa y que a nivel de rendimiento, no nos oferecerá el mismo rendimiento que una consulta Linq to objects nativa de .NET.

En todo caso y resumiendo, lo que no debemos hacer es devolver los objetos obtenidos mediante consultas a la DB si usamos ORM cómo EF pues contienen sus relaciones y estas, pueden ser complejas, pesadas y transmitir datos que no son necesarios para nuestra aplicación, sobretodo, si su destino son entornos que deben ser fluidos cómo la web, el cloud y las app.

A desarrollar :)
Saludos!

MTOM cómo codificación de ficheros en WCF

MTOM (Mecanismo de optimización de transmisión del mensaje), como su nombre indica, es un mecanismo para transmitir datos binarios grandes con basicHttpBinding. De forma predeterminada, basicHttpBinding envía y recibe los mensajes en XML y este tipo de encoding del mensaje debe ser habilitado en la configuración de nuestro proyecto cómo veremos más adelante.

El objetivo de usar MTOM en vez de otro encoding de mensajes, no es otro que optimizar la transmisión de grandes cargas binarias. Al contrario, esta codificación cuándo se usa para pequeñas cargas añade una sobrecarga innecesaria en nuestro mensaje por lo que debemos saber cuándo debemos usarlo, aunque teniendo en cuenta los documentos que se usan hoy en día y la calidad de las fotografías seguramente lo uséis la gran mayoría de las veces.

Antes de empezar os recomiendo que os bajéis el código de ejemplo y lo vais siguiendo con lo que explicaré. Como veréis dentro de la solución que he creado, hay dos carpetas: Cliente y Servidor. La primera de ellas tendrá un proyecto WinForms que nos servirá para subir/bajar archivos y la segunda el servicio WCF. Lo primero que vamos a ver es un sencillo servicio en WCF que nos permitirá subir y bajar archivos de cualquier tipo. Es facil, la interface IService.cs tendrá dos métodos; uno para subir ficheros con el atributo IsOneWay = true para indicar que este método no va a devolver mensaje de respuesta y otro para obtener el fichero. Además dos clases, PeticionDescarga y Fichero. Esta última implementara la interfaz IDisposable para asegurarnos que al terminar la transmisión podremos vaciar el flujo de datos.

Cómo MTOM transmite el mensaje por SOAP debemos formatear las clases cómo tal, así que la clase Fichero contendrá las propiedades NombreFichero y Peso con el atributo [MessageHeader(MustUnderstand = true)] que nos indica que ambas propiedades pertenecerán a la cabecera SOAP (Header) del mensaje y nos permite especificar si el Header deberá ser analizado y comprendido por el destinatario o no. Sí esta a true, al enviar los datos se espera que el receptor comprenda la cabecera y los tenga en el DataContract. La propiedad FicheroBytes tiene el atributo [MessageBodyMember()] que nos indica que será el cuerpo del mensaje.

Ahora viene lo complicado, ¿Cómo hacemos que nuestro servicio transfiera sus datos a través de la codificación MTOM? Configurando el binding de nuestro servicio. Para ello nos situamos en el archivo Web.config y nos deberá quedar, además de las otras etiquetas, la configuración del servicio así:

<system.serviceModel>
<
bindings>
<
basicHttpBinding>
<
bindingname=MTOMBindingmaxReceivedMessageSize=1048576messageEncoding=MtomtransferMode=Streamed>
</
binding>
</
basicHttpBinding>
</
bindings>
<
services>
<
servicename=ServicebehaviorConfiguration=ServiceBehavior>
<
endpointaddress=“”binding=basicHttpBindingbindingConfiguration=MTOMBindingcontract=IService/>
</
service>
</
services>
<
behaviors>
<
serviceBehaviors>
<
behaviorname=ServiceBehavior>
<
serviceMetadatahttpGetEnabled=true/>
<
serviceDebugincludeExceptionDetailInFaults=true/>
</
behavior>
</
serviceBehaviors>
</
behaviors>
</
system.serviceModel>

Como veís, le indicamos que la codificación del mensaje será con MTOM y a través del transporte http. Las propiedades a destacar y de importacia són maxReceivedMessageSize que nos indica el tamaño máximo del mensaje total expresado en bytes y transferMode que lo estableceremos a Streamed indicando así que no será un transporte almacenado en un búfer si no que será transmitido, es decir, solo deberán ser almacenadas las cabeceras SOAP y el cuerpo del mensaje será transmitido cúando se haga la petición. Cómo le hemos indicado que será transmitido ahora maxBufferSize deberá ser menor o igual que maxReceivedMessageSize ya que a diferencia de este, maxBufferSizeunicamente limita el tamaño máximo de la cabecera SOAP. Para que lo podaís entender mejor, maxBufferSize configura el búfer de WCF que almacena la cabecera del mensaje mientras se procesa el mensaje en el extremo del cliente y no la totalidad del mismo mensaje. Así pues, nuestra cabecera SOAP contendrá el nombre del fichero y el peso tal y cómo hemos configurado la interficie IService.

El servicio no tiene más, lo que haremos ahora es la implementación de la interface y el código de los métodos que no explicaré ya que son muy básicos, aunque podéis preguntar si tenéis cualquier duda por supuesto y además lo más complejo lo he comentado. Algo simple, dentro de Service.cs tendremos implementados los métodos SubirFichero y DescargarFichero.

El método SubirFichero, recibirá una instancia de la clase Fichero con la información del archivo a subir: su nombre, tamaño y el contenido en un stream de bytes. Este método tendrá un FileStream que va a escribir a disco en una ruta concreta el stream de bytes recibido, o sea, el archivo. Y el método DescargarFichero hará todo lo contrario, recibirá una instancia de la clase PeticionDescarga con el nombre del archivo a obtener y va a crear una instancia de la clase Fichero,es decir, el nombre, su longitud y el contenido en un stream devolviendo esta instancia cómo resultado de la llamada al método.

Hasta aquí todo lo que se refiere al servicio, ya lo tenemos terminado y ahora nos toca hacer la parte de cliente. WCF siempre debe de tener almenos un EndPoint así que a esta aplicación cliente le añadiremos una referencia a un servicio, el que hemos hecho, del que deberemos obtener la URL para saber dónde está hospedado. La podremos ver en la barra de direcciones ejecutando el servicio independientemente. Aunque el servicio este configurado, el cliente tiene total libertad para decidir cómo actuar con el servicio cómo por ejemplo en nuestro caso le estamos indicando que únicamente (como servicio) podremos recibir 1Mb y mediante Streamed. El cliente debe responder con acuerdo a nuestras configuraciones pero por ejemplo puede hacer que el tamaño máximo que se envié al servicio sea de 0,5Mb.

Bien, los parametros a configurar del lado cliente van a ser el maxBufferSize, maxReceivedMessageSize y transferMode. Se debe indicar que el tamaño maximo sea igual o menor que el tamaño que permite el servico (>= 1mb), en caso contrario el servicio lanzará una excepción indicando que no soporta un tamaño superior. Con el último parámetro debemos tener cuidado, debe ser transferMode=StreamedResponse” porqué nuestro servicio espera recibir las cabeceras SOAP en un buffer y recibir la transmisión del mensaje al momento del envío. De esta forma evitamos tener un buffer con miles de bytes debido al gran peso del fichero.

Comentar que las carpetas de subida y descarga se crearan dentro de los proyectos correspondientes. La de subida dentro del App_Code del servicio y la de descarga dentro del bin del cliente.Para obtener el código, que esta adjunto, debéis abrir el archivo MTOM.zip y copiar cada una de las subcarpetas que contiene Projects y WebSites dentro de las carpetas de proyecto de Visual Studio 2008 y abrir la solución Ficheros.sln. Está hecho con el Framework 3.5 y c#, como no :)

Espero que no dudéis en preguntar y que os guste el post! Happy code!

MTOM