Gerardo Contijoch

Experiencias del día a día trabajando con .NET – ASP.NET, C#, ASP.NET MVC y demas…

Archive for 28 febrero 2009

Problema con requests asíncronos en Silverlight 2

Posted by Gerardo Contijoch en febrero 28, 2009

Ayer fue uno de ‘esos días’ en el trabajo. Con ‘esos días’ me refiero a aquellos en los que uno pierde todo el día resolviendo (o al menos intentando resolver) un pequeñísimo inconveniente y no puede dar con la solución a pesar de estar convencido de que hay una. Uno esta a un paso de resolver todo, pero siempre hay un pero. En mi caso fue un pero grande, al punto en que no estoy seguro hasta donde vale la pena seguir el desarrollo de lo que estaba haciendo.

Espero que este post sirva de advertencia para los que deseen hacer algo similar a lo que hice yo y les ahorre bastantes horas de pruebas e investigación.

Lo que yo necesito hacer es una clase (dentro de una librería de clases Silverlight) que básicamente se comporte como un proxy a un WebService (cumple otras funciones este proxy, pero nos alcanza con saber que se conecta con un WebService). Como mi intensión es ocultar del usuario la verdadera implementación (es decir, la llamada al WebService en si), mi idea era consultar sincrónicamente este WebService cuando el usuario llame a un método y devolverle la respuesta en el momento, como si no hubiese WebService en el medio. Rápidamente encontré que no podía. Paso a explicar.

El problema está en como se comportan la clases HTTP (WebClient y HttpWebRequest en mi caso, pero lo mismo ocurre con los proxies a WebServices por ejemplo) en Silverlight.

Problema 1: Requests asincrónicos

En una aplicación WinForm o un site común las clases HTTP que vienen en el framework permiten hacer tanto requests sincrónicos como asincrónicos (por ejemplo, WebClient posee los métodos DownloadString y DownloadStringAsync), pero en Silverlight (que tiene una versión especial del Framework), solo se permite hacerlos de la última manera. Esto generó algunas quejas en la comunidad de programadores, pero parece que no va a cambiar ya que Microsoft se propuso respetar un estándar (NPAPI) que exige que estas llamadas sean hechas de manera asincrónica. Como Silverlight es un plugin para los browsers, el mismo necesita usar las APIS expuestas por este, las cuales están definidas por NPAPI (por lo menos en los browsers que respetan el estándar).

Así que me vi obligado a simular una operación sincrónica que por detrás funciona asincrónicamente. Para ello hice algo similar a esto (dejando de lado detalles como el manejo de excepciones y timeouts):

   1: public string Request() {
   2:
   3:     string respuesta = "";
   4:
   5:     WebClient client = new WebClient();
   6:
   7:     // Usamos este objeto para saber cuando se recibio la respuesta a nuestro request
   8:     AutoResetEvent arDownloadCompleto = new AutoResetEvent(false);
   9:
  10:     // Handler para procesar la respuesta
  11:     // Noten que nos limitamos a guardarla y a notificar que la recibimos
  12:     client.DownloadStringCompleted += delegate(object o, DownloadStringCompletedEventArgs ev) {
  13:         respuesta = ev.Result;
  14:         arDownloadCompleto.Set();
  15:     };
  16:
  17:     // Comenzamos el request (en este ejemplo, bajamos el HTML de www.example.com)
  18:     client.DownloadStringAsync(new Uri("http://www.example.com", UriKind.Absolute));
  19:
  20:     // Esperamos a que el método Set() sea llamado (esto deberia ocurrir cuando se complete la bajada)
  21:     arDownloadCompleto.WaitOne();
  22:
  23:     /* ...
  24:      * Procesamos la respuesta...
  25:      * ...
  26:      */
  27:
  28:     // Devolvemos la respuesta
  29:     return respuesta;
  30: }

