Mostrando las entradas con la etiqueta C#. Mostrar todas las entradas
Mostrando las entradas con la etiqueta C#. Mostrar todas las entradas

sábado, octubre 03, 2009

BUG Warehouse. CSPS. La cadena vacía '' no es un nombre válido (The empty string ‘’ is not a valid name)

CSPS: Contexto, Situación, Problema, Solución
Bug: Defecto

Contexto
  • Microsoft Visual Studio 2008, NET Framewotk 3.5 SP1
  • Servicio Web ASMX existente expuesto mediante SOAP y HTTPS.
  • Cliente del servicio mediante protocolo SOAP.
Situación
  • Se tiene un servicio Web asmx operativo y conforme con Basic Profile Version 1.1 expuesto a través del protocolo SOAP.
  • Se toma una aplicación cliente cualesquiera (por ejemplo una consola) y se adiciona un referencia de servicio (Service Reference). Las referencias a servicios funcionan tanto con servicios de WCF o bien con servicios heredados tipo asmx. Se hace notar que también podría ser una referencia web (Web Reference) heredada de NET Framework 2.0 (asmx) y se produce el mismo comportamiento.
Problema

Al ejecutar el cliente se obtiene el siguiente mensaje de error:

   1: System.ServiceModel.FaultException was unhandled



   2:  



   3: Message="System.Web.Services.Protocols.SoapException: El servidor no puede procesar la solicitud. ---> System.ArgumentException: La cadena vacía '' no es un nombre válido.\n en System.Xml.XmlTextWriter.ValidateName(String name, Boolean NCName)\n en System.Xml.XmlTextWriter.InternalWriteName(String name, Boolean NCName)\n en System.Xml.XmlTextWriter.WriteQualifiedName(String localName, String ns)\n en System.Web.Services.Protocols.Soap11ServerProtocolHelper.WriteFault(XmlWriter writer, SoapException soapException, HttpStatusCode statusCode)\n en System.Web.Services.Protocols.SoapServerProtocol.WriteException(Exception e, Stream outputStream)\n en System.Web.Services.Protocols.WebServiceHandler.WriteException(Exception e)\n en System.Web.Services.Protocols.WebServiceHandler.Invoke()\n en System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()\n --- Fin del seguimiento de la pila de la excepción interna ---"



   4:  



   5: Source="mscorlib"



   6:  



   7: StackTrace:



   8:  



   9: Server stack trace: 



  10:  



  11: en System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)



  12: ...



  13: Exception rethrown at [0]: 



  14:  



  15: en System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)



  16:  



  17: en System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)



  18: ...



  19: en ....MyApp.MyMethod(UploadCollectionFileRequest request) en C:\...\Reference.cs:línea 357



  20: ...




El error no dice mucho y tampoco contiene información detallada de dónde ha sucedido como para hacer un seguimiento del código fuente. En la línea 19 se muestra un texto de ejemplo del detalle máximo referente al código, donde sólo se observa que hay algún problema con el “request” que se ha creado en nuestra referencia de servicio “Reference.cs”.



Solución


Si observamos la primera parte el problema se refiere a la dificultad de escribir el resultado de una serialización XML de forma correcta, debido a que hay un nombre inválido (vacío).



Al revisar el código fuente del Servicio Web se encontró que el servicio capturaba cualquier excepción y la convertía en una SoapException personalizada con las siguientes características:



try
{
//----------------------------------------------------------
// Custom code that throws an exception...
//----------------------------------------------------------

}
catch (Exception ex)
{
throw new SoapException("Mi descripción personalizada del error.", XmlQualifiedName.Empty, ex);
}
finally
{
// ...


}



Como se observa al preparar la excepción SOAP el parámetro XmlQualifiedName se está pasando con el valor “Empty”, lo que provoca que al intentar serializar la excepción esta no pueda procesarse.



Si bien es cierto que usted podría colocar cualquier nombre calificado, en vez de XmlQualifiedName.Empty y podría evitar este bug referido, por ejemplo, new XmlQualifiedName("Mi nombre para el código de excepción SOAP"), lo recomendable es respetar el estándar para el protocolo, ya que el parámetro del constructor de SoapException que acepta un XmlQualifiedName se llama “code” y se refiere al tipo de código de error de SOAP, por lo tanto el deber ser es, elegir alguno de los códigos disponibles, conocidos como códigos de error SOAP para la versión 1.1 del protocolo SOAP. En nuestro caso así queda la línea de excepción completa:



catch (Exception ex)
{
throw new SoapException("Mi descripción personalizada del error."
, SoapException.ServerFaultCode, ex);
}



Lo anterior resuelve el bug presentado y en su caso permite obtener la excepción subyacente que mostraría el verdadero error que se está produciendo en el servicio web.



---(FIN)---

martes, septiembre 22, 2009

Update Progress Solution for ASP.NET MVC with Ajax

Etiquetas de Technorati: ,,,,,
Introducción
Buscando alternativas para utilizar ajax y un estilo como el de update progress de ASP.NET para ASP.NET MVC me encontré con algunas soluciones que no eran lo que buscaba, algunas mezclando componentes de servidor otras con código demasiado abstracto para algo que debería ser simple.
Finalmente decidí hacerlo de otro modo y obtuve esta solución simple basada directamente y exclusivamente, en lo que ofrece ASP.NET MVC. El concepto que propongo está basado en MVC y proporciona control total sobre lo que queremos que se muestre. Es objetivo de este artículo generar una solución base los más directa posible con los controles existentes de MVC para facilitar la comprensión, con menos abstracciones, tal como se muestra a continuación.
Audiencia
Si usted no tiene conocimiento previo sobre los conceptos básicos de ASP.NET MVC le recomiendo revisar los mismos.
Lo que queremos lograr

