WCF bajo Contrato


Una práctica introducción a los contratos de WCF

Compartir

Willy Mejía
Julio 2008
Actualizado en Agosto 2008

Contenido:

  1. Contrato de Servicio
  2. Contrato de Datos
  3. Contrato de Mensajes
  4. Contrato de Fallo

Como continuación al artículo Servicios Web con WCF: Una sencilla introducción a Servicios Web con WCF, el presente artículo abordará de manera práctica los principales tipos de contratos que se emplean para definir los Servicios WCF. La finalidad es dar una introducción a los mismos, sin pretender ser exhaustivo. Así que partiendo del servicio Hello del artículo anterior, comencemos por el Contrato de Servicio.

Contrato de Servicio

Un contrato de servicio permite la comunicación con el mismo, al proporcionar la siguiente información:

  • Listado de las operaciones que ofrece y la ubicación de las mismas
  • Firma de las operaciones, en términos de intercambio de mensajes
  • Tipos de datos empleados en los mensajes.
  • Protocolos concretos y formatos de serialización.

Para esto se utilizan los documentos WSDL y XSD, que son lenguajes estándar para describir servicios web. No obstante WSDL y XSD son difíciles de utilizar, por ello las aplicaciones WCF utilizan atributos, interfaces y clases para definir la estructura de un servicio e implementar un servicio.

En el artículo Servicios Web con WCF se definió el contrato del servicio utilizando la clase de implementación del mismo. Sin embargo es recomendable utilizar una interfaz para ello, ya que, al igual que un contrato de servicio, las interfaces solo definen la agrupación de métodos (operaciones) con ciertas firmas sin especificar ningún detalle sobre su implementación. Además, todas las ventajas de las interfaces administradas se aplican a las interfaces de contrato de servicio:

  • Las interfaces pueden extender cualquier número de interfaces.
  • Una única clase puede implementar cualquier número de interfaces.
  • Puede modificar la implementación de una interfaz, mientras la interfaz siga siendo la misma.

Por lo que adicionalmente se puede controlar la versión del servicio implementando la interfaz antigua y la nueva. Los clientes antiguos se conectan a la versión original, mientras los clientes más nuevos pueden conectarse a la versión más nueva.

Así que, tras realizar refactoring al Servicio WCF Hello, el contrato y su implementación ahora quedan de la siguiente forma:

Listado de Hello.cs

using System;
using System.Text;
using System.ServiceModel;

namespace hello
{  