La sincronicidad nos la da el objeto AutoResetEvent que en la línea 21 detiene la ejecución del hilo hasta que se haya completado la descarga.

Problema 2: Request solo en el UI Thread

Sin embargo este código no funciona debido a un pequeño detalle de la clase WebClient: el callback DownloadStringCompleted se ejecuta siempre en el UI Thread. En la mayoría de los casos eso es una buena noticia ya que podemos interactuar con la IU sin necesidad de estar usando el Dispatcher, pero en nuestro caso no lo es. Este comportamiento para nosotros significa que la ejecución de este método queda detenida en la línea 21, ya que nunca va a ejecutarse el callback debido a que el hilo esta ocupado deteniéndose a la espera del callback (sí, tiene sentido lo que dije, reléanlo).

Si ésa línea 21 hubiera sido la siguiente

// Esperamos 5 segundos como máximo para evitar un lockeo del hilo
arDownloadCompleto.WaitOne(5000);

hubiese continuado el proceso (luego de 5000 milisegundos), pero la variable respuesta hubiese estado vacía y el callback solo se hubiese ejecutado luego de que el UI Thread se haya liberado.

Como consecuencia de esto decidí darle una oportunidad a la clase HttpWebRequest (que, por cierto, es usada internamente por WebClient), que según leí no tiene esa limitación. Resulta sí la tiene, pero se presenta de otra manera. En el caso de HttpWebRequest, el callback se ejecuta efectivamente en un thread diferente al del código en el cual ejecutamos BeginGetResponse (una forma primitiva de DownloadStringAsync), pero ahora el problema no esta en el momento de ejecutar el callback, sino que esta al ejecutar el request en sí (algo que no vemos nosotros). Si bien uno puede crear hilos en Silverlight (y hacer requests asincrónicos en esos otros hilos), sólo el UI Thread es el que se comunica con el browser (¿recuerdan las APIs de NPAPI que expone el browser?) y por ello el request en sí se ejecuta en el UI Thread, independientemente de que thread vaya a ejecutar el callback. Una vez mas, tenemos un deadlock en la línea en que llamamos a WaitOne().

En definitiva, no encontré todavía manera de exponer una API sincrónica que funcione por dentro de modo asincrónico en Silverlight, pero si a alguien se le ocurre algo, no dude en dejar un comentario o contactarse conmigo.

Al mal tiempo, buena cara

Lo único positivo de perder un día entero con este tema es que aprendí mucho sobre el tema y sobre el funcionamiento interno de los requests, algo que me resulto interesantísimo (¡que nerd!), así que podría decirse que en realidad el día no estuvo del todo perdido :P

Nos vemos en el próximo post!

Referencias:

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in Desarrollo Web, Silverlight | Etiquetado: , | 2 Comments »

Como resolver el error ‘It is an error to use a section registered as allowDefinition=’MachineToApplication’ beyond application level.’

Posted by Gerardo Contijoch en febrero 22, 2009

Hoy mientras estaba configurando la autenticación de un site me encontré con este error. Es un tanto críptico, pero no tan difícil de entender si pensamos un poco (y conocemos otro poco). El mensaje básicamente nos esta diciendo que hay una sección de la configuración (en mi caso la sección <authentication>, pero puede ocurrir lo mismo con otras secciones) que esta registrada con un atributo allowDefinition=’MachineToApplication’ y por eso utilizarla más allá del nivel de aplicación no esta permitido.

El archivo web.config de mi aplicación se ve algo así (resumido para mayor claridad):

   1: <?xml version="1.0"?>
   2: <configuration>
   3:     ...
   4:     <location path="Admin">
   5:         <system.web>
   6:             <authentication mode="Forms">
   7:                 <forms loginUrl="Login.aspx" name=".ASPXFORMSAUTH" defaultUrl="~/Inicio.aspx" protection="Validation"/>
   8:             </authentication>
   9:
  10:             <authorization>
  11:                 <deny users="?" />
  12:             </authorization>
  13:         </system.web>
  14:     </location>
  15:     <system.web>
  16:         <authorization>
  17:             <allow users="*" />
  18:         </authorization>
  19:         ...
  20:     </system.web>
  21: </configuration>

