jueves, 28 de abril de 2011

WCF RIA Services como OData, SOAP o JSON

Casi a punto ya de agotar las vacaciones de Semana Santa, he querido escribir este post que ya llevaba tiempo como Draft, espero que sea de utilidad.

  1. ¿Estas intentando trabajar con RIA Services? 
  2. ¿Quieres exponer tu servicio RIA a través de un servico web?
  3. ¿SOAP o REST?
  4. ¿Quieres exponer tus datos SQL con EntityFramework  a través de un Servicio Web en cuestion de unos cuantos clicks?

Si estas interesado en conocer las respuestas a estas preguntas este es tu post:

Antes de comenzar asegura que las DLLs instaladas son las adecuadas. Para ello revisa que en la ruta “C:\Program Files (x86)\Microsoft SDKs\RIA Services\v1.0\Libraries\Server” se encuentra el siguiente conjunto de Dlls:

image

De no ser así, sigue lo siguientes pasos hasta conseguirlo. Esto es debido a que los distintos cambios de versiones instaladas sobreescriben a las anteriores y en la mayoria de los casos, verás que sólo existen la dlls “Microsoft.xxx” o las “System.xxx”. ¡Vaya complicación!, de no ser por mi compañero y amigo “David Gonzalez” todavía estaría instalando, ¡Muchas gracias!.

Para evitar todo esto:

Desinstala:

  1. Microsoft Silverlight
  2. Microsoft Silverlight Tools for Visual Studio 2010
  3. Microsoft Silverlight 4 SDK
  4. Microsoft F# Runtime for Silverlight 4
  5. WCF RIA Services v1.0 for Visual Studio 2010
  6. WCF RIA Services Toolkit