Veamos primero lo que queremos obtener.
El usuario hace clic en la liga “Actualizar foo…”
image
El sistema pide su confirmación.
image
El usuario acepta y se procede con la actualización mostrando una imagen animada.image
Al finalizar el procedimiento de actualización se muestra un mensaje de “actualización lista” (Ready.).
image
En una vista cualesquiera (sea de un control de usuario o vista final) se crea un control de usuario llamado UpdateProgressControl y se inicializa su modelo UpdateProgressViewModel con un nombre de negocio que lo identifique “UpdateFoo”, el nombre debe ser válido para un control pero sólo basta con que contenga el sentido de negocio (los prefijos y/o sufijos como para un control, por ejemplo “divUpdateProgressUpdateFoo” los coloca el propio modelo).
Veamos una vista con un vínculo ajax (liga, link) y los elementos comentados.

<p>
<% =Ajax.ActionLink("Actualizar foo..."
, "Update1"
, new AjaxOptions()
{
Confirm
= "¿Seguro desea proceder con la actualización?"
, OnBegin
= UpdateProgressViewModel.GetFunctionNameForShow(
"UpdateFoo")
, OnComplete
= UpdateProgressViewModel.GetFunctionNameForHide(
"UpdateFoo")
}
) %>
</p>
<% Html.RenderPartial("UpdateProgressControl"
, new UpdateProgressViewModel("UpdateFoo")); %>



Como se observa en el código, mediante AjaxOptions se establecen los nombres de las funciones javascripts, utilizando métodos estáticos del propio modelo que reciben como parámetro el nombre simple de negocio que hemos definido antes (“EditFoo”). Más adelante se verán los detalles.


En resumen, el concepto central es simplemente:




  1. Crear el control de usuario UpdateProgressControl.


  2. Configurar los métodos OnBegin y OnComplete de AjaxOptions para mostrar y ocultar el control.



Además podemos colocar las características o estilo de nuestro control en un Cascading Style Sheets, CSS de nuestro sitio.



Índice



  1. Crear estilos (CSS).


  2. Crear un modelo para la vista.


  3. Crear la vista del control UpdateProgress.


Veamos cada punto en el mismo orden.




1. Crear los estilos.


Creamos un conjunto de estilos que utilizará nuestro control y nos permite modificar la presentación del mismo.



/* Update Progress  
----------------------------------------------------------*/
.updateProgress
{
display: none;
background-repeat: no-repeat;
background-image: url('../Content/Images/ajax-loader.gif');
padding-top: 5px;
padding-bottom: 20px
}
.updateProgress label
{
padding-left: 45px
}


2. Crear un modelo para la vista.

El modelo cuenta con:





  1. Varios constructores.


  2. Dos funciones estáticas para recuperar (y generar) los nombres de funciones de java script que se utilizarán para mostrar y ocultar la operación en progreso.


  3. Varias propiedades básicas (ID, Name y Message) para utilizarlas en el control.



image   
A continuación se muestra solamente el constructor más simple que inicializa valores predeterminados.



/// <summary>
///
Initialize update progress with default message.
/// </summary>
/// <param name="name">
Update progress name.</param>
public UpdateProgressViewModel(string name)
: this(name, UpdateProgressViewModel.GetInProgressMessage(), false)
{
}


3. Crear la vista del control UpdateProgress.


Ahora creamos la vista parcial del control Update Progress que utilizará el modelo previamente creado.

La vista integra los estilos definidos, unas rutinas de javascript y el modelo para generar un control simple html que se desplegará y ocultará a conveniencia durante las llamadas ajax de nuestras páginas consumidoras.


Nuestro control de usuario tiene dos DIV para desplegar nuestra imagen de actualización y para desplegar el mensaje de actualización terminada. El modelo es el encargado de generar los nombres o ID's que necesitemos.



Este primer DIV es para desplegar la imagen de actualización y se carga desde el CSS.



<div id="<%=Model.ID%>" class="updateProgress" style="display: none">
<
label>
<%= Html.Encode(Model.Message) %>
</label>
</
div>



El segundo DIV se encarga de desplegar un mensaje de actualización completada (Ready).



<div id="<%=Model.ID%>MessageResult" class="updateProgressResult" style="display: none">
<
label>
<%= SmartResource.GetString("ReadyLabel", "Ready.") %>
</label>
</
div>



Se puede sustituir la línea de “<%= SmartResource…” por un mensaje fijo.

Finalmente nuestro control de usuario mediante el modelo también genera el javascript que necesitamos para mostrar y ocultar nuestro control. Por ejemplo para mostrar el control y tenemos:



function <%=Model.FunctionNameForShow%>() {
$("#<%=Model.ID%>").show();
$("#<%=Model.ID%>MessageResult").hide();
var disableSubmit
= <%=Model.DisableSubmitOnRequestString %>;
if(disableSubmit)
{
//Disable submits.
$(":submit").each(
function (i)
{
this.disabled = true;
}
);
}
}



Como se observa estamos aprovechando las facilidades de jQuery para referenciar los elementos que queremos controlar.


En una próxima entrega podríamos explorar las ventajas de adicionar más abstracción a este tema y generar un MVC AjaxHelper especializado para este control que tanta utilidad tiene, lo que simplificaría aún más la llamada desde el cliente. En vez de utilizar el Ajax.ActionLink, sería tan simple como esto:


<p>

   
<%=Ajax.UpdateProgressLink("Actualizar foo helper..."

         
, "Update1"

         
, "¿Seguro desea proceder con la actualización?"

         
, "UpdateFoo") %>

</p>



---(Fin)---