Aparente no tiene nada de raro, se configura la autenticación y autorización del site, así como una de las secciones del site (la rama Admin). El error me decía que la linea 6 era la del problema.

Ahora veamos de entender el problema para poder resolverlo.

Si buscamos en nuestro web.config veremos que la sección <authorization> no esta definida en ningún lugar y eso se debe a que la misma esta definida en el archivo machine.config. El mismo se encuentra en el path ‘C:\Windows\Microsoft.NET\Framework\<versión del framework>\CONFIG\machine.config’.

La sección <authentication> se encuentra definida de la siguiente manera:

   1: <configuration>
   2:     <configSections>
   3:         ...
   4:         <sectionGroup name="system.web" type="System.Web.Configuration.SystemWebSectionGroup, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
   5:             ...
   6:             <section name="authentication" type="System.Web.Configuration.AuthenticationSection, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" allowDefinition="MachineToApplication"/>
   7:             ...
   8:         </sectionGroup>
   9:     </configSections>
  10: </configuration>

Como se puede ver, efectivamente esta sección esta ‘configurada’ para que solo pueda ser utilizada en el machine.config, en el web.config que se encuentra junto con el machine.config o en el web.config que se encuentra en el root de nuestra aplicación (para ver los posibles valores de este atributo y sus significados pueden ir a esta página). Y justamente ese era el problema. En mi web.config la seccion <authentication> estaba siendo utilizada dentro de la sección <location>. Esto es lo mismo que colocarla dentro de un web.config en el directorio especificado en el atributo path de la sección <location> y por eso ASP.NET se quejaba.

Todo se solucionó cuando moví la sección problemática fuera de <location> quedando el web.config de la siguiente manera:

   1: <?xml version="1.0"?>
   2: <configuration>
   3:     <location path="Admin">
   4:         <system.web>
   5:             <authorization>
   6:                 <deny users="?" />
   7:             </authorization>
   8:         </system.web>
   9:     </location>
  10:
  11:     <system.web>
  12:         <authentication mode="Forms">
  13:             <forms loginUrl="Login.aspx" name=".ASPXFORMSAUTH" defaultUrl="~/Inicio.aspx" protection="Validation"/>
  14:         </authentication>
  15:
  16:         <authorization>
  17:             <allow users="*" />
  18:         </authorization>
  19:     </system.web>
  20: </configuration>

La confusión mía estaba en que quería configurar la autenticación dentro de un subdirectorio, cuando en realidad solo tenia que configurar la autorización para ese subdirectorio. La configuración de autenticación se tiene que aplicar a nivel de site y no de subdirectorio, en cambio, la configuración de autenticación si puede ser diferente para distintos subdirectorios.

En esta página pueden encontrar algo mas de información sobre el tema.

Espero que les sea útil.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, ASP.NET, Desarrollo Web | Etiquetado: , , , | 10 Comments »

Keyword default en C#

Posted by Gerardo Contijoch en febrero 15, 2009

Desde la versión 2.0 de C# hay un nuevo uso para keyword default que no todos conocen y resulta de muchísima utilidad en ciertos casos. Típicamente a este keyword lo vemos en bloques de tipo switch, pero también puede ser utilizado para la inicialización de variables. En este último caso, la función del keyword es devolver el valor por defecto de un tipo dado. Veamos un ejemplo:

   1:
   2: public class UnaClase { ... }
   3:
   4: public struct UnaEstructura { ... }
   5:
   6: public void Test(){
   7:
   8:     int i = default(int);
   9:     string s = default(string);
  10:     UnaClase c = default(UnaClase);
  11:     UnaEstructura est = default(UnaEstructura);
  12:
  13:     /* ... */
  14: }
  15:

Acá la variable i se va a inicializar en 0 (que es el valor por defecto para los tipos numéricos), s en null (que es el valor por defecto para strings), c en null (valor por defecto para tipos por referencia) y est va inicializarse con una nueva estructura cuyos campos toman el valor por defecto de sus tipos.

En un código como el anterior no tiene mucha utilidad este keyword ya que sabemos de antemano el valor por defecto de los tipos dados y podemos asignarlos directamente. La verdadera utilidad se descubre cuando trabajamos con tipos genéricos:

   1: public class GenericList<T> {
   2:
   3:     private class Node {
   4:         public Node Next;
   5:         public T Data;
   6:     }
   7:
   8:     private Node head;
   9:
  10:     //...
  11:
  12:     public T GetNext() {
  13:         T temp = default(T);
  14:         Node current = head;
  15:         if (current != null) {
  16:             temp = current.Data;
  17:             current = current.Next;
  18:         }
  19:         return temp;
  20:     }
  21: }

En este ejemplo (sacado de acá) es imposible saber que valor por defecto hay que asignarle a la variable temp (dentro del método GetNext()) ya que su tipo es genérico. Por supuesto también puede utilizarse para determinar si una variable esta o no inicializada sin necesidad de conocer su tipo.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, C# | Etiquetado: , | Leave a Comment »

Hacer referencia a una librería de clases desde una aplicación Silverlight

Posted by Gerardo Contijoch en febrero 10, 2009

Hoy me vi en la necesidad de utilizar una librería de clases que había creado hace un tiempo, desde una aplicación hecha en Silverlight 2.0 y descubrí que no podía hacerlo. Luego de investigar un poco me entere de que Silverlight usa una versión especial del .NET Framework, lo cual explica porque no se puede referenciar a un assembly compilado con el framework estándar.

La única solución para resolver este problema es crear una nueva librería de clases Silverlight (un nuevo tipo de proyecto que se agrega al instalar Silverlight) y hacer referencia a ella. Algunas personas sugieren, para no repetir código que ya hemos escrito, agregar links los archivos de código (*) en vez de copiar todos los archivos de nuestra librería, pero no lo sugiero debido a que como los frameworks son diferentes, el código escrito en uno puede no compilar en el otro.

En la siguiente imagen se pueden ver algunos assemblies con versión 2.0.5.0 (pertenecientes a Silverlight):

referenciassilverlight-object-browser

Estos son una versión especial de los assemblies del framework 2.0 (de ahí su numeración) y se encuentran en ‘C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\’. Es importante tener en cuenta la versión y ubicación de estos assemblies ya que proyectos como los de Test no hacen referencia a estos y vamos a tener que agregarlas a mano, procurando referenciar las dlls versionadas como 2.0.5.0 y no las otras.

¿En que se diferencian los frameworks? No creo que sean tantas las diferencias entre ambos, pero me parece que son las suficientes para que no nos compile la mayoría de los proyectos con los que trabajemos, en especial si procesan xmls. Entre los assemblies modificados se encuentran System, System.Xml, System.Xml.Linq, y mscorlib. Los cambios que encontré yo en mi proyecto eran de fácil solución en su mayoría debido a que se trataban de sobrecargas de métodos que no existían en el framework de Silverlight.

¡Nos vemos en el próximo post!

(*) Para agregar un link a un archivo y no una copia del mismo en el Open file dialog que aparece cuando queremos agregar un item existente hay que seleccionar la opción Add As Link desde el menú que se despliega desde el botón Add. De este modo, estaremos referenciando al archivo original y no se creará una copia del mismo en la carpeta de nuestro proyecto.

referenciassilverlight-addasalinkPublicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in Desarrollo Web, Silverlight, Visual Studio | Etiquetado: , | 1 Comment »

Personalizar la carga de DataSets tipados

Posted by Gerardo Contijoch en febrero 6, 2009