Instala en el siguiente orden. Importante tener muy en cuenta las versiones:

  1. Silverlight4_Tools.exe (en Ingles).
  2. RiaServices.msi (RIA Services v1.0 for Visual Studio 2010 – Mayo 2010).
  3. RiaServicesToolkit.msi (Aunque esta disponible la versión de Diciembre, instala la versión de Mayo 2010 con objeto de que sea compatible con RiaServices puesto que de este aun no está disponible una versión más actualizada.

Ahora el Visual Studio está listo para comenzar.

Importante: Desde Abril de 2011 ya está disponible la nueva versión de “WCF RIA Services ToolKit” que es compatible con la versión de Mayo de 2010 y por tanto podemos evitar la ardua tarea anterior, adicionalmente, tambien está disponible WCF RIA Services v1.0 SP1, con lo que tenemos todo al completo.

Ahora ya podemos comenzar a crear nuestro servico RIA:

Para exponer un servicio como RIA, añadiremos un nuevo item de tipo “Domain Service Class” a nuestro proyecto Web.

NOTA: No neceariamente tiene que ser un proyecto web, pero si pretendes sacarle partico al servicio y no solo para el acceso desde SilverLigth o LigthSwitch, entonces utiliza un servicio web como “Best Practice”:

image

Una vez incluido este nuevo item, veremos que en el “web.config” se ha añadido lo siguiente:

<domainServices>



  <endpoints>



    <add name="OData" type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />



  </endpoints>



</domainServices>




Ahora nuestro servicio (REST) ya está listo para ser consultado como si de un Atom, RSS, etc se tratara, eso sí, no existe ningún servicio "*.svc" físicamente, sin embargo se autogenera un “.svc” de la siguente manera:




Formato de la url dinámica: -.svc/OData/">http://localhost:[Puerto]/<namespace>-<DomainServiceName>.svc/OData/



Ejemplo/WcfService1-DomainService1.svc/ODATA/">http://localhost:<Puerto>/WcfService1-DomainService1.svc/ODATA/




Una vez que ya tenemos este servicio, ¿Porqué no utilizarlo como SOAP o incluso como JSON?, para ello bastará con incluir las siguientes dos líneas/endpoints  en la sección <domainServices> <endpoints>.




<add name="soap" type="Microsoft.ServiceModel.DomainServices.Hosting.SoapXmlEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add name="json" type="Microsoft.ServiceModel.DomainServices.Hosting.JsonEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />





Ahora tendremos tres url distintas para el acceso a cada uno de estos distintos endpoints:




  1. ODATA: -.svc/OData/">http://localhost:[Puerto]/<namespace>-<DomainServiceName>.svc/OData/


  2. SOAP: -.svc">http://localhost:[Puerto]/<namespace>-<DomainServiceName>.svc


  3. JSON:-.svc/json/GetProducts/"> http://localhost:[Puerto]/<namespace>-<DomainServiceName>.svc/json/GetProducts/






image




Ahora sí, nuestros endpoints estarán listos para comenzar a ser usados.



Uso desde Ligthswitch.



Para que todos estos EndPoint funcionen correctamente es necesario que nuestra clase de Dominio contenga el atributo de clase “[EnableClientAccess()]”, sin embargo “VS Ligthswitch” recomienda el uso de este atributo y por tanto mostrará el siguiente mensaje: “This data source will be publicly accessible from outside your application.  It is recommended that EnableClientAccess is disabled on the WCF RIA Service.



image



En cualquier caso siempre podremos continuar.



Nada más por el momento, espero haber aclarado algunos temas.  Si quieres conocer más detalle y ver ejemplos sobre su consumo, echa un vistazo a este enlace: WCF RIA Services Part 10 - Exposing Domain Services To Other Clients.



Saludos @Higuera la Real

Juanlu

Etiquetas: , , ,


domingo, 7 de diciembre de 2008

WCF: Un behavior para personalizar cabeceras HTTP.

 

Hace ya unos días ante la necesidad de tener que trabajar con información adicional en las cabeceras ("Headers") HTTP de WCF me encontré con un lugar más donde aplicar un nuevo "Behabior".  Como siempre, el tener que crear uno nuevo implica a "vote pronto" un, ¡¡¡bufffff, una vez más a meterse en las entrañas de WCF....!!! si, esta sería quizás la reacción normal, sin embargo no fué así  la mia, jejejeje...

Comento a continuación como crear una nuevo Behavior para este caso y por supuesto con objeto de evitar esta constante idea sobre nuestras mentes informáticas bastante ocupadas, :-D.

  1. Crear un proyecto "WCF Service Application" y añadir el siguiente método a la clase "Service1" que se crea por defecto:
  2. public string GetHeader()
    {
    HttpApplication app
    = HttpContext.Current.ApplicationInstance;

    string header = app.Request.Headers["UserQuery"];

    if ((header == null) || (header.Length == 0))
    throw new Exception(String.Format("Access Denied. Header information not found. {0}",
    header
    ?? String.Empty));

    return header;
    }

  3. Crear un proyecto cliente de tipo consola y añadir la clausula using; "using ConsoleApplication1.localhost;" y el siguiente código al método "main":

  4. Service1Client client = new Service1Client();
    client.Endpoint.Behaviors.Add(
    new HttpHeaderEndPointBehavior("UsuarioCabecera1"));

    string header = client.GetHeader();

    client.Close();

  5. Para hostear el servicio en IIS añadir el attributo "[AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Allowed)]" a la clase "Service1", de esta manera, estará disponible el contexto HTTP ("HttpContext.Current") para su tratamiento y de donde obtendremos la información de cabecera enviada por el cliente. Como hostear WCF en IIS.

  6. Publicar el servico Web en IIS, ej.: http://loclahost:9001/PruebaWCFCustomHeader/Service1.svc

  7. Finalmente, añadir en el proyecto consola la referencia al servicio web con la url del punto anterior.

  8. Run (F5) y a depurar.

 


Para que todo esto funcione es necesario generar un Behavior específico y para ello  será necesario escribir un par de clases que implementen las interfaces IEndPointBehavior y IClientMessageInspector. Básicamente los métodos que nos interesan son; ApplyClientBeabior y  BeforeSendRequest respectivamente:


 


HttpHeaderEndPointBehavior:


using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using elGuerre.Pocs.HttpHeaderExtension;

namespace elGuerre.Pocs.HttpHeaderExtension
{
public class HttpHeaderEndPointBehavior : IEndpointBehavior
{
private string _UserName;

public HttpHeaderEndPointBehavior(string userAgent)
{
this._UserName = userAgent;
}

public void AddBindingParameters(ServiceEndpoint endpoint,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
// Nothing to do
}

public void ApplyClientBehavior(ServiceEndpoint endpoint,
System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
HttpHeaderMessageInspector inspector
= new HttpHeaderMessageInspector(this._UserName);
clientRuntime.MessageInspectors.Add(inspector);
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Nothing to do
}

public void Validate(ServiceEndpoint endpoint)
{
// Nothing to do
}
}
}

HttpHeaderMessageInspector:


using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;

namespace elGuerre.Pocs.HttpHeaderExtension
{
public class HttpHeaderMessageInspector : IClientMessageInspector
{
private const string USER_NAME_HTTP_HEADER = "UserQuery";
private string _UserName;
private string _UserEncodedName;

public HttpHeaderMessageInspector(string userName)
{
_UserName
= userName;
_UserEncodedName
= null;
}

private string UserEncodedName
{
get
{
if (_UserEncodedName == null)
{
_UserEncodedName
= GetBase64Encoded(_UserName);
}
return _UserEncodedName;
}
}

public void AfterReceiveReply(
ref System.ServiceModel.Channels.Message reply,
object correlationState)
{
// Nothing to do.
}

public object BeforeSendRequest(
ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel)
{
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;

if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
{
httpRequestMessage
= httpRequestMessageObject as HttpRequestMessageProperty;
if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_NAME_HTTP_HEADER]))
{
httpRequestMessage.Headers[USER_NAME_HTTP_HEADER]
= this.UserEncodedName;
}
}
else
{
httpRequestMessage
= new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(USER_NAME_HTTP_HEADER,
this.UserEncodedName);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}

private static string GetBase64Encoded(string userName)
{
byte[] inData;
char[] charArr;

charArr
= userName.ToCharArray();
inData
= new byte[charArr.Length];

for (int i = 0; i < charArr.Length; i++)
{
inData[i]
= (byte)charArr[i];
}

return Convert.ToBase64String(inData, 0, inData.Length);
}
}
}