  [ServiceContract(Namespace="http://hello/, Name="Hello")]
  interface IHello
  {
    [OperationContract()]
    string SayHello(string name);
  }
    
  [ServiceBehavior(Namespace="http://hello/", Name="HelloService")]    
  public class Hello : IHello
  {
    private StringBuilder message = new StringBuilder("!Hola ");

    public string SayHello(string name)
    {
      if (name == null || name.Length == 0)
        throw new FaultException("Parámetro 'name' nulo o vacío.");
      string msg = message.Append(name + "!").ToString();
      System.Console.WriteLine(msg);
      return msg;
    }
  }
}

Observe que ahora los atributos ServiceContract y OperationContract se aplican a la interfaz, y que a la clase de implementación sólo se la aplica el atributo ServiceBehavior. Recuerde que los dos primeros atributos definen el contrato del servicio y sus operaciones, mientras que el tercero define comportamientos concretos de la implementación. En el ejemplo los utilizamos para marcar las responsabilidades de cada una, así como el namespace a utilizar en la exportación hacia el WSDL.

Las operaciones de un servicio están soportadas por un intercambio de mensajes SOAP. La firma de una operación de servicio dicta un cierto patrón de intercambio de mensajes (MEP por sus siglas en inglés) subyacente que puede admitir la transferencia de datos y las características que una operación requiere. Puede especificar tres patrones en el modelo de programación de WCF: patrón de mensajes de solicitud/respuesta, unidireccionales (one-way) y dúplex.

Un patrón de solicitud/respuesta es uno en el que un cliente invoca una operación y recibe una respuesta con la que está relacionada la solicitud. Éste es el MEP predeterminado porque soporta tanto una operación en la que uno o más parámetros se pasan a la operación como una devolución y uno o más valores de salida que la operación devuelve al llamador. Mas información en: Servicios de solicitud/respuesta.

Nota: Incluso una operación con tipo de retorno void por omisión será del tipo solicitud/respuesta, a menos que en el contrato de la operación se especifique que es unidireccional.

Un patrón unidireccional (one-way) es uno en la que un cliente invoca una operación y no espera a que finalice la operación del servicio, ni procesa errores SOAP. Este tipo de modelo de intercambio de mensajes soporta el comportamiento como evento de un cliente a una aplicación de servicio. Mas información en: Servicios unidireccionales.

Un patrón dúplex se caracteriza porque tanto el servicio como el cliente tienen la capacidad de enviar mensajes entre sí independientemente de si se está utilizando una mensajería unidireccional o de solicitud/respuesta. En modelo dúplex es útil para los servicios que deben comunicarse directamente con el cliente, o para proporcionar una experiencia asincrónica a cada lado de un intercambio de mensajes, incluido el comportamiento similar a un evento. Mas información en: Servicios dúplex.

Tras compilar el servicio:

>csc /t:library Hello.cs /r:System.ServiceModel.dll

Continuamos con el host. El código de HelloHost.cs permanece sin cambios, pero tenemos que hacer un ligero cambio al archivo de configuración HelloHost.exe.config, de modo ahora el atributo contract del endpoint señale a la interfaz:

Listado de HelloHost.exe.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="hello.Hello" behaviorConfiguration="enableMetadata">
        <endpoint contract="hello.IHello" binding="basicHttpBinding" address="" 
            bindingNamespace="http://hello/" name="HelloPort" />
        <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="enableMetadata" >
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Nota: En lo sucesivo el host y su archivo de configuración no serán alterados.

Del lado servidor solo resta ejecutar el host:

>HelloHost.exe

El Servicio Web Hello se esta ejecutando...
WSDL: http://localhost:8080/hello?wsdl
Presione una tecla para cerrarlo.

Ahora pasamos al cliente. Primero debemos regenerar los artefactos cliente con svcutil.exe:

>svcutil http://localhost:8080/hello?wsdl /out:Hello.cs /config:Client.exe.config /n:http://hello/,hello

Después modificamos una línea del código de la aplicación cliente:

Listado de Client.cs

using System;

public class Client
{
  static void Main(string[] args)
  {
    String arg = null;
    String result = null;
    if (args.Length > 0) {
      arg = args[0];
    }
    else {
      arg = "Anónimo";
    }
    try {      
      hello.Hello service = new hello.HelloClient();
      result = service.SayHello(arg);
    }
    catch (System.ServiceModel.FaultException fault) 
    { 
      result = fault.Message; 
    }
    catch (Exception ex) {
      result = ex.ToString();
    }
    finally{
      System.Console.WriteLine(result);
    }
  }
}

Observe que la interfaz que define el contrato del servicio en el cliente se llama Hello en lugar de IHello, eso se debe a que así se especificó en el parámetro por nombre «Name» del atributo ServiceContract de la interfaz IHello en el servicio.

Compilamos tanto el proxy como la aplicación:

>csc /t:library Hello.cs /r:System.ServiceModel.dll

>csc /t:exe Client.cs /r:System.ServiceModel.dll,Hello.dll

…y la ejecución será la misma que la última vez:

>Client Willy

Hola Willy.

Contrato de Datos

WCF emplea, de forma predeterminada, a la clase DataContractSerializer como mecanismo de serialización de datos .NET desde y hacia XML, el cual requiere que los tipos tengan un contrato de datos definido.

Hasta ahora el servicio utiliza tipos simples (string) para el intercambio de datos (en el parámetro de entrada y el valor devuelto) sin ninguna preparación. Esto se debe a que ciertos tipos del .NET Framework, entre ellos los «primitivos», tienen contratos de datos predeterminados. Si lo desea puede consultar la lista de los tipos admitidos por el DataContractSerializer.

En caso que deseáramos utilizar nuestros propios tipos de datos, necesitamos definir tipos complejos. Para ello debemos crear tipos que tengan un contrato de datos definido, lo cual conseguimos aplicando el atributo DataContract a las clases, estructuras y/o enumeraciones a ser serializables por el DataContractSerializer. Además de lo anterior debemos aplicar el atributo DataMember a los miembros que deseemos sean serializables, sin que importe su modificador de acceso.

Continuando con nuestro ejemplo, supongamos que ahora deseamos utilizar los siguientes tipos para el intercambio de datos:

Listado de HelloData.cs

using System;
using System.Runtime.Serialization;

namespace hello.data
{
    [DataContract(Namespace="http://hello/data/")]
    public class Person
    {
        private string firstName;
        private string lastName;

        [DataMember()]
        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        [DataMember()]
        public string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }
    }

    [DataContract(Namespace="http://hello/data/")]
    public class HelloCard
    {
        private string greeting;
        private int serial;

        [DataMember()]
        public string Greeting
        {
            get { return greeting; }
            set { greeting = value; }
        }

        public int Serial
        {
            get { return serial; }
            set { serial = value; }
        }
    }
}

Nótese que son clases «planas» con los atributos que definen el contrato de datos. Observe también la declaración del espacio de nombre del .NET Framework: System.Runtime.Serialization, a cuyo ensamblado debemos referenciar para compilar HelloData.cs:

>csc /t:library HelloData.cs /r:System.Runtime.Serialization.dll

Una vez definidos los tipos personalizados, ahora definimos el nuevo contrato de servicio, y reescribimos su implementación, como se lista a continuación:

Listado de Hello.cs

using System;
using System.Text;
using System.ServiceModel;
using hello.data;

namespace hello
{
  [ServiceContract(Namespace="http://hello/" Name="Hello")]
  interface IHello
  {
    [OperationContract()]
    public HelloCard SayHello(Person person);
  }