Ayer comentaba que había comenzado a trabajar con DataSets tipados por primera vez y no tardé en encontrarme con algunos problemas, principalmente debido a la falta de costumbre al trabajar con ellos. Hoy me encontré con otro problema muy molesto que tiene una solución simplísima, pero que me costó encontrar.

El método estándar para llenar las tablas de un DataSet tipado es mediante el método Fill() del TableAdapter generado dinámicamente para cada tabla. El problema con el que me encontré fue que no había una manera de llenar las tablas con datos previamente filtrados. Luego de investigar un rato encontré con la solución: simplemente hay que abrir el DataSet en el editor de esquemas y hacer click derecho sobre la tabla que queramos filtrar. A continuación elegimos Add–>Query… y veremos que se abre un asistente que nos ayudará a crear una consulta nueva que filtre los datos como nos parezca. También es posible utilizar un SP ya existente o crear uno nuevo en el momento. Cabe aclarar que si nuestra consulta depende de valores dinámicos, los mismos pueden ser provistos en forma de parámetros en la consulta dentro del asistente que se abre y el asistente generará un método con los parámetros necesarios para la consulta.

Si nuestra consulta no esta asociada a una tabla en particular, esto mismo se puede hacer sobre el propio DataSet (haciendo click derecho sobre el fondo del editor de esquemas). En este caso, los métodos generados se encontrarán en un objeto QueriesTableAdapter.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in Visual Studio | Etiquetado: , , | Leave a Comment »

Actualizar el esquema de un DataSet tipado en Visual Studio

Posted by Gerardo Contijoch en febrero 5, 2009

No tengo la costumbre de trabajar con DataSets tipados principalmente porque trabajo con DataReaders o DataTables no tipados. Me gusta tener acceso a los datos y controlar lo que se hace con ellos yo mismo, sin ninguna clase que me los formatee ni organice automáticamente. Sin embargo, hoy decidí darles una oportunidad dentro de un prototipo de aplicación ya que precisaba hacer algunos INSERTs y SELECTs sobre pocas tablas y no quería perder tiempo con SPs escritos a mano.

La verdad es que los DataSets tipados son ideales para prototipos simples y me vinieron muy bien, pero me encontré rápidamente con una limitación: no hay una manera sencilla de modificar su estructura para reflejar los cambios en la estructura de la DB (algo relativamente común en las primeras etapas de desarrollo). En realidad si la hay, pero a lo que me refiero es a que esperaba alguna opción en algún lugar del estilo ‘Update DataSet Schema’ o ‘Sync DataSet Schema’.

Buscando un poco encontré dos soluciones que parecen ser las más adecuadas para estos casos. La primera consiste simplemente en borrar las tablas del DataSet y volver a agregarlas a mano desde el Server Explorer, mientras que la segunda consiste en modificar las consultas a mano con las cuales se generaron las tablas del DataSet (seleccionando la opción Configure del menú contextual que aparece al hacer click derecho en la tabla dentro del editor de esquemas del DataSet).

El problema de ambas soluciones esta en que al realizar la actualización uno pierde todos los cambios que pudo haberle hecho a los DataSets y DataTables. Para arreglar esto, acá sugieren transformar las clases generadas por Visual Studio en clases parciales (agregando ‘partial’ a la definición de la clase) y agregar las modificaciones a esas clases (ahora parciales) dentro de otros archivos, diferentes de los que genera Visual Studio de manera automática. Así, cuando se regeneren los DataSets nuestros cambios no son eliminados. No tengo a mano una copia de Visual Studio 2005 para verificarlo, pero la versión 2008 ya genera los DataSets y DataTables como clases parciales por lo que nos ahorraríamos un paso en este caso.

Esperemos que en una próxima versión de Visual Studio haya alguna manera un poco mas ‘user friendly’ de hacer esto.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in Visual Studio | Etiquetado: , , | 3 Comments »