Nota:  La información de los "bindings" es la generada por defecto, así que para probar este behavior no será necesario nada más, luego, más fácil aún, :-D.


KIS: "Nada es difícil hasta que se demuestra lo contrario"


Saludos
JuanLu, elGuerre

Etiquetas: , ,


martes, 24 de junio de 2008

WCF & MSMQ

Hace unas semanas hablaba con un compañero acerca de como implementar una aplicación con acceso  a MSMQ y tras una larga discusión, en un intento de conseguir pensar en el mejor camino de lograrlo, pensé en hacerlo con WCF, y,  ¿Que mejor forma de probarlo que haciendo un ejemplo o proyecto? , pues, e aquí el motivo de este post.

Os dejo el conjunto de pasos seguidos:

Pasos:

[ServiceContract]
public interface IService1
{
[OperationContract(IsOneWay
= true, Action = "*")]
void Send(MsmqMessage<Persona> msg);
}




public class Service1 : IService1
{
[OperationBehavior(TransactionScopeRequired
= true, TransactionAutoComplete = true)]
public void Send(MsmqMessage<Persona> message)
{
Persona msg
= (Persona)message.Body;
Console.WriteLine(
"Persona: [{0} {1}; ({2})] ", msg.Nombre, msg.Apellidos, msg.OtraCosaMariposa);
}
}




static void Main(string[] args)
{
string msmqPath = ConfigurationManager.AppSettings["baseAddress"];

if (!MessageQueue.Exists(msmqPath))
MessageQueue.Create(msmqPath);

Uri baseAddress
= new Uri(String.Format("msmq.formatname:DIRECT=OS:{0}", msmqPath));

using (ServiceHost serviceHost = new ServiceHost(typeof(Service1), baseAddress))
{
serviceHost.Authorization.PrincipalPermissionMode
= System.ServiceModel.Description.PrincipalPermissionMode.None;

serviceHost.Open();

Console.WriteLine(
"Servicio listo.");
Console.WriteLine(
"Pulsa <INTRO> para finalizar.");
Console.ReadLine();

serviceHost.Close();
}
}




<appSettings>
<add key="baseAddress" value=".\private$\MyTest33"/>
</appSettings>

<system.serviceModel>
<bindings>
<msmqIntegrationBinding>
<binding name="NewBinding0" exactlyOnce="false" useSourceJournal="false"
useMsmqTracing
="true">
<security mode="None">
<transport msmqAuthenticationMode="None" />
</security>
</binding>
</msmqIntegrationBinding>
</bindings>
<services>
<service name="WcfMsmqHost.Service1">
<endpoint address="" binding="msmqIntegrationBinding" bindingConfiguration="NewBinding0"
contract
="WcfMsmqIntegration.IService1" />
</service>
</services>
</system.serviceModel>



string msmqPath = ConfigurationManager.AppSettings["baseAddress"];

MsmqIntegrationBinding binding
= new MsmqIntegrationBinding();
EndpointAddress address
= new EndpointAddress(
String.Format(
@"msmq.formatname:DIRECT=OS:{0}", msmqPath));
ChannelFactory
<IService1> channelFactory = new ChannelFactory<IService1>(binding, address);

// None, porque MQM no esta en Active Directory
binding.Security.Mode = MsmqIntegrationSecurityMode.None;

IService1 channel
= channelFactory.CreateChannel();

Persona msg
= new Persona();
msg.Nombre
= "Juan Luis";
msg.Apellidos
= "Guerrero";
msg.OtraCosaMariposa
= "elGuerre";