  [ServiceBehavior(Namespace="http://hello/", Name="HelloService")] 
  public class Hello : IHello
  {
    private StringBuilder message = new StringBuilder("¡Hola ");
  
    public HelloCard SayHello(Person person)
    {      
      if (person == null ||
          person.FirstName == null ||
          person.FirstName.Length == 0)
        throw new FaultException("Objeto Person inválido.");

      message.Append(person.FirstName + "!");
      string msg = message.ToString();
      HelloCard card = new HelloCard();
      card.Greeting = msg;
      System.Console.WriteLine(msg);
      return card;
    }
  }
}

Nota: En el código se altera la interfaz del contrato del servicio, cuando en la práctica se debe definir una nueva interfaz e implementarla. Se hizo así para simplicidad del ejemplo.

Ahora compilamos el servicio agregando la referencia al ensamblado generado previamente:

>csc /t:library Hello.cs /r:System.ServiceModel.dll,HelloData.dll

El host y su archivo de configuración permanecen sin cambios, por lo que solo resta ejecutar HelloHost.exe.

Ahora pasamos al cliente. Primero regeneramos los artefactos cliente con svcutil.exe como hasta ahora, y después modificamos el código de la aplicación cliente, tal como se aprecia a continuación:

Listado de Client.cs

using System;

public class Client
{
  static void Main(string[] args)
  {
    String arg = null;
    String result = null;
    if (args.Length > 0) {
      arg = args[0];
    }
    else {
      arg = "Anónimo";
    }
    try {
      hello.Hello service = new hello.HelloClient();
      hello.data.Person person = new hello.data.Person();
      person.FirstName = arg;
      hello.data.HelloCard card = service.SayHello(person);
      result = card.Greeting;
    }
    catch (System.ServiceModel.FaultException fault) 
    { 
      result = fault.Message; 
    }
    catch (Exception ex) {
      result = ex.ToString();
    }
    finally{
      System.Console.WriteLine(result);
    }
  }
}

Compilamos los archivos del proxy y el cliente, y ejecutamos Client.exe como hasta ahora.

Nota: Además de DataContractSerializer, WCF también admite (de forma excluyente) la clase XmlSerializer, que es el mismo mecanismo de serialización utilizado en Servicios Web ASP.NET (aka ASMX). Aunque XmlSerializer solo admite un conjunto reducido de tipos, permite un mayor control sobre el XML resultante y admite mejor la norma de XSD. Para más información consulte Utilización de XMLSerializer.

Contrato de Mensajes

Hasta ahora se ha redefinido el contrato de servicio utilizando una interfaz, así como empleado tipos personalizados con contrato de datos definidos, lo cual es suficiente en la mayoría de los casos.

Pero cuando se desea tener mayor control sobre la estructura de los mensajes SOAP debemos crear un contrato de mensajes el cual permite utilizar un tipo para el parámetro y el valor devuelto que se serialicen en los mensajes SOAP de solicitud y respuesta que se necesiten.

A éste estilo de modelado de las operaciones se le conoce como estilo de mensajería, en contraposición al estilo de llamada de procedimiento remoto (RPC). Las firmas de las operaciones de estilo de mensajería tienen hasta un parámetro y un valor de retorno y ambos deben ser tipos de mensaje que se serializan directamente en una estructura del mensaje SOAP especificado.

Para definir el contrato de mensajes debemos aplicar el atributo MessageContract a las clases que vayan ser los tipos de mensaje. Así también debemos aplicar el atributo MessageBodyMember a sus miembros (que pueden ser campos, propiedades o eventos) que serán los elementos del cuerpo del mensaje SOAP, o bien el atributo MessageHeader para especificar el encabezado del mensaje.

Podriamos haber empleado el contrato de mensajes desde la versión del servicio que empleaba tipos simples, en cuyo caso podría ser algo como:

[MessageContract()]
public class SayHelloRequest
{
  [MessageBodyMember()]
  public string name
}

[MessageContract()]
public class SayHelloResponse
{
  [MessageBodyMember()]
  public string greeting
}

Pero continuando con nuestro ejemplo, los contratos de mensaje para la operación SayHello quedan de la siguiente forma:

Listado de Hello.cs (fragmento)

using hello.data;

