Orden de campos del DataContract

Cuando estaba preparándome la certificación de WCF, descubrí algo que es conveniente tener apuntado, pues puede dar bastantes quebraderos de cabeza: el orden en que aparecen los campos de información en los mensajes WCF una vez serializados. Estas son las reglas:

  • Si el contrato hereda de otro tipo, aparecen en primer lugar los elementos de ese tipo base.
  • Después, los elementos del propio contrato que no tengan la propiedad Order de DataMemberAttribute, ordenados alfabéticamente.
  • Por último, los elementos que tengan la propiedad Orden, según el valor asignado.

Me ha resultado curioso que aparezcan primero los elementos sin la propiedad Order. Seguramente haya una razón que desconozco para que éste sea el comportamiento, pero entiendo que puede llevar a error fácilmente. ¿Por qué? Pues porque si seleccionas un elemento que quieres que vaya primero, y le das la propiedad Order = 1, probablemente, de desconocer estas reglas, esperarás que con esto sea suficiente para que aparezca en primer lugar en el mensaje WCF.

Pero no, será necesario que asignes valores a todos los otros elementos, para que no se antepongan al que has elegido tú, por aquello de no tener propiedad Order.

Por último, destacar que estas reglas aplican también al orden en que aparecen los elementos que añadamos a un MessageContract con el atributo MessageBodyMemberAttribute.

Algún día tengo que buscar porqué la gente de .NET decidió esto así.

WCF: Validación de certificados

Cuando se está en un entorno de desarrollo, es bastante común utilizar certificados “auto-firmados” (self-signed) para bindings que requieran seguridad.

Sin embargo, WCF no acepta este tipo de certificados, lanzando una excepción de tipo SecurityNegotiationException, con un mensaje acompañando del estilo a “Could not establish trust relationship for the SSL/TLS secure channel with authority ‘localhost:8732’”. ¿Qué hacer? Es fácil, simplemente hay que “engañar” al cliente para que acepte este certificado que el servidor nos está ofreciendo para realizar la conexión SSL, aunque esté firmado por sí mismo.

Para ello, en el cliente, es necesario introducir el siguiente código:

   1: static void Main(string[] args)
   2: {
   3:     using (DemoService.GetHeadersClient proxy = new DemoService.GetHeadersClient())
   4:     {
   5:         ServicePointManager.ServerCertificateValidationCallback += 
   6:             new System.Net.Security.RemoteCertificateValidationCallback(ValidateCertificate);
   7:  
   8:         // Do stuff with proxy object
   9:     }            
  10: }
  11:  
  12: public static bool ValidateCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
  13: {
  14:     return true;
  15: }

El método definido será invocado por WCF en el momento de la validación del certificado, asegurándonos que el cliente va a aceptarlo en cualquier caso.

Por supuesto, esto es algo totalmente desaconsejado en cualquier entorno de producción. Pero para salir del paso en desarrollo, viene muy bien.

Respuesta original vista aquí.

Petición POST con C#

Dicen que nunca te acostarás sin aprender algo nuevo. Yo he tenido mi ración diaria con la forma de realizar una petición POST desde .NET. Probablemente sea una simpleza que todo el mundo sabe, pero yo lo desconocía.

El proceso es bastante intuitivo, salvo en la forma de añadir parámetros a la petición, que a mí me ha dejado algo más sorprendido. Pasos:

  1. Instanciar una URI con la dirección a la que se quiere hacer la petición, pero sin parámetros.
  2. Crear el objeto Request. Castearlo directamente a HttpWebRequest, puesto que el Create, aunque devuelva un WebRequest, en realidad es un objeto HttpWebRequest (que es un subtitpo de WebRequest).
  3. Configurar la request, indicando que se va a usar POST (obligatorio), el content-type (opcional), la longitud (obligatorio) y la versión del protocolo HTTP (opcional).
  4. Obtener un steam de la petición. Se va a añadir los parámetros como si fueran bytes, que al fin de cuentas es el contenido de la petición.
  5. Terminamos haciendo una llamada a GetResponse para obtener la respuesta.