MsmqMessage
<Persona> message = new MsmqMessage<Persona>(msg);
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
channel.Send(message);
scope.Complete();
}


<configuration>
<appSettings>
<add key="baseAddress" value=".\private$\MyTest33"/>
</appSettings>
</configuration>

No olvidar incluir los espacios de nombre:



using System.ServiceModel;
using System.Messaging;
using System.ServiceModel.MsmqIntegration;


Y ni que decir tiene que la clase Persona (Persona.cs) contiene  tres propiedades públicas sin más; Nombre, Apellidos y OtraCosaMariposa, incluso, no será necesario establecer el atributo DataContract.


 


Por otro lado y durante la configuración del ejemplo nos podemos encontrar con alguno errores y que detallo a continuación:


Caso 1:


serviceHost.Open: Binding validation failed because the binding's ExactlyOnce property is set to true while the destination queue is non-transactional. The service host cannot be opened. Resolve this conflict by setting the ExactlyOnce property to false or creating a transactional queue for this binding.


Este problema es debido a la propiedad "exactlyOnce" tiene el valor "true" y la cola no es transaccional para solucionarlo:



  1. Si la cola es transaccional entonces asignarle el valor "true" a esta propiedad.
  2. si la cola NO es transaccional asignarle el valor "false".

Caso 2:


Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain but MSMQ is installed with Active Directory integration disabled. The channel factory or service host cannot be opened.


En este caso la solución es evitar que el valor del modo de seguridad del binding sea "WindowsDomain", puesto que la integración de MSMQ con Active Directory no ha sido instalada. Bastará, y si estamos en un dominio, con instalar esta integración desde los componentes de windows. Luego para nuestro caso, tanto cliente como servidor/host deberán establecer algunos valores:


Cliente en modo programático:



binding.Security.Mode = MsmqIntegrationSecurityMode.None;


Servidor mediante fichero de configuración:


<endpoint address="" binding="msmqIntegrationBinding"
bindingConfiguration
="NewBinding0" contract="WcfMsmqIntegration.IService1" />

El inconveniente de utilizar transacciones, si es que es un inconveniente, es el MSTC (Microsoft Transsaction Coordinator), que nos asegura la transaccionalidad de la operación de forma automática, pero que sin embargo, y dependiendo del entorno, es decir de la distribución entre servidores, podría resultar muy pesado e incluso no deseable. En caso de no hacer uso de la transaccionalidad, bastará con invocar de nuevo a nuestro "Sender" (envío de mensajes a MSMQ con WCF) desde el propio servidor/host y establecer un tratamiento adecuado y con esto estaría solucionado.


Finalmente y por ahora, he decir, que la ventaja de utilizar WCF para el tratamiento de colas MSMQ radica principalmente en la posibilidad de cambiar los bindings sin problema alguno, la trazabilidad totalmente automática, la seguridad de que todo va a funcionar a la primera, :-D


"Lo malo" puede ser que nos encontremos con que ya tenemos un sistema de colas MSMQ en un entorno de producción y que no podamos crear ningún servicio WCF, en tal caso, siempre podremos optar por crear un cliente WCF (ClientBase<T>), incluso también optar por el caso contrario, un servidor WCF y un cliente no WCF (System.Messaging).


http://www.cogin.com/mq/download.php


Un nuevo camino para el tratamiento de colas MSMQ.


saludos
Juanlu

Etiquetas: , ,


jueves, 28 de febrero de 2008

WCF - Creación dinámica de una clase proxy a partir de un WSDL

 

No sé si a alguien le puede venir bien este ejemplo, pero seguro que sí, en cualquier caso, aquí está, :-P

Se trata de un proyecto C#  que permite crear "proxys" clientes a partir de un WSDL de forma dinámica.

Es un ejemplo de una calculadora muy sencillo pero que sin embargo y lo más importante es que puede modificarse y adaptase a nuestra necesidad con bastante facilidad.

 

He aquí la referencia para descargarlo y pegarse un poco más con el: http://wcf.netfx3.com/files/folders/development_tools/entry6148.aspx

 

Una vez Más espero haber ayudado un poquito.
Juanlu

Etiquetas: , ,


miércoles, 27 de febrero de 2008

Hosteando en IIS un WCF con Http Basic Authentication y Compatibilidad ASP.NET (HttpContext; Session, cookies, cache, etc)

Una vez más, sigo por aquí con VS2008 y con WCF, en esta ocasión trataré de comentar, como hostear en IIS un WCF basado en un binding "Http Basic Authentication" y conseguir hacer uso de nuestra conocida clase HttpContext. Son unos cuantos pasos muy fáciles y básicos, jejeje.. ¡claro, ahora que los conozco!