  [MessageContract()]
  public class SayHelloRequest
  {
    private Person person;

    [MessageBodyMember(Name="Person")]
    public Person PersonParameter
    {
      get { return person; }
      set { person = value; }
    }
  }

  [MessageContract()]
  public class SayHelloResponse
  {
    private HelloCard helloCard;

    [MessageBodyMember(Name="HelloCard")]
    public HelloCard HelloCardResult
    {
      get { return helloCard; }
      set { helloCard = value; }
    }
  }

Nótese que se trata de clases «planas» con atributos, y que los elementos del cuerpo de los mensajes son los tipos personalizados con contratos de datos definidos anteriormente en HelloData.cs.

Para utilizar los tipos de mensaje ahora la firma del contrato de servicio debe redefinirse como sigue:

Listado de Hello.cs (fragmento)

[ServiceContract(Namespace="http://hello/", Name="Hello")]
interface IHello
{
  [OperationContract()]
  SayHelloResponse SayHello(SayHelloRequest request);
}

Por lo que también debemos reescribir su implementación. El listado completo de Hello.cs queda entonces de la siguiente forma:

Listado de Hello.cs

using System;
using System.Text;
using System.ServiceModel;
using hello.data;

namespace hello
{

  [MessageContract()]
  public class SayHelloRequest
  {
    private Person person;

    [MessageBodyMember(Name="Person")]
    public Person PersonParameter
    {
      get { return person; }
      set { person = value; }
    }
  }

  [MessageContract()]
  public class SayHelloResponse
  {
    private HelloCard helloCard;

    [MessageBodyMember(Name="HelloCard")]
    public HelloCard HelloCardResult
    {
      get { return helloCard; }
      set { helloCard = value; }
    }
  }
  
  [ServiceContract(Namespace="http://hello/", Name="Hello")]
  interface IHello
  {
    [OperationContract()]
    SayHelloResponse SayHello(SayHelloRequest request);
  }

  [ServiceBehavior(Namespace="http://hello/", Name="HelloService")]
  public class Hello : IHello
  {
    private StringBuilder message = new StringBuilder("¡Hola ");

