Syndication con WCF, sobrecarga de métodos de paso.
Otra de las funcionalidades que se agregaron con el assemblie System.ServiceModel.Web es la posibilidad de sindicar contenido al estilo .net o sea, fácil y tipificado de verdad, es tán fácil que vamos a ir diréctamente al ejemplo de código. Vamos a ver que en este caso definí una interface del servicio con los atributos, esto es lo más natural y nos da un extra de flexiblidad y un poco más de limpieza en el código que hacerlo como en los ejemplos anteriores:
[ServiceContract] [ServiceKnownType(typeof(Atom10FeedFormatter))] //indica la forma de serializar los datos Atom [ServiceKnownType(typeof(Rss20FeedFormatter))] public interface IServicioPrueba { [OperationContract] [WebGet] //se usa webget porque tanto RSS con Atom corren sobre HTTP y se invocan con verbos HTTP SyndicationFeedFormatter Feed(string tipo); [OperationContract] [WebGet] SyndicationFeedFormatter Feed(); }
public class ServicioPrueba : IServicioPrueba { /// <summary> /// Este método nos permite elegir si queremos Atom, sino RSS /// </summary> public SyndicationFeedFormatter Feed(string tipo) { SyndicationFeed feed = GenerarFeed(); if (tipo.Equals("atom")) { return new Atom10FeedFormatter(feed); } else { return new Rss20FeedFormatter(feed); } } /// <summary> /// Genera un feed con dos entradas /// </summary> private static SyndicationFeed GenerarFeed() { IList<SyndicationItem> items = new List<SyndicationItem>(); items.Add(new SyndicationItem("primer noticia", "extra extra cupido apesta!", null)); items.Add(new SyndicationItem("segunda noticia", "Meteoro no seas tonto el enmascarado es tu hermano", null)); SyndicationFeed feed = new SyndicationFeed(items); return feed; } /// <summary> /// En este método lo publicamos como Rss las clases Rss20FeedFormatter y Atom10FeedFormatter heredas /// de SyndicationFeedFormatter por lo tanto se pueden intercambiar casi indistintamente (tiene su especialización) /// </summary> /// <returns></returns> public SyndicationFeedFormatter Feed() { return new Rss20FeedFormatter(GenerarFeed()); } }
Por último el host
static void Main(string[] args) { string address = "http://localhost:8080/servicio/"; using(ServiceHost host = new ServiceHost(typeof(ServicioPrueba))) { //Creamos un endpoint igual al de REST ServiceEndpoint endPoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IServicioPrueba)), new WebHttpBinding(), new EndpointAddress(address)); endPoint.Behaviors.Add(new WebHttpBehavior()); host.Description.Endpoints.Add(endPoint); Console.WriteLine("inicializando.."); host.Open(); Console.WriteLine("iniciado.."); Console.ReadLine(); Console.WriteLine("cerrando..."); host.Close(); Console.WriteLine("listo"); } }
Lo que hice fue crear dos métodos, uno (Feed) que devuelve siempre el canal en Rss y otro con un parámetro que nos permite elegir el formato. Si intentamos correr esto vamos a recibir un error muy feo del tipo InvalidOperationException y es porque estamos intentando sobrecargar métodos en WCF.
Sobrecarga de método en WCF
Sí, se pueden sobrecargar métodos en WCF, de echo lo hacemos y al compilar todo va bien, hasta que hacemos Open() del host y todo explota. ¿Qué ocurre? simple, al hacer Open() WCF comieza a generar la metadata para el servicio, como siempre utilizar WSDL y WSDL es SOAP (o naturalmente lo es), y a pesar que SOAP es "Simple Object Access Protocol" (ya sé que desde la versión 1.2 no lo es), no soporta conceptos de orientación a objetos como la sobrecarga por lo tanto BOOM, no funciona.
La metadata generada para el servicio a pesar que el WebHttpBinding es WSDL
<?xml version="1.0" encoding="utf8" ?> <wsdl:definitions name="ServicioPrueba" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasisopen.org/wss/2004/01/oasis200401wsswssecurityutility1.0.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:tns="http://tempuri.org/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"> <wsdl:types> <xsd:schema targetNamespace="http://tempuri.org/Imports"> <xsd:import schemaLocation="http://localhost:8080/servicio/?xsd=xsd3" namespace="http://tempuri.org/" /> <xsd:import schemaLocation="http://localhost:8080/servicio/?xsd=xsd0" namespace="http://schemas.microsoft.com/2003/10/Serialization/" /> <xsd:import schemaLocation="http://localhost:8080/servicio/?xsd=xsd1" namespace="http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication" /> <xsd:import schemaLocation="http://localhost:8080/servicio/?xsd=xsd2" namespace="http://www.w3.org/2005/Atom" /> </xsd:schema> </wsdl:types> <wsdl:message name="IServicioPrueba_Feed_InputMessage"> <wsdl:part name="parameters" element="tns:Feed" /> </wsdl:message> <wsdl:message name="IServicioPrueba_Feed_OutputMessage"> <wsdl:part name="parameters" element="tns:FeedResponse" /> </wsdl:message> <wsdl:portType name="IServicioPrueba"> <wsdl:operation name="Feed"> <wsdl:input wsaw:Action="http://tempuri.org/IServicioPrueba/Feed" message="tns:IServicioPrueba_Feed_InputMessage" /> <wsdl:output wsaw:Action="http://tempuri.org/IServicioPrueba/FeedResponse" message="tns:IServicioPrueba_Feed_OutputMessage" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="WebHttpBinding_IServicioPrueba" type="tns:IServicioPrueba"> <wsdl:operation name="Feed"> <wsdl:input /> <wsdl:output /> </wsdl:operation> </wsdl:binding> <wsdl:service name="ServicioPrueba"> <wsdl:port name="WebHttpBinding_IServicioPrueba" binding="tns:WebHttpBinding_IServicioPrueba" /> </wsdl:service> </wsdl:definitions>
¿Entonces cuál es la solución?
La solución es utilizar un alias para los métodos, ya sé que van a decir que entonces es como si tuvieran otro nombre, y tienen razón, pero parcialmente, si consumimos un servicio WCF desde WCF podemos llamar al mismo método en una u otra sobrecarga (utilizando la interface cliente no el proxy svcutility.exe en caso de usar el proxy se puede editar a mano)
Entonces el código de la interface queda así:
[ServiceContract] [ServiceKnownType(typeof(Atom10FeedFormatter))] //indica la forma de serializar los datos Atom [ServiceKnownType(typeof(Rss20FeedFormatter))] public interface IServicioPrueba { [OperationContract(Name = "default")]//alias para esta operación [WebGet] //se usa webget porque tanto RSS con Atom corren sobre HTTP y se invocan con verbos HTTP SyndicationFeedFormatter Feed(string tipo); [OperationContract] [WebGet] SyndicationFeedFormatter Feed(); }
Y listo, ahora funciona
Mágico una vez más, hasta la próxima.
Referencias
No hay comentarios:
Publicar un comentario