Los pasos a seguir para conseguir el objetivo de este post son:

  1. Crear un proyecto Web de tipo "WCF Service Application".

 

1 [ServiceBehavior(Namespace="http://elGuerre.loc/Service1/")]
2 [AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Allowed)]
3 public class Service1 : IService1
4 {
5 public string GetData(int value)
6 {
7 return string.Format("You entered: {0}", value);
8 }
9
10 public CompositeType GetDataUsingDataContract(CompositeType composite)
11 {
12 if (composite.BoolValue)
13 {
14 composite.StringValue += "Suffix";
15 }
16 return composite;
17 }





1 <system.serviceModel>
2 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
3 ...


O,



Ahora tenemos la compatibilidad con ASP.NET  pero con la potencia de WCF, nuestra clase HttpContext (session, cookies, cache, etc) está lista para ser usada.


Si estáis interesados en esto, aquí os dejo unas cuantas referencias:



 


Saludos @3Cantos
Juanlu

Etiquetas: ,


lunes, 11 de febrero de 2008

Evitando el namespace "http://tempuri.org" con WCF

Hace unos días me toco quitar el ya conocido http://tempuri.org del WSDL y asignarle un namespace específico, es más, esto es lo recomendado por seguridad y como buena practica, en fin, para conseguirlo bastará con lo siguiente:

[ServiceContract(Namespace = "http://MyProject.Tests")]






[DataContract(Namespace="http://MyProject.Tests")]





1 <service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior">
2 <endpoint bindingNamespace="http://MyProject.Tests" address="" binding="wsHttpBinding" contract="WcfService1.IService1">


ó, graficamente:




El valor para esta propiedad, aunque puede ser cualquiera, ¡con un poco de sentido común, claro!, sería conveniente que tomara el mismo que el indicado para el "[ServiceBehavior]".



Este último punto fue el que más tardé en encontrar, ¡y mira que está visible! :-D  ¡si leyera un poco de vez en cuando!, jeje... ¡si es que lo dice claramente al pie de la ventana! De todos modos, es curioso, porque todos los post y artículos que hacen referencia a los namespaces, pasan por alto este último punto.


 


Una ayudita más, un gran logro, :-D.


Saludos
Juanlu

Etiquetas: , , ,


miércoles, 30 de enero de 2008

WCF - "ServiceModelReg -i"

Durante el día de ayer mientras trabajaba con WCF en una máquina virtual, tuve la necesidad de instalar Exchanger Server 2003 junto con OWA porque el proyecto en el que estoy en cierta forma lo requería, cual fue mi sorpresa cuando tras la instalación, los web services desarrollados con WCF (Framework 3.0) dejaron de funcionar. El error "The page cannot be display" o "Service Unavailable" ¿Por qué? ¿A que se debe esto?, pues bien, la respuesta es muy fácil,¡claro ahora que la conozco! Los ficheros ".svc" no son reconocidos, las ISAPI que tratan estos ficheros no se encuentran registradas y por tanto es necesario volverlas a registrar, jeje... ¡es lo que tiene el instalar y desinstalar cosas en las máquinas de desarrollo!

Estos son los pasos a realizar:

Tras la ejecución de este comando

En esta página, explica los pasos más en profundidad así como la reparación manual si fuera necesario.

 

Justo hoy, un añito más viejo, jejeje...
Gracias a tod@s por compartirlo conmigo
Juanlu

Etiquetas: , , ,


miércoles, 12 de diciembre de 2007

Windows Communication Foundation (WCF) - BUG 1

Seguimos con otra cosita interesante, ahora sobre WCF, se trata del Configurador "WCF Service Configuration Editor". Sabemos que es una herramienta "externa" pero a la vez, integrada en el Visual Studio 2008, bueno, o eso creo.

Es curioso, pero si intentamos abrir este configurador a partir de nuestro menú "popup" al hacer "click" sobre un fichero ".config" de un proyecto, debería abrirse, ese es su cometido, sin embargo, no ocurre así, no la primera vez, si, como digo, no, la primera vez, jejejeje....

Sigue estos pasos y verás:

Por último, vuelve a hacer "click" con el botón derecho sobre el item ".config" y........ "taaaaachaaaaannnn....", ahí esta el "tío", :-P

Pues nada, una vez más, mostrando un camino más fácil, o por lo menos, dando a conocer los tropiezos de un largo recorrido, :-D

Saludos desde Nuevos Ministerios
Juanlu

Etiquetas: , , ,


This page is powered by Blogger. Isn't yours?