    public SayHelloResponse SayHello(SayHelloRequest request)
    {
      if (request.PersonParameter == null ||
          request.PersonParameter.FirstName == null ||
          request.PersonParameter.FirstName.Length == 0)
        throw new FaultException(
            "Parámetro Person inválido.");

      message.Append(request.PersonParameter.FirstName + "!");
      string msg = message.ToString();
      SayHelloResponse response = new SayHelloResponse();
      HelloCard card = new HelloCard();
      card.Greeting = msg;
      response.HelloCardResult = card;
      System.Console.WriteLine(msg);      
      return response;
    }
  }
}

Las clases de los archivos HelloData.cs y HelloHost.cs permanecen sin cambios. Por lo que del lado del servicio únicamente compilamos el archivo Hello.cs:

>csc /t:library Hello.cs /r:System.ServiceModel.dll,HelloData.dll

…y ejecutamos la aplicación host.

Continuemos con el cliente. Primero debemos (una vez más) regenerar los artefactos cliente con svcutil.exe, y después modificar el código de Client.cs como se muestra a continuación:

Listado de Client.cs

using System;

public class Client
{
  static void Main(string[] args)
  {
    String arg = null;
    String result = null;
    if (args.Length > 0) {
      arg = args[0];
    }
    else {
      arg = "Anónimo";
    }
    try {
      hello.Hello service = new hello.HelloClient();         
      hello.data.Person person = new hello.data.Person();
      person.FirstName = arg;
      hello.SayHelloRequest request = new hello.SayHelloRequest();
      request.Person = person;
      hello.SayHelloResponse response = service.SayHello(request);
      result = response.HelloCard.Greeting;
    }
    catch (System.ServiceModel.FaultException fault)
    {
      result = "Fault: " + fault.Message;
    }
    catch (Exception ex) {
      result = ex.ToString();
    }
    finally{
      System.Console.WriteLine(result);
    }
  }
}

Nótese que antes del llamado al método SayHello se deben crear una instancia del tipo de mensaje SayHelloRequest, así como el tipo Person para su miembro correspondiente. La invocación retorna una instancia del tipo de mensaje SayHelloResponse mediante la cual se recupera el mensaje a desplegar. Note también que los elementos de los mensajes tienen los nombres especificados en el argumento por nombre «Name» del atributo MessageBodyMember correspondiente.

Para finalizar compilamos el proxy y la aplicación cliente, y ejecutamos Client.exe como hasta ahora.

Dado que en el presente ejemplo no hemos empleado el contrato de mensajes para nada extraordinario, el uso del mismo pareciera una sobrecarga innecesaria. Pero tenga presente que es útil para aquellos casos en los cuales se requiere tener control sobre los elementos o cabecera del mensaje, tales como la resolución de problemas de seguridad del mensaje o problemas de interoperabilidad.

Contrato de Fallo

Cualquier operación que no sea «One-Way» devuelve dos posibles mensajes: su mensaje de respuesta normal, o bien un mensaje de error. Para enviar un mensaje de error (un «SOAP Fault» en términos de servicios web) se lanza una FaultException en la implementación del servicio, tal y como se ha ejemplificado hasta ahora.

Pero además se pueden especificar formalmente los mensajes de error utilizando tipos personalizados con un contrato de datos adecuado, dado que se serializarán mediante el DataContractSerializer. El tipo a utilizar en el mensaje de error se especifica contractualmente en la firma de la operación utilizando el atributo FaultContract, mientras que en la implementación de la operación se genera mediante una FaultException del tipo especificado en el FaultContract.

Como ejemplo suponga que deseamos especificar un mensaje de error, que nos informe sobre un error en el parámetro de la petición, utilizando el siguiente tipo con su contrato de datos:

Listado de HelloData.cs (fragmento)

[DataContract(Namespace="http://hello/data/")]
public class InvalidRequest
{
  private int id;
  private string message;
  private string notes;

  [DataMember()]
  public int ID
  {
    get { return id; }
    set { id = value; }
  }

  [DataMember()]
  public string Message
  {
    get { return message; }
    set { message = value; }
  }				

  [DataMember()]
  public string Notes
  {
    get { return notes; }
    set { notes = value; }
  }				
}

El fragmento de código se agrega al archivo HelloData.cs, tras lo cual lo compilamos.

Para especificar el uso del tipo InvalidRequest como tipo de fallo sobre la operación, aplicamos el atributo FaultContract a la firma de la operación:

Listado de Hello.cs (fragmento)

[ServiceContract(Namespace="http://hello/", Name="Hello")]
interface IHello
{
  [OperationContract()]
  [FaultContract(typeof(InvalidRequest))]
  SayHelloResponse SayHello(SayHelloRequest request);
}

Después en la clase de implementación del servicio se lanzan las excepciones correspondientes:

Listado de Hello.cs (fragmento)

  [ServiceBehavior(Namespace="http://hello/", Name="HelloService")]
  public class Hello : IHello
  {
    private StringBuilder message = new StringBuilder("¡Hola ");

    private void ValidateRequest(SayHelloRequest request)
    {
      if (request.PersonParameter == null)
      {
        InvalidRequest fault = new InvalidRequest();
        fault.ID = 1;
        fault.Message = "Parámetro Person inválido";
        fault.Notes = "El objeto Person no puede ser nulo...";
        throw new FaultException<InvalidRequest>(fault, "Parámetro inválido.");
      }
      else if (request.PersonParameter.FirstName == null ||
        request.PersonParameter.FirstName.Length == 0)
      {
        InvalidRequest fault = new InvalidRequest();
        fault.ID = 2;
        fault.Message = "Miembro FirstName inválido";
        fault.Notes = "El miembro FirstName del objeto Person no puede ser nulo o vacío...";
        throw new FaultException<InvalidRequest>(fault, "Parámetro inválido.");
      }
    }

    public SayHelloResponse SayHello(SayHelloRequest request)
    {
      ValidateRequest(request);

      message.Append(request.PersonParameter.FirstName + "!");
      string msg = message.ToString();
      SayHelloResponse response = new SayHelloResponse();
      HelloCard card = new HelloCard();
      card.Greeting = msg;
      response.HelloCardResult = card;
      System.Console.WriteLine(msg);
      return response;
    }
  }

Nótese que hemos extraído el código de validación del request en un método privado, en el cual se crea una instancia de InvalidRequest, se establece la información pertinente, y se lanza un FaultException con dicha instancia. Note además que se hace el uso de la versión «genérica» de FaultException para ello.

Compilamos Hello.cs, y para terminar del lado del servicio sólo ejecutamos la aplicación host.

Para el cliente una vez más se regeneran los artefactos cliente con svcutil.exe.

Después, aunque el código de la aplicación cliente podría funcionar sin cambios, para poder consumir los mensajes adicionales de error (de tipo InvalidRequest) ahora agregaremos un catch para «atrapar» el FaultException de tipo InvalidRequest. El listado de Client.cs queda de la siguiente manera:

Listado de Client.cs

using System;
using System.ServiceModel;

public class Client
{
  static void Main(string[] args)
  {
    String arg = null;
    String result = null;
    if (args.Length > 0) {
      arg = args[0];
    }
    else {
      arg = "Anónimo";
    }
    try {
      hello.Hello service = new hello.HelloClient();
      hello.data.Person person = new hello.data.Person();
      person.FirstName = arg;
      hello.SayHelloRequest request = new hello.SayHelloRequest();
      request.Person = person;
      hello.SayHelloResponse response = service.SayHello(request);
      result = response.HelloCard.Greeting;
    }
    catch (FaultException<hello.data.InvalidRequest> helloFault)
    {
      result = String.Format("{0}\nID: {1}\nMensaje: {2}\nNotas: {3}",
                helloFault.Message,
                helloFault.Detail.ID,
                helloFault.Detail.Message,
                helloFault.Detail.Notes);
    }
    catch (FaultException fault) {
      result = "Fault: " + fault.Message;
    }
    catch (Exception ex) {
      result = "Error: " + ex.ToString();
    }
    finally{
      System.Console.WriteLine(result);
    }
  }
}

Observe que para acceder a los miembros de la instancia de InvalidRequest enviada utilizamos la propiedad Detail de la instancia de FaultException helloFault.

Compilamos el proxy y el cliente, y ejecutamos Client.exe. Pruebe las excepciones modificando el código de la aplicación cliente, ya sea estableciendo a nulo el valor del miembro FirstName o el parámetro Person, y volviendo a compilar y ejecutar…

Epílogo

Con los ejemplos vistos en el presente artículo se han explorado brevemente los diferentes tipos de contratos de WCF: contrato de servicio, contrato de datos, contrato de mensajes y contrato de fallo. Los mismos se definen aplicando ciertos atributos a clases y sus miembros. Los atributos permiten parámetros por nombre, tales como el Namespace y Name,  para una mejor definición del elemento al cual se aplica. Queda como tarea para el lector que indague sobre dichos parámetros, ya que existen muchos otros en cada uno de los distintos atributos.

Quedan pendientes entonces varias cosas, además del nuevo cliente Java, mismas que espero abordar en futuras ocasiones. ¡Hasta entonces!

4 respuestas a WCF bajo Contrato

  1. Darjo dijo:

    Excelente amigo. De los mejores articulos que he visto para lograr entender y dar el primer paso en WCF. Muchas gracias.

  2. Juan dijo:

    Gracias por el artículo, es muy bueno. ¿Podrías poner el código para descargarlo?. Saludos.

Deja un comentario