El código de todo este proceso sería algo parecido a lo siguiente:

   1: HttpWebRequest httpRequest = 
   2:     HttpWebRequest.Create("http://direccion.com/pagina") as HttpWebRequest;            
   3: httpRequest.Method = "POST";
   4: httpRequest.ProtocolVersion = HttpVersion.Version11;
   5: // Parameters es un string que contiene todos los parametros en 
   6: // formato param1=value&param2=&param3=value
   7: httpRequest.ContentLength = Encoding.ASCII.GetByteCount(parameters);
   8: httpRequest.ContentType = "application/x-www-form-urlencoded";            
   9:  
  10: using (Stream requestStream = httpRequest.GetRequestStream())
  11: {
  12:     byte[] parametersBuffer = Encoding.ASCII.GetBytes(parameters);
  13:     requestStream.Write(parametersBuffer, 0, parametersBuffer.Length);
  14: }
  15: WebResponse httpResponse = httpRequest.GetResponse();

Añadir assemblies a Visual Studio

Muchas veces necesitamos que un assembly esté en el GAC para no estar preocupados de incluirlo en cada proyecto. Simplemente, lo seleccionamos de la ventana “Add Reference” de Visual Studio, y somos felices con ello. Sin embargo, esta semana pasada me surgió la necesidad de añadir un assembly al GAC por mí mismo.

AddReferenceWindow

No es que sea demasiado difícil, basta con copiarlo a c:windowsassembly. Hasta un tecnicoless podría hacerlo. Sin embargo, no apareció mágicamente en la ventana de Add Reference. ¿Qué había hecho mal? ¿Qué me faltaba? Buceando un poco por Internet encontré a un tipo explicando cómo hacerlo de 3 formas distintas.

Huelga decir que elegí la más sencilla: copiar mi assembly a C:Program FilesMicrosoft Visual Studio 9.0Common7IDEPublicAssemblies

Rearrancar el Visual Studio fue suficiente para ver mi assembly en la pestaña .NET. ¿Fácil no?

Usuarios, permisos y SQL Server

Recientemente me encontré ante la situación de tener que configurar, en un proyecto de Database de Visual Studio 2008, una serie de usuarios, permisos y roles.

En concreto, necesitaba crear un rol que pudiera ejecutar procedimientos almacenados y hacer SELECTs sobre un determinado esquema. A este rol, a su vez, pertenecería un usuario que también debía crearse dinámicamente en el script. Tras una mañana de investigación, estas son mis conclusiones.

Logins y Users

En SQL Server hay que distinguir entre Logins y Users. Los Logins se crean a nivel de servidor de base de datos. Pueden crearse como Logins de SQL Server, o pueden crearse a partir de una cuenta Windows, lo que en general es más seguro por utilizarse la autenticación integrada. En mi caso era precisamente eso lo que pretendía hacer, crearlos a partir de una determinada cuenta Windows. Esto es muy sencillo de hacer, basta un comando como el que se puede ver a continuación:

USE [yourdb]
GO
CREATE LOGIN [YourDomainYourUser] FROM WINDOWS
GO

Sin embargo, esto tiene una pega: no se puede hacer en los proyectos de base de datos de Visual Studio 2008, o al menos yo no he encontrado el modo. Tiene su sentido, de todas formas, puesto que estos proyectos no dejan de ser la creación de una base de datos concreta, y no debería afectar a la configuración del servidor en sí.

Una vez creado el Login, ya podemos crear un usuario y asociarlo a él. Esta parte sí se puede añadir al proyecto de BD de VS2008, en concreto en la carpeta Users dentro de Security. El TSQL necesario sería algo así:

CREATE USER [YourDomainYourDbUser] FROM LOGIN [YourDomainYourUser]
GO

Bien, con esto ya tendríamos nuestro usuario concreto de la base de datos creado, asociado a un Login a nivel del servidor.

Roles y permisos

El siguiente paso es crear el rol al que vamos a asociar el usuario que recién hemos creado. Este rol va a pertenecerle a dbo. No podemos darle sus permisos directamente en el script que se crea en la carpeta Database Roles de Security, esa parte tendremos que añadirla en un script de Post-deployment. Así que en el script de creación del rol simplemente tendríamos la siguiente sentencia TSQL:

CREATE ROLE [YourNewRole] AUTHORIZATION [dbo]
GO

Quedarían dos pasos por ejecutar: darle los permisos al rol que necesite y asociarle el usuario que creamos al principio.

La primera parte se completa con una sentencia parecida a ésta. Sería necesario indicar qué esquema de los existentes en la base de datos, va a poder el rol ejecutar sus SPs.

GRANT EXECUTE ON SCHEMA ::[YourSchema] TO [YourNewRole]
GO

En cuanto a la segunda, bastaría algo como lo que sigue, apoyándonos en uno de los procedimientos almacenados del sistema:

EXEC sp_addrolemember N'YourNewRole', N'YourDomainYourUser'
GO

Bibliografía: