Armando un control personalizado necesitaba setear los valores de atributos HTML, lo de siempre class, rel, pero especialmente atributos data por ejemplo los que son necesarios los binding de los frameworks javascript MVVM como KnockoutJS, AngularJS…
Estos atributos tienen esta forma: data-bind, ng-mode, ng-click
Si lo queremos insertar en un Textbox/TextboxFor en el parametro htmlAttibutes una propiedad con guion del medio en el nombre:
NOTA: Aquí la @class esta para cuando en C# necesitamos utilizar una palabra reservada como nombre de propiedad de un objeto anónimo
Bueno es conocido que debemos colocar con guiones bajos es decir data_bind y convertirá en un atributo data-bind
Pero necesitaba realizarlo en un control personalizado… asi que en vez de “armar el código para realizar esto” investigue un poco como estaba armando el propio y simple textbox mirado el código fuente de NET Framework.
Di con este método estático HtmlHelper.AnonymousObjectToHtmlAttributes (en System.Web.Mvc.HtmlHelper) dentro de que en la ayuda nos explica todo… es el responsable de armar estos atributos

“…Reemplaza los caracteres de subrayado (_) por guiones (-) en los atributos HTML especificados….”
El código:
//from System.Web.Mvc.HtmlHelper
public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes)
{
RouteValueDictionary result = new RouteValueDictionary();
if (htmlAttributes != null)
{
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(htmlAttributes))
{
result.Add(property.Name.Replace('_', '-'), property.GetValue(htmlAttributes));
}
}
return result;
}
Entonces el código quedo algo así para armar la cadena de atributos a insertar en mi control personalizado atributosCadenaHtml.ToString();
...
var atributosFormateados =
HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
var atributosCadenaHtml = new StringBuilder();
foreach (var atributo in atributosFormateados)
{
atributosCadenaHtml .AppendFormat("{0}=\"{1}\"", atributo.Key, atributo.Value);
}
...
Enlaces que pueden ayudar
Hace un par de semanas salió el Update 2 para Visual Studio 2012 y para el TFS2012… y por la oficina lo descargamos completo para no estar realizando esto cada vez que necesitábamos tanto las maquinas de desarrollo como las virtuales que armamos. Lo único malo era que la descarga completa lo hacia con para todos los idiomas disponible (esta descarga completa era de unos 1.8Gb), pero bueno era la alternativa de tenerlo “en modo offline”
Descargar .ISO
Pero recién me entero que se “alinearon los planetas” y ya esta la descarga oficial del ISO
Enlace directo al .iso (VS2012.2.iso) http://go.microsoft.com/fwlink/?LinkID=298533 (y por supuesto también de 1.8Gb)
NOTA: Seria bueno que el enlace al .iso estuviera en el enlace oficial de descarga del Update 2 ;)
http://www.microsoft.com/en-us/download/details.aspx?id=38188
Fuente:
http://blogs.msdn.com/b/visualstudio/archive/2013/05/03/announcing-availability-of-isos-for-visual-studio-updates.aspx
Esta semana finalizo un curso que tuve el agrado facilitarlo con Federico y Juan Pablo (los tres jugadores mas a la derecha)

El curso fue en el marco de las Becas EmplearTec Programación Orientada a .NET Intermedio que si bien son del 2012 (se comenzo en Diciembre y retomamos la segunda etapa en Febrero
Los temas que tratamos lo podríamos resumir en:
- Patrones de diseño
- Entity Framework
- ASP.NET MVC 4.0
- KnockoutJs
- Bonus Track: SignalR (que no entro en el examen)
Bueno como siempre agradecimientos…
Porque si bien preparamos los temas y facilitamos ejemplos prácticos… siempre aprendemos cosas nuevas, muchas veces nuevos conocimientos técnicos (afianzar otros) pero también relaciones humanas e intercambio de ideas/opiniones.. asi que gracias a los que cursaron (y nos aguantaron)
Agradezco a la gestión del Polo IT Corrientes (desde Andrés y Fabián los que gestionan los cursos hasta Carlos que nos aguanta a los profesores). Esta gestión se realiza directamente con CESSI y el Ministerio de Trabajo y que hace posible estos tipo de cursos en Corrientes.
Esperemos que el contenido haya quedado (por lo menos de interrogante… a seguir profundizando)
Mas info Sobre EmplearTec
Que son las becas EmplearTec?
“Con el objetivo de fomentar más y mejor empleo en las áreas de software y tecnología a nivel nacional, el Ministerio de Trabajo, Empleo y Seguridad Social de la Nación, en conjunto con CESSI, y con el apoyo de empresas referentes del sector, ofrecen cursos de capacitación gratuita en todo el país.”
Similares que tuve la oportunidad de facilitar:
Si alguna vez te toco trabajar en ambiente web con requerimientos de publicación de archivos, subir archivos desde el cliente o descargarlos, guardar el contenido… cuando enviamos un email con datos adjuntos (aunque esto ultimo se realiza automáticamente) seguramente has trabajado con tipos MIME (Multipurpose Internet Mail Extensions) Y seguramente tienes en tu app o componente un mecanismo para mapear extension a tipo MIME.
Para los que comienzan o necesitan utilizar… Una pregunta relacionada con MIME es ¿Como recuperamos el tipo de un archivo a partir de la extensión o viceversa?
Las opciones:
- Obtener tipo MIME de la Registry (incluso la inversa, la extensión asociada)
- Obtener tipo MIME “manualmente” ayudándonos con una estructura de un Dicccionario (o switch kilométrico)
- Obtener tipo MIME mediante el método GetMimeMapping de la clase System.Web.MimeMapping [Nuevo desde ASP.NET 4.5]
- 3.1 App Pool en Modo Clásico: Obtiene los valores de un diccionario (idem opción 2)
- 3.2 App Pool en Modo Integrado: Obtiene los valores de la lista de tipos Mime configurada en el IIS (y/o app web)
Vemos los pro y contra que tiene cada uno así podemos conocer cuando utilizarlos y cuando “no”.
OPCION 1: Obtener tipo MIME de la Registry de Windows
Si bien esta opción no es recomendada.. Seguramente habrás encontrado algún código que realiza esto. La idea aquí es obtener el tipo MIME o la extensión a partir de la búsqueda dentro del árbol en la Registry
HKEY_CLASSES_ROOT\MIME\Database\Content Type

A tener en cuenta: El inconveniente de esta opción es que debe estar cargado en el la registry de nuestro sistema operativo donde ese alojara la aplicación web todos los tipos que necesitemos (y allí solo se registran los que las app registran/instalan o viene predeterminado). - Se debe tener permisos para agregar, así que no es muy factible de escalar en nuestras app web
Como podemos ver, no es muy oportuno utilizarlo, pero dejo la opción para indicar los inconvenientes.
OPCION 2: Obtener el tipo MIME “manualmente” ayudándonos con una estructura de un Diccionario (o switch kilométrico)
La mejor recomendación es utilizar un diccionario para acceder por la key (pero podrías también tener una lista genérica con un tipo de entidad creada para tal fin)
Ejemplo con un Dicccionario:
private static readonly Dictionary<string, string> TiposMime =
new Dictionary<string, string>((IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase)
{
{".323", "text/h323"},
{".3g2", "video/3gpp2"},
{".3gp2", "video/3gpp2"},
{".3gp", "video/3gpp"},
{".3gpp", "video/3gpp"},
{".aac", "audio/aac"},
{".aaf", "application/octet-stream"},
....
A tener en cuenta que necesitaríamos buscar en ella a partir de los dos valores. El mas común siempre es buscar a partir de la extensión pero también nos puede servir en algún momento la inversa buscar a partir de un tipo MIME la extensión correspondiente.
OPCION 3: Obtener mediante el método GetMimeMapping de la clase System.Web.MimeMapping [Nuevo desde ASP.NET 4.5]
Desde la versión de ASP.NET 4.5 tenemos la clase System.Web.MimeMapping que posee un solo método de clase GetMimeMapping que lo bueno es que obtiene los valores MIME de dos lugares diferentes dependiendo del modo que esta configurado el Pool de Aplicaciones en el IIS donde se aloja nuestra aplicación web
- Si se encuentra en modo Clásico: Obtiene el valor de un diccionario que se encuentra entro del assembly (idem a opcion2). Si somos curiosos y vemos el código fuente vemos 343 mapeos (System.Web.dll v4.0.30319.17929)
- Si se encuentra en modo Integrado: Obtiene los valores de la lista de tipos Mime configurada en el IIS. Lo cual es mucho mejor y extensible sin tocar el código de nuestro mapeador
Podemos ver como esta codificado y así investigamos como se obtiene los datos

http://msdn.microsoft.com/en-us/library/system.web.mimemapping.getmimemapping.aspx
Lo bueno
- Al actualizar el sistema operativo más tipos MIME se añaden a IIS
(incluso podríamos agregar los que necesitamos)
- Podemos agregar tipos MIME personalizados por app con solo utilizar la seccion system.WebServer\staticContent
(que desde IIS7 toma)
- [Otro post] Agregar tipos MIME personalizados a IIS/IIS Express o nuestra app web en la seccion staticContent del web.config
A tener en cuenta
- El diccionario embebido que se utiliza en modo Classic contiene 343 mapeos (System.Web.dll v4.0.30319.17929)
- El diccionario en modo Integrado (IIS Express): 374 mapeos (archivo ..\IISExpress\config, seccion system.WebServer\staticContent)
… pero además podemos agregar lo que necesitamos para una app específica
Ejemplo
Por ejemplo si necesitamos mapear la extension .yoda al tipo mime starwars/characters
Utilizando el IIS Express (o IIS 7 o superior), podríamos mapear el contenido estatico directamente en el web.config, para que sola esta aplicacion tome este tipo MIME
(mas abajo esta el ejemplo para descargar)
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".yoda" mimeType="starwars/characters" />
</staticContent>
</system.webServer>
</configuration>
Entonces en modo Integrado (nos devolveria) starwars/characters y en modo clasico el tipo MIME por default application/octet-stream

Ejemplo para descargar
Enlace para descargar: http://sdrv.ms/WXeNel
Referencias
Bueno si tienes varias maquinas en una intranet para que estar descargando en cada una el instalador a través de web plataform, es bueno descargarlo el instalador y compartirlo
Como sabrán se descarga a través del Web Platform installer
http://www.microsoft.com/web/handlers/webpi.ashx?command=getinstallerredirect&appid=ASPDOTNETandWebTools2012_2
Puedes descargar ASP.NET and Web Frameworks 2012.2 (32Mb) desde Microsoft Download.
El enlace directo es:
http://download.microsoft.com/download/6/5/6/6562AFBE-9503-4E64-970C-1427133FCD73/AspNetWebTools2012Setup.exe
(NOTA: No se porque no esta buscando por el texto, el que aparece en el buscador es e la versión RC que tiene 50Mb y es de Dic/2012, debe ser para que lo hagamos por Web Platform installer)
Bueno pero si descargamos directamente la versión final que pesa 32Mb la podremos instalar tranquilamente…

Si tienes “Microsoft Visual Studio 2012 for Web” (que es la version express para web) tendrías que descargar esto
Referencia
Como podemos limpiar los controles dentro de un ModalPopupExtender (léase dentro de un panel asociado a un ModalPopupExtender)? O simplemente realizar acciones en el cliente cuando se visualiza un ventana modal?
Bueno podemos limpiarlo al momento de realizar la acción de postback. Pero si lo queremos limpiar en el cliente? Que tenemos disponible?
Aquí tendremos que utilizar los propiedades OnOkScript y OnCancelScript, pero también podremos suscribirnos a estos eventos con nuestras propias funciones, para ello utilizaremos: add_showing / add_shown / add_hiding / add_hidden
Además dejo un ejemplo para descargar.
Vemos entonces:
- Como descargar e instalar AjaxControlTookit en VS2012 (VS2010) con NuGet
Como descargar/instalar los componentes, agregarlos a la barra de herramientas - Manipular el ModalPopup vía Javascript
- ModalPopup y sus eventos (suscripción a eventos)
1) Como descargar e instalar AjaxControlTookit en VS2012 (VS2010) con NuGet
Como hace un par de años no utilizo AjaxControlTookit, estoy mucho con proyectos ASP.NET MVC… pero siempre es bueno refrescar componentes. Y si estas utilizando ASP.NET Webforms es util esta librería que nos da un abanico de controles y extensiones de controles webforms para utilizarlo con “ajax features” sin escribir mucho codigo, simplemente configurando.
Lo bueno de ver esto es que me hizo ver la forma de descargar estos componentes por NuGet en un proyecto con VS2012…
Asi que para descargarlo simplemente ir al Administrador de Paquetes de NuGet

Y buscarlo…

Y como ya lo conocemos a la configuración… pero nuget lo hace por vos. Nos queda asi (y algo mas para agregar los controles)
Te agrega los assemblies necesarios en el BIN
|  |
| Y te configura el prefijo para los controles en el web.config |  |
Agregar los controles en la barra de herramientas (esto no es automático) Pero te creas el nuevo tab en la barra y arrastras simplemente el assembly |  |
Para utilizar el ModalPopupExtender ya nos comentaba algo Luis Miguel Blanco
Pero además tenemos el sitio de ejemplo de AjaxControlToolkit para visualizar el modo de utilización del ModalPopupExtender
2) Manipular el ModalPopup vía Javascript
Como Visualizar/Mostrar vía javascript una ModalPopupExtender?
AQui debemos obtener el Id del lado del cliente, mediante la propiedad ClientID (que tambien podemos modificar su comportamiento con la propiedad ClientIDMode), luego nos valemos del metodo $find (acceso directo al método findComponent de la clase Sys.Application)
function abrirModal() {
var modalId = '<%=ModalPopupExtender1.ClientID%>';
var modal = $find(modalId);
modal.show();
}
Para ocultar el metodo: hide();
NOTA: Si vamos al ejemplo de ModalPopup Demonstration podemos ver en el código HTML como se realiza dicha acción.
3) ModalPopup y sus eventos (suscripción a eventos)
Eventos: add_showing / add_shown / add_hiding / add_hidden (y sus respectivos remove)
Podemos ver el archivo ModalPopupBehavior.pre.js del código fuente en CodePlex (ajaxcontroltoolkit.codeplex.com) de AjaxControlToolkit que tiene estos eventos, que son metodos para agregar nuestros funciones cuando estan sucediendo estos eventos…
- add_showing / remove_showing: Cuando se esta por visualizar el modal
- add_shown / remove_shown: Cuando ya se visualizo el modal
- add_hiding / remove_hiding: Cuando se esta comenzando a ocultar (se dispara primero)
- add_hidden / remove_hidden: Cuando ya se oculto el modal (luego recien se dispara el OnCancelScript)
Ejemplos del codigo fuente:


Suscribiéndose con funciones propias a los eventos
Aquí necesitamos realizar esto en el load, y no suscribimos a la función add_load de la aplicación del lado del cliente con (puedes descargar el ejemplo de código que dejo mas abajo)
Sys.Application.add_load(modalInicializar);
NOTA IMPORTANTE: Esto debe estar dentro del tag form de nuestro webforms
Bueno en el ejemplo podemos ver como disparar estos eventos
<script type="text/javascript">
Sys.Application.add_load(modalInicializar);
function abrirModal() {
var modalId = '<%=ModalPopupExtender1.ClientID%>';
var modal = $find(modalId);
modal.show();
}
function modalInicializar() {
var modalId = '<%=ModalPopupExtender1.ClientID%>';
//Antes de visualizarlo
$find(modalId).add_showing(modalShowing);
//Una vez que se renderizo
$find(modalId).add_shown(modalShown);
//Ocultando...
$find(modalId).add_hiding(function () {
alert('Hiding...');
});
//Una vez que se oculto
$find(modalId).add_hidden(function () {
alert('Hidden...');
});
}
</script>
Como vemos hay varias formas de suscribirse, pasando como parámetro la función a llamar o escribiendo la función anónima directamente… tu eliges dependiendo de tu política de desarrollo
Ejemplo: Limpiar controles al momento de visualizar/mostrar el modal
Aqui en el evento previamente que nos “suscribimos con nuestra función”:
$find(modalId).add_showing(modalShowing);
Entonces seria algo así en el ejemplo para descargar:
function modalShowing() {
alert('modalShowing...');
modalLimpiarControles();
}
function modalLimpiarControles() {
//OPCION1: Limpiando cada control con javascript "puro"
//------------------------------------------------------
//var txtNombre = '<%=txtNombre.ClientID%>';
//var txtApellido = '<%=txtApellido.ClientID%>';
//document.getElementById(txtNombre).value = '';
//document.getElementById(txtApellido).value = '';
//
//
//OPCION2: Limpiando con jquery todos los textbox
//-------------------------------------------------------
var panelModal = '<%=Panel1.ClientID%>';
jQuery('#' + panelModal + ' input[type=text]').val('');
}
Como verán la parte de limpieza de controles es porque en las preguntas de los foros de MSDN este era el objetivo final (la cual me disparo que publique este post que lo tenia en borrador), pero puede ser cualquier acción necesaria sobre estos controles (focus, obtener datos, presentar imágenes de cargando.. etc)
Ejemplo para descargar
Puedes descargar el ejemplo “simple” desde aquí: ASP.NET-ModalPopupExtender-Demo.zip
Enlaces
Como asignar un DefaultButton para un formulario de Login que se encuentra en un layout con MasterPages y el común denominador de un formulario de login que siempre lo podemos encontrar del lado derecho o arriba a la izquierda.
Esta fue la pregunta desde los foros de MSDN de ASP.NET que si bien no es fácil asignar esta propiedad cuando el botón objetivo se encuentra dentro de un template de un webcontrol, se puede hacer y aqui dejo para ”ayuda-memoria” y referencia.
Si bien lo podemos hacerlo mediante javascript (algo similar ya lo comente por aquí) la idea en el presente articulo es aplicar la propiedad DefaultButton que lo tiene los controles Form (HtmlForm) y Panel.
Particularmente por el layout presentado era que MasterPage que en una pagina con un formulario, cuando se quería dar login y desde el texbox de password al “presionar el enter” no era el evento por default el botón de login sino otra parte de la pagina. Como sabrán esto depende de como esta “armada” ya que en HTML el tab de los controles es en orden que están en el HTML (no del orden visible). Por eso que tenemos en ASP.NET la posibilidad de asignarlo este comportamiento tan natural a un Form (HtmlForm) o Panel. Incluso si estas familiarizado con los ValidationGroups de los controles esto viene a ayudarnos una vez mas a tener “porciones o grupos de formularios independientes” en nuestros webforms.
NOTA: Solo se admite para esta propiedad controles Button e ImageButton.
Como asignamos la propiedad DefaultButton de un Panel con el botón de un webcontrol Login?
Aquí tienes dos opciones para asignar esta propiedad por código y mediante el uso de identificador que renderiza en el cliente (sin código)
Si bien podemos convertir a template el webcontrol para manipular los controles internos de su template, el botón de login sigue dentro y no podemos obtener directamente su UniqueID (pero si lo podemos hacer por codigo)

Y vemos realmente el ID del botón:
Pero estas dos opciones que comentare no es necesario convertir a template el webcontrol.
OPCION 1: Por código asignamos el DefaultButton al panel (obteniendo el botón de login mediante FindControl)
Aquí la idea es por código obtener el UniqueID (identificador único jerárquico) del botón, si necesidad de convertirlo a template.
Si tenemos por ejemplo el siguiente webcontrol login dentro de un panel:
<asp:Panel ID="pnlLogin" runat="server">
Aqui otro boton de login<br/>
<asp:Login ID="Login1" runat="server"
onauthenticate="Login1_Authenticate"
onloggingin="Login1_LoggingIn">
</asp:Login>
</asp:Panel>
Y desde el codebehind asignamos la propiedad DefaultButton del Panel.
Para eso obtenemos el botón dentro del webcontrol Login (con FindControl) y lo asignamos el UniqueID del mismo como propiedad del DefaultButton del panel
protected void Page_Load(object sender, EventArgs e)
{
pnlLogin.DefaultButton = ((Button)Login1.FindControl("LoginButton")).UniqueID;
}
Vemos que es necesario el UniqueID y no el ClientID, textual de MSDN:
(…)Esta propiedad se diferencia de la propiedad de ID , en que la propiedad de UniqueID incluye el identificador del contenedor de nomenclatura del control de servidor. Este identificador se genera automáticamente cuando se procesa una solicitud de página.(…)

OPCION 2: Vía el nombre del webcontrol padre (en modo diseño, "Login1$LoginButton")
Aquí nos ayudamos de nombre del que obtiene el botón que se encuentra dentro del webcontrol login

Como vemos en la propiedad name se concatenan con $ los nombres de los controles padres hasta el control. Esto es la jerarquía del control (útil para controles con el mismo nombre)
No es necesaria toda la ruta solamente el control contenedor padre (ya que el panel se encuentra en la misma ubicación y como padre del webcontrol login) para asignar a la propiedad…
Es decir nos quedaría así:
DefaultButton="Login1$LoginButton"
<asp:Panel ID="pnlLogin" runat="server"
DefaultButton="Login1$LoginButton">
Aqui otro boton de login<br/>
<asp:Login ID="Login1" runat="server" ClientIDMode="Static"
onauthenticate="Login1_Authenticate"
onloggingin="Login1_LoggingIn">
</asp:Login>
</asp:Panel>
Y así ya estaría funcionando el DefaultButton.
NOTA (por si se confunde el id con el name): Para esta opción 2 utilizamos el NamingContainer que se renderiza en el name que justamente no influye el modo que lo utilicemos a la propiedad ClientIDMode porque aquí esta dentro de un control Login y siempre lo estará, pero lo único que cambia es el id del lado del cliente (por eso es la propiedad ClientIDMode)
O sea el id de “cliente” cambia pero no el name cuando la propiedad es Static por ejemplo

Otros enlaces que te pueden ayudar
Delicious tags :
asp.net,
aspnet
Esta es una articulo que se corresponde con uno anterior donde hable sobre las opciones de compartir session:
Particularmente en este articulo veremos la opción 1.1
OPCION 1: Cambiar el modo de estado de session (SessionState) a StateServer
OPCION 1.1: O cambiamos el ID de App (AppDomainAppId)
OPCION 1.2: O cambiamos el método que se obtiene el ID de Session (sessionIDManagerType)
Si bien hay varias opciones para compartir, la idea de hacerlo con el Servicio de estado de ASP.NET (StateServer) pero no tiene en modo nativo la forma de unificar el nombre de la aplicación y mas precisamente la propiedad AppDomainAppId que se utiliza dentro del servicio de estado para mantener el diccionario de variables de session por aplicación. Además mas abajo un ejemplo para descargar
Vamos paso por paso
Pasos que debemos cumplir para lograr el objetivo
- Todas las aplicaciones deben estar bajo el mismo dominio (para restricciones de cookie) IMPORTANTE!
En este caso mis pruebas fueron bajo localhost, pero funciona si cambas el dominio. Solo respetar que cada app esta bajo el mismo dominio
- Establecer el SessionState en modo StateServer
- Configurar el servicio de estado de session (Importante)
- Establecer la misma machinekey
Incluso para compartir entre diferentes servidores cuando estas en un cluster
Ejemplo: Puedes encontrar varios generadores online
- Establecer la misma nombre de la cookie
PAra poder compartir los datos deben ser ubicados (en la misma cookie de session). Acuerdense que es simplemente un identificador
- IMPORTANTE El mismo nombre de la app (mmmm!??) propiedad AppDomainAppId
Por lo que hice este post donde explicamos como modificarla
Paso 1: Todas las aplicaciones deben estar bajo el mismo dominio
Aclaraciones, bajo el mismo dominio, y están configuradas como directorios virtuales. (mas abajo el ejemplo para descargar)

Para crear una de estas app


Mas adelante veremos como se obtiene el nombre de la app AppDomainAppId (Paso 6)
Paso 2: Establecer el SessionState en modo StateServer
Aqui utilizamos la seccion del web.config sessionState para establecer donde se ubicara nuestra variables de session
<sessionState cookieName="YODADemoApp"
mode="StateServer"
stateConnectionString="tcpip=127.0.0.1:42424" />
Aqui el primer comentario es que no tiene una forma de setear el nombre de la aplicación, siendo que en su correspondiente funcionalidad en Windows Azure si tiene!
vamooosss que se puede modificar applicationName
http://msdn.microsoft.com/en-us/library/windowsazure/gg185682.aspx
PASO 3: Configurar el servicio de estado de sesión
Para que funcione al configuración del Paso 3 donde establecimos el modo StateServer y la ubicacion del servidor en la maquina local debemos habilitar ese servicio

Paso 4: Establecer la misma machinekey
Incluso para compartir entre diferentes servidores cuando estas en un cluster o en la misma maquina debes configurar la misma machinekey que se utiliza para:
“(…)Configura los algoritmos y las claves que se utilizan en el cifrado, descifrado y validación de los datos de autenticación de formularios y de los datos de estado de vista, así como para la identificación del estado de sesión fuera de proceso.(…)”
Ejemplo: Puedes encontrar varios generadores online
Paso 5: Establecer la misma nombre de la cookie
PAra que se guarde el valor de la session (un indentificdor) y que podamos obtenerlo a traves de multiples applicaciones en necesario que el nombre sea identico, por esta razon es conveniente modificarlo en la propiedad cookieName de sessionState
<sessionState cookieName="YODADemoApp"
mode="StateServer"
stateConnectionString="tcpip=127.0.0.1:42424" />
Paso 6: IMPORTANTE El mismo nombre de la app (mmmm!??)
Bueno hicimos todo lo anterior, pero cuando utilizamos StateServer para guardar los datos de session el proveedor utiliza el ApplicationName. El servicio es simplemente un ejecutable que tiene en cuenta el nombre de la aplicación

Como unificar el mismo nombre de ApplicationName para guardar el estado de session
Por aquí podemos leer que los proveedores de utilizan el nombre de la app para mantener el estado de session:
(…) Para mantener el ámbito de la sesión, los proveedores de estados de sesión guardan por separado la información de sesión de cada aplicación. De esta forma, varias aplicaciones ASP.NET pueden usar el mismo origen de datos sin entrar en conflicto en caso de que se encuentren identificadores de sesión duplicados.
Dado que los proveedores de almacén de estados de sesión guardan por separado la información de sesión de cada aplicación, deberá asegurarse de que el esquema de datos, las consultas y las actualizaciones incluyen el nombre de la aplicación. Por ejemplo, se podría utilizar el comando siguiente para recuperar los datos de sesión de una base de datos.(…)
Cual es el nombre de mis app en este ejemplo
Las app que configure bajo el WebSite0 (dos websites ASP.NET Webforms y un app con ASP.NET MVC)
El árbol en el IIS quedo así en el ejemplo:
| Para las app |
Los AppDomainAppId son respectivamente |
 |
WebSite0:
/LM/W3SVC/2/ROOT
WebSite1:
/LM/W3SVC/2/ROOT/WebSite1
WebSite2:
/LM/W3SVC/2/ROOT/WebSite2
MvcApplication1:
/LM/W3SVC/2/ROOT/MvcApplication1
|
De donde sale este nombre?
Podes listar las app con la herramienta de lista de comandos appcmd pero los AppDomainAppId no se encuentran visible
Comando: appcmd list app

Pero ni aquí encontré el AppDomainAppId

Pero si listamos la configuración por sitio podemos ver el ID del sitio (un numero), con appcmd:
En la configuración avanzada mediante el Administrador de IIS

Entonces… como se arma el nombre?
El primer sitio que esta detenido “Default Web Site” es el primero que se creo en mi IIS local es el sitio numero 1, el segundo que cree para esta demo es WebSite0 que como es el 2 y el el raiz tiene el nombre:
/LM/W3SVC/2/ROOT
Por eso los nombres: /LM/W3SVC/2/ROOT/{nombre de la app}
Cambiar el AppDomainAppId
Primero antes de cambiarlo como lo leemos desde nuestro código:
Por eso veras en que el ejemplo que lo colocamos al nombre en un webcontrol literal llamado litAppDomainAppId:
private void RefrescarControles()
{
litAppDomainAppId.Text = HttpRuntime.AppDomainAppId;
Literal1.Text = SessionHelper.Variable1;
Literal2.Text = SessionHelper.Variable2;
}
Como cambiar
Si vemos la propiedad es estática y de solo lectura, así que no nos sirve ya que no podremos modificarla
Aquí nos ayudamos Reflection para obtener la variable privada dentro de HttRuntime que contiene el valor que es expuesto a traves de la propiedad AppDomainAppId. Ya han publicados ejemplos aquí y aquí, pero bueno es Reflection al fin y al cabo
El ejemplo seria en una método estático que nos ayude a modificar el nombre:
public static class SessionStateServerSharedHelper
{
public static void ChangeAppDomainAppId(string name)
{
FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic);
if (runtimeInfo == null) return;
HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);
if (theRuntime == null) return;
FieldInfo appNameInfo = typeof(HttpRuntime).GetField("_appDomainAppId", BindingFlags.Instance | BindingFlags.NonPublic);
if (appNameInfo == null) return;
var appName = (String)appNameInfo.GetValue(theRuntime);
if (appName != "applicationName") appNameInfo.SetValue(theRuntime, name);
}
}
En el
global.asax nos ayudamos con el evento
Application_Start (que se ejecuta una sola vez al primera vez que se solicita un recurso a la aplicacion web ASP.NET)
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
SessionStateServerSharedHelper.ChangeAppDomainAppId("YODADemoApp");
}
Con esto logramos que nuestras aplicaciones donde aplicamos esto tengan el mismo nombre y asi poder compartir las variables de session cuando utilizamos ASP.NET State Server
NOTA 1: El único problema que podemos encontrar es cuando los chicos de IT están monitoreando nuestros sitios y necesitan conocer en que app causo el problema, pero bueno hablando con ellos podremos detectarla con al nombre y la url que dio el error
NOTA 2: Cambiar metadata del IIS para unificar directorios: Si bien hay una forma de cambiar la metada es mas bien para compartir entre sitios web (aqui la idea que es que se mantenga la metadata y a nivel de aplicacion
El ejemplo para descargar
Sencillo para observar los pasos comentados. Se puede mejorar encapsulando este comportamiento y no estar compartiendo archivos de clase (eso lo dejo para mejorar por el que necesita utilizarlo). Lo puedes descargar directamente aquí
| |
|
Tres aplicaciones bajo un solo dominio
2 Websites ASP.NET Webforms
1 Webapp ASP.NET MVC
Debes configurarlo en el IIS como aplicaciones
|
 |
Donde el negocio es simplemente modificar variables de session y ver los valores de todas las variables
Website1: Modifica la variable 1, Visualiza ambas variables
WebSite2: Modifica la variable 2
, Visualiza ambas variables
MvcApplication1: Visualiza ambas variables |
 |
| Todas tienen una clase que nos ayuda a modificar el nombre de AppDomainAppId mediante Reflection |
Clase SessionStateServerSharedHelper
Metodo de clase (estatico): ChangeAppDomainAppId |
Además nos ayudamos para tener intellisense en las variables de session con una clase.
Por ejemplo para obtner el valor de la Variable1 de session utilizo
public static string Variable1
{
get
{
if (HttpContext.Current.Session["Variable1"] != null)
{
return HttpContext.Current.Session["Variable1"].ToString();
}
return string.Empty;
}
set {
HttpContext.Current.Session["Variable1"] = value;
}
}
Una mejora es que el nombre de la AppDomainAppId compartida sea configurada por el AppSettings pero como se mencione mas arriba deberías reiniciar la app para que lo tome. Esto se puede solventar con la opcion 1.2 sessionIDManagerType que será para el próximo post
Espero que les sirva de ayuda o guía.
,
iis
Hay momentos y aplicaciones que a veces debemos necesitamos compartir sus variables de session entre otras app web (sea con Webforms o ASP.NET MVC)
Esta fue una pregunta en los foros de MSDN pero también de un equipo de desarrollo donde hace par de meses donde estaba facilitando una capacitación en ASP.NET, así que dejo plasmado aquí (la ayuda memoria) y de como seria la manera de realizarlo con la configuración del administrador de estado de sesión.
La idea es este post es presentar un listado de opciones que tenemos para compartir y obligarme (cuando tenga un tiempo) de ir publicando mas en detalle y tener ejemplos de cada una para que sea más útil.
A la pregunta:
"...¿Cual es la mejor manera de compartir variables de sesión entre aplicaciones virtuales?..."
Compartir? Estas seguro?
La idea de compartir sesión si bien se puede realizar hay que evaluar bien este tipo de “conexión entre app” es decir evaluar arquitectura de app que tenemos…
- Por que la naturaleza separada de estas app? están separadas en dominios de app diferentes y se quieren compartir datos?
- Podriamos implementar un login común, pero bueno... eso es otro tema. Si es necesario un SSO (single-sign-on)
- La arquitectura que se le da a las app web que estamos desplegando. Porque podríamos tener otras formas de compartir variables y que el usuario se siente libremente “paseando entre app web” (y porque no entre diferentes app en tu maquinas locales)
- Por algo se realizan app separadas, si es necesario compartir algo seria otro tipo de arquitectura o mediante componentes comunes
Pero no es tema de este post pero es bueno que te detengas a pensarlo aunque sea tomando una cerveza.
Pero el tema que me atañe comentar son las formas de formas que tenemos de compartir la session del usuario… asi que
Las opciones que tenemos…
Si lo que quieres es compartir la variables de session podemos
- OPCION 1: Cambiar el modo de estado de session (SessionState) a StateServer
Para realizar esto debemos utiliza: el mismo nombre de cookie, la misma configuración del machineKey en el web.config de la app y además el mismo nombre de aplicación (que?)
En esta ultima parte esta el problema, no es fácil cambiar el nombre de App (para el modo de StateServer, pero se puede) por default el AppName que da el IIS (que es algo asi LM/W3SVC/2/ROOT/WebSite1) o cambiamos la forma
<ayudaMemoria>{aquí va el enlace al post con mas detalle}</ayudaMemoria>
- OPCION 2: Cambiar el modo de estado de session (SessionState) a SQLServer
Utilizando el motor SQL Server ya sabemos para guardar el estado de session, esta opción es idem al anterior pero mas fácil ya que son procedimientos almacenados los que debemos modificar.
- OPCION 3: Cambiar el modo de estado de session (SessionState) a Custom,
Y utilizar algún componente de proveedor de estado de session que hayas armado que contemple poder compartir las variables
- OPCION 4: Utilizar una DB para compartir "elementos comunes"
Idem al punto 3, pero tendríamos que codifica la lógica sin ayudarnos con un proveedor de estado de session
- OPCION 5: Pasar variables de session mediante un mecanismo GET/POST
Idem a la técnica para a pasar variables de session desde ASP Clásico, PHP, etc)… muy artesanal esta opción y difícil de mantener
- OPCION 6: Utilizar Windows Server AppFabric Cache con un proveedor Custom de estado de session
Bueno si bien esta opción es como la 3, aquí la vedette es Windows Server AppFabric y el servicio de Cache Distribuido (ex nombre clave Velocity) que podamos instalarlo en varios servidores y administrar los objetos de cache agrupándolos por un simple nombre e incluso regiones (en vez de tener una sola “bolsa”), administración centralizada… la verdad una belleza.
NOTA 1: Creo que son estas 6 si hay alguna otra escriban mas abajo en comentarios así aumentamos conocimiento y estrategias
NOTA 2: Lo que no tengo es conocimiento sobre Azure en mi know-how, hasta que pueda tener mi codekata en algún proyecto
En este momento el post se hizo extenso así que lo particioné en estos temas para que sean mas cohesivos. Espero llegara escribir todos las opciones en artículos por separado o por lo menos tener enlaces para ayudar en cada caso
Necesito feedback al respecto, así que por favor sus comentarios son bienvenidos
- Cuando utilizarla? Cuando no utilizarla?
- Has necesitado esto alguna vez?
Espero que les sirva de ayuda o guía.
,
iis
Este problema es conocido desde la versión SP1 del VS2010, por lo menos a mi me toco vivenciar este problema en varias maquinas y esta documentado en los “known-issues” por si alguien necesita mas info.
En su momento fue porque instale una versión de NuGet antes de instalar SP1, algo así fue no recuerdo bien…me volvió a pasar en una maquina virtual de hace un tiempo así que dejo como instalar “manualmente” (copiando y pegando archivos).
Justo hoy quise actualizar una maquina virtual que la tenia para algunos proyectos, pero también sucedió con estas versiones (las anote por las dudas):
- 1.2.20325.9034 (un poco antigua) a la versión 1.6.21215.9133
- 1.5.20902.9026 a la versión 1.6.21215.9133
- Y hace unos minutos de la versión 1.6.30117.9648 a la versión 1.7.30402.9028
(aquí con solo descargar el instalador y ejecutarlo funcionó).
[Actualización el Lun/04/Jun/2012] Veo en Hotfix para VS2010 SP1: KB2581019 en el sitio la pagina de descarga de NuGet. Va como Solución 3 Si por las dudas tratas de instalar NuGet en un Visual Studio 2010 SP1
Y en el log tienes el siguiente mensaje de error:
VSIXInstaller.SignatureMismatchException: The signature on the update version of ‘NuGet Package Manager’ does not match the signature on the installed version. Therefore, Extension Manager cannot install the update.
Y la pantalla:
Solución 1: Desinstalar y volver a instalar
- Desinstalar NuGet
- Desde el VS2010. En Herramientas > Administrador de Extensiones
(NOTA: Si no te aparece este botón para desinstalar habilitado debes iniciar VS como Administrador)
- O por el Panel de Control
- Volver a instalarlo… (con la opción que mas te guste)
Solución 2: Instalar una actualización de NuGet “manualmente”
Como instalar el administrador de paquetes manualmente…
- Descarga la ultima versión de NuGet desde Visual Studio Gallery
(ejemplo la ultima versión al 24/04/2012 es la 1.7.30402.9028)
- Descomprime el archivo (las extensiones de .vsix son archivos comprimidos)
Cambias la extensión por .zip y descomprimes…
Algo ya nos había mencionado El Bruno:
- [#VS2010] HowTo: Instalar un paquete VSIX sin utilizar el instalador
- En la carpeta de instalación del administrador de paquetes de NuGet en Archivos de Programas
- En 32bits:
%ProgramFiles%\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft Corporation\NuGet Package Manager - Si estas en 64bits la carpeta es Archivos de Programas (x86)
%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft Corporation\NuGet Package Manager
- Se debe crear la carpeta para la versión a instalar y copiar todos los archivos que se descomprimieron.
En este ejemplo que descargue la versión 1.7.30402.9028 aqui la carpeta
- Y listo! ya esta disponible esta versión
Como sabemos cual esta utilizando VS2010?
Si vamos a Herramientas > Administrador de Extensiones podremos ver la versión de Administrador de Paquetes de NuGet
Solución 3: Un hoxfix salvador
[Actualización el Lun/04/Jun/2012] Veo en Hotfix para VS2010 SP1: KB2581019 en el sitio la pagina de descarga de NuGet. Va como Solución 3
Te instalas este Hotfix para VS2010 SP1
Enlaces
Si necesitamos tener un control checkbox en cada fila y que solo podamos seleccionar un sola fila a la vez (idem a un option)
Esto puede servir tanto para ASP.NET Webform como para MVC, con la salvedad de que depende de como renderizan las grillas de datos.
Gracias a una pregunta de los foros de MSDN de ASP.NET “Limitar selección Checkbox en GridView” tenia algo similiar en el borrador así que aquí va.
Si tenemos un Gridview con una columna template algo así:
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="Seleccion">
<ItemTemplate>
<asp:CheckBox ID="chkSeleccion" runat="server" CssClass="controlSeleccion" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Nombre" HeaderText="Nombre" />
<asp:BoundField DataField="FechaNacimiento" HeaderText="Fecha Nacimiento" />
</Columns>
</asp:GridView>
Cuando renderiza en ASP.NET 4.0 lo hace de esta manera
| La tabla HTML |
La celda con el control |
 |
 |
Solución con jQuery: Evento .click()
Podriamos tener un selector en jQuery que tome todos los checkbox que están dentro de nuestra clase “controlSeleccion” y cada vez que se realiza un evento click (con mouse o sin el directamente con el teclado igualmente dispara el evento)
<script type="text/javascript"> 1: $(function () { 2: $('.controlSeleccion input:checkbox').click(function () { 3: $('.controlSeleccion input:checkbox').removeAttr('checked'); 4: $(this).attr('checked', true); 5: });
6: });
</script>
NOTA: El selector :checkbox es identico a [type=checkbox]
Solución con jQuery: Atachando el evento con .on() (nuevo en jQuery 1.7)
En la version 1.7 de jQuery tenemos la sintaxis “.on()” para bindear a eventos (como lo eran live o bind, etc).
Nos quedaria algo asi para hacer lo anterior:
<script type="text/javascript"> 1: $(function () { 2: $('.controlSeleccion input:checkbox').on('click', function () { 3: $('.controlSeleccion input:checkbox').removeAttr('checked'); 4: $(this).attr('checked', true); 5: });
6: });
</script>
Para otros post:
Mas adelante escribiré como hacer que este checkbox se comporte como el “comando de selección del gridview”, para que nos quede todo “un poco mas elegante”, ya que en este caso particular el desarrollador deberá “iterar” para buscar que control esta seleccionado. Una mejora seria que este control este enlazado a algun campo booleano del la colección de objetos
Espero que les sirva de ayuda o guía.
Se acerca fin de año y por estos lugares, terminó un curso intensivo (y casi sin respiro) de estas tres tecnologías (WPF/WCF/Silverlight) en el marco de las Becas Control+F (@becascontrolf)
“Con el objetivo de fomentar más y mejor empleo en las áreas de software y tecnología a nivel nacional, el Ministerio de Trabajo, Empleo y Seguridad Social de la Nación, en conjunto con CESSI, y con el apoyo de empresas referentes del sector, ofrecen cursos de capacitación gratuita en todo el país.”
Esta vez se armaron en Corrientes, y los gestionó el Polo IT Corrientes a los cursos de Programación .NET Junior, .NET Senior, Administración en SQL Server y Genexus
Aquí los que rindieron el primer día, y los del segundo (a la derecha):

Algunos ya trabajaban con este tecnología o en .NET, pero la mayoría alumnos de entidades educativas de la región (UTN FRRe, UNNE, Institutos de IT) y por eso razón casi la mitad del curso quedo en el grupo de los rezagados que quedaron por el camino (por falta de tiempo)
Mas fotos
Similares que tuve la oportunidad de estar al frente:
Mas info
Trabajando con Federico necesitamos realizar consultas a la DB “dinámicamente” para rellenar reportes con Report Server embebido en nuestras aplicaciones web.
Para eso utilizamos Procedimientos Almacenados, y como queremos que sea lo mas “genérico posible”, obtenemos el nombre del reporte (ya sea por un campo de la DB o armándolo con alguna política de nombres teniendo en cuenta el del reporte).
Como sabemos la Cadena de Conexión de Entity Framework esta compuesta por la cadena, path de archivos del modelo y de asignación. Por ejemplo…
<connectionStrings>
<add name="AdventureWorksEntities"
connectionString="metadata=.\AdventureWorks.csdl|.\AdventureWorks.ssdl|.\AdventureWorks.msl;
provider=System.Data.SqlClient;provider connection string='Data Source=localhost;
Initial Catalog=AdventureWorks;Integrated Security=True;Connection Timeout=60;
multipleactiveresultsets=true'" providerName="System.Data.EntityClient" />
</connectionStrings>
Para armar nuestro Comando ADO.NET “puro y simple” (y genérico para llenar nuestros reportes) necesitamos la Conexión y para ello una cadena… y no queríamos:
- Tener una cadena por separado (la tentación y fuerte!)
- Parsear la cadena de conexión de EF (para obtener la parte que necesitamos)
Entonces solo basta una lectura rápida de las propiedades de la conexión en este caso nos valemos de la propiedad StoreConnection del EntityConnection
Textual de MSDN:
(…)Proporciona acceso a la conexión de origen de datos subyacente usada por el objeto EntityConnection.(…)
Podríamos entonces tener una propiedad estática que nos ayude:
public static string ConnectionString
{
get
{
using ( AdventureWorksEntities contexto = new AdventureWorksEntities())
{
string cadenaCon =
((System.Data.EntityClient.EntityConnection)contexto.Connection).StoreConnection.ConnectionString;
return cadenaCon;
}
}
}
Enlaces
Si alguien quiere utilizar en una vista con Razor el caracter “@” en alguna función javascript y/o html por ejemplo, algo simple…
var indiceArroba = email.indexOf("@");
El mensaje que nos proporciona:
""" is not valid at the start of a code block. Only identifiers, keywords, comments, "(" and "{" are valid.

O simplemente si quieres escribir en HTML un simple @

Con el mensaje:
A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following "@" with no space in between.
Solución: El caracter escape para el @ es el doble @@
Para poder seguir adelante el carácter escape para el @ es simplemente el doble arroba @@ 
var indiceArroba = email.indexOf("@@");
<span>O si quieres simplemente escribir un @@ simple.</span>
NOTA: No esta demás comentar también que podemos combinar HTML/Client Scripting con código Razor utilizando @: y <text>, mas info aquí
Enlaces
Si estas accediendo a un SQL SERVER 2008 y utilizando uno de sus nuevos tipos de datos como el Time veras que no es fácil leerlo con un SqlDataReader. La idea es obtener el valor del Time (SQL) que en .NET es un tipo de dato TimeSpan.
Si estas pensando hacer esto! olvídalo…
TimeSpan tiempo = (TimeSpan)reader["tiempo"];
Ir a las fuentes (ayuda a MSDN)
Investigando un poco di con método GetTimeSpan del SqlDataReader
Para los curiosos:
Si utilizas el tipo de dato Time(de SQL 2008) se mapea a un tipo de datos TimeSpan de .NET.
Si vemos el articulo Asignar tipos de datos de SQL Server (ADO.NET) vemos que para obtener este valor tenemos un método GetDateTime del SqlDataReader
Pero luego descubrí el GetTimeSpan
Obteniendo el valor de campo Time…
- Para obtener el valor del Time (SQL) que en .NET es un tipo de dato TimeSpan utilizamos el método GetTimeSpan del SqlDataReader, que recibe la posición de la columna, si es la columna 3, el índice es 2... ejemplo:
TimeSpan horaTimeSpan = reader.GetTimeSpan(2);
- Podemos mejorar agregando el método GetOrdinal para obtener el índice de la columna si tenemos el nombre
TimeSpan horaTimeSpan = reader.GetTimeSpan(reader.GetOrdinal("hora"));
hora.Text = horaTimeSpan.ToString("HH:mm");
NOTA 1: Como dice la ayuda en MSDN se debería antes verificar si IsDBNull la columna del reader o sino la conversión genera una excepción
NOTA 2: Dar formato al TimeSpan “para que quede legible por nosotros, los humanos” ;)
Espero que te sirva de ayuda o guía.
Enlaces

Es grato recibir buenas noticias por correo! Y mas si es un premio.
Dear Jose,
Congratulations! We’re pleased to inform you that your contributions to Microsoft online technical communities have been recognized with the Microsoft Community Contributor Award.
The Microsoft Community Contributor Award is reserved for participants who have made notable contributions in Microsoft online community forums such as TechNet, MSDN and Answers. The value of these resources is greatly enhanced by participants like you, who voluntarily contribute your time and energy to improve the online community experience for others.
Becoming a Microsoft Community Contributor Award recipient includes access to important benefits, such as complimentary resources to support you in your commitment to Microsoft online communities…
Thank you for your commitment to Microsoft online technical communities and congratulations again!
Uno día a día esta con el “cotidiano fuego de los proyectos” y se hace tiempo para ayudar y seguir aprendiendo en los foros… donde cada uno aporta un granito de arena, para que entre todos podamos crecer y evangelizar en la tecnología que nos apasiona (y nos da dolores de cabeza).
Asi que los invito a los foros de MSDN, TechNet y Answers, para compartir, preguntar, ayudar o simplemente “mirar pasivamente”.
Gracias a Microsoft por el reconocimiento… y también por el premio; ya que además de la “distinción” tenemos acceso a una librería online con muchos libros de Microsoft Press (E-Reference Library de Safari Book Online) así que no hay impedimento para seguir formándose…
Es grato ver en Geeks.ms que Javier Torrecilla y Josué Yeray Julián Ferreiro también tienen este honor… y es mas honor para mi compartir con ellos.

Por una pregunta sobre este tema “Enviar datos a una forma dentro de un iframe”, desempolve algo que tenia en borrador hace años…
ANTES NADA… Por qué lo tenia en borrador?
Pense que nadie lo necesitaba… Porque trabajar con iFrames en ASP.NET Webforms en “un dolor constante”, hay que hacer cosas con javascript para la comunicación… una tema fácil es incrustar desde ASP.NET contenido en iframes, y otra es enviar-recibir información. Todo esto porque la magia es ASP.NET es tener un solo formulario que casi casi no debes “tocarlo”
(NOTA: en ASP.NET MVC es una maravilla trabajar con javascript, tienes un mejor control del HTML resultante)
En ASP.NET Webforms tenemos varias opciones a los iFrames para “cargar contenido”
Pero volvamos al tema de enviar datos a una iFrame dentro de la misma pagina…
El problema
Al problema que en los foros de MSDN preguntaron tenia era como eje esta misma temática así que aquí va para tenerla de referencia.
“Enviar datos a una forma dentro de un iframe”, que como sabemos en ASP.NET tenemos un solo formulario en las paginas, en cada postback se realiza un post a la misma pagina salvo que cambiemos en un boton por ejemplo la propiedad PostbackUrl para modificar la pagina donde enviara los datos… pero no podemos definir el target (nombre del frame) de donde queremos que cargue la pagina destino.
Solución
Para que funcione necesitamos en ASP.NET webforms o en cualquier otro formulario web modificar el atributo target del formulario
Solución en ASP.NET Webforms
Como modificamos la pagina de destino para enviar los datos? con la propiedad PostbackUrl.
Como modificamos el target del formulario? en el cliente lo tenemos que hacer al momento de realizar el click… del boton. Nos ayudamos con OnClientClick
Bueno para muestra “basta un botón” así que el ejemplo para descargar 
La pagina padre
Default.aspx
Ingrese un Nombre: <asp:TextBox ID="txtNombre" runat="server" ClientIDMode="Static"></asp:TextBox>
<asp:Button ID="btnAceptar" runat="server" Text="Aceptar" OnClientClick="enviarDatosAFrame('iframePaginaHija');" PostBackUrl="~/PaginaIframeDestino.aspx" />
<iframe id="iframePaginaHija" name="iframePaginaHija" src="PaginaIframeDestino.aspx" width="100%" height="200px"></iframe>
<p> </p>
<asp:Label ID="lblHora" runat="server" Text="lblHora"></asp:Label>
Si podemos ver el botón que es un webcontrol Button que tiene dos atributos definidos
- OnClientClick: Que nos ayuda a llamar a una funcion del lado del cliente antes de enviar el submit
- PostbackUrl: Nos ayuda a definir una pagina de destino diferente a la actual cuando se realice el submit
Tenemos un script llamado “enviarDatosAFrame” que es una función del lado del cliente en javascript que nos ayuda a cambiar el atributo target del formulario de ASP.NET
<script type="text/javascript">
function enviarDatosAFrame(frameNombre) {
var formulario = document.getElementById('form1');
formulario.target = frameNombre;
setTimeout('formularioInicializar(\'' + formulario.id + '\');', 2000);
}
</script>
Mas adelante vemos que es el formularioInicializar.
NOTA: Para que nos sirva de esta manera que buscamos el formulario por Id (getElementById), debes tener definido el Id del formulario de ASP.NET (de tu pagina o MasterPage)
La pagina destino (el contenido del iframe)
PaginaIframeDestino.aspx (en el ejemplo)
Nombre: <asp:Label ID="lblNombre" runat="server" Text="lblNombre"></asp:Label>
<p> </p>
<asp:Label ID="lblHora" runat="server" Text="lblHora"></asp:Label>
Tengo definido en la pagina la directiva PreviousPageType para tener intellisense de lo voy a obtener de la pagina padre
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="PaginaIframeDestino.aspx.cs" Inherits="PaginaIframeDestino" %>
<%@ PreviousPageType VirtualPath="~/Default.aspx" %>
En el codebehind…
protected void Page_Load(object sender, EventArgs e)
{
if (PreviousPage != null)
{
//Sin el atributo de pagina PreviousPageType
//TextBox txt = (TextBox)PreviousPage.FindControl("txtNombre");
//string nombre = txt.Text;
//Con el atributo de pagina PreviousPageType,
//tenemos acceso a intellisense de PreviousPage (que agregamos una propieddad publica)
string nombre = PreviousPage.Nombre;
lblNombre.Text = nombre;
}
lblHora.Text = DateTime.Now.ToLongTimeString();
}
NOTA en la pagina padre (default.aspx) tengo una propiedad para recuperarla en la destino
public partial class _Default : System.Web.UI.Page
{
public string Nombre {
get {
return txtNombre.Text;
}
}
...
Colocar el atributo target directamente en el form? por si lo pensaste colocar aquí…
Hacer esto significaría que todo postback de esa pagina tendrá como target el definamos “estáticamente allí”… si lo hacemos con javascript a este cambio será dinámico y solamente hay que agregar al control que hace el sumbit la funcionalidad de que ejecute, previo submit, el cambio de target

Con JQuery: Agregar funcionalidad no intrusiva en botón
Con la ayuda de jQuery podemos armar una función que nos ayude a la tarea de modificar el target del formulario a partir de la configuración de un botón si necesidad de adicionar OnClientClick sino simplemente configurando el botón con unos atributos
En este ejemplo, para probar los atributos data de jQuery, se me ocurrió utilizar data-FrameDestino para definir el nombre del iframe donde quiero enviar los datos y como todavía no se como es el selector para “data”, utilice un selector de clase css, entonces todos los botones con la clase “postbackToFrame” tendrán esta funcionalidad
El botón queda así:
<asp:Button ID="btnAceptarConjQuery" runat="server" Text="Aceptar (con jQuery)"
PostBackUrl="~/PaginaIframeDestino.aspx"
CssClass="postbackToFrame"
data-frameDestino="iframePaginaHija" />
La función jQuery
<script type="text/javascript">
$(document).ready(function () {
$(".postbackToFrame").click(function () {
var form = $(this).parents("form:first");
var formNombre = $(form).attr("id");
var frameNombre = $(this).data("frameDestino");
if (frameNombre != null || frameNombre != 'undefined') {
$(form).attr('target', frameNombre);
}
setTimeout('formularioInicializar(\'' + formNombre + '\');', 2000);
});
})
</script>
Mas adelante vemos que es el formularioInicializar…
Modificamos el target del form, pero las siguientes acciones toman el mismo camino. Como inicializamos?
Cuando modificamos en el cliente el target del formulario el mismo permanece allí y cualquier otra acción que involucre un postback se dirigirá al target correspondiente. Si en la pagina tenemos mas botones, linkbutton o cualquier control que realice un submit tomara el valor del formulario que configuramos
Si llegamos a necesitar que el valor del target se inicialice debemos llamar a una función para tal motivo luego de un tiempo prudencial del submit, en mi caso 2000 milisegundos
Con jQuery
<script type="text/javascript">
function formularioInicializar(formularioNombre) {
$('#' + formularioNombre).attr('target', '');
}
</script>
O como la primer versión con javascript puro.
<script type="text/javascript">
function formularioInicializar(formularioNombre) {
var form = document.getElementById(formularioNombre);
formt.target ='';
}
</script>
NOTA: Seria bueno este inicializar formulario reciba el target tambien y podemos unificar todas las funciones que modifican este atributo
Ejemplo para descargar
Espero que les sirva de ayuda o guía.
Como en los foros de MSDN alguien necesitaba de un tema similar, lo tenia en borrador y encontré el “justificativo” de terminarlo. Ahora lo tengo para referencias de “opciones de capturar la salida HTML”, encubriendo para minificar el HTML resultante.
Que es Compactar (Minify) HTML?
La idea es limpiar el HTML resultante de caracteres que no son necesarios para renderizar la pagina (espacios en blanco innecesarios, salto de línea) esto se llama Minificar (Minification) Por que son innecesarios estos caracteres? Solo están para una mejor lectura para nosotros “los mortales desarrolladores” la maquina no los necesita.. y el HTML tampoco :)
Aquí utilizare de sinónimo Minificar = Minimizar = Compactar
Puedes ver con un ejemplo online de esta tarea:
Veamos a “vista de águila”…
| Antes | Después |
 |  |
Se utiliza generalmente para los Javascript, y los CSS donde existen muchas herramientas online, incluso para .NET el Microsoft Ajax Minifier.
Es necesario compactar la salida HTML?
“No colocar todos los huevos en la misma canasta/cesta”
Depende… utilizando herramientas como YSlow o Page Speed no darán información en que lugar “ganamos más” optimizando. Y no siempre es en la compactación del HTML, ya que con la configuración de la Compresión HTTP (gzip) en el IIS es suficiente.
Entonces que tenemos que compactar?
Lo que si es necesario es comprimir, compactar y agrupar (son tres técnicas que en conjunto logran reducir tiempos deF descarga) los CSS y JS porque allí si ganamos en cantidad de archivos a descargar, como así también los CSS Sprites pero bueno eso ya es otro tema
Todo el mundo ya conoce que tenemos herramientas para estas tareas como por ejemplo Microsoft Ajax Minifier o YUI Compressor for .NET (u otras similares) Algunas con todas las características que necesitamos otras con menos.
Primero lo primero: Como modificamos el HTML resultante?
Si conocemos el ciclo de vida de una pagina ASP.NET, lo que se nos viene a la cabeza como lugar para realizar la acción es el método Render, por lo que hay que sobrescribirlo.
La idea la tome por aquí para obtener el HTML resultante, que era para quitar el Viewstate, así que nos sirve para este ejemplo.
Ahora el objetivo: Minimizar el HTML
Lo primero que se nos viene la cabeza es reemplazar cadenas, y allí el “balón de oro” es el que tenga mejor performance para cadenas inmutables ;)
Muy bueno este articulo: Comparing RegEx.Replace, String.Replace and StringBuilder.Replace – Which has better performance?
Pero además, vamos a necesitar si o si expresiones regulares (esos textos que como siempre digo parece que un Vulcano los haya escrito) para detectar algunos patrones que nos son simples de buscar. Entonces para el ejemplo RegEx.Replace. (perdonen pero hay que sufrir con esto de expresiones regulares)
Objetivos secundarios de este post
Vamos a ver opciones para modificar el HTML resultante para minimizarlo, las complicaciones que podamos tener y porque no es uno de los pilares de la optimización pero aunque no lo utilicemos veremos tecnicas que en alguna oportunidad nos pueden ayudar para otras tareas de modificar la salida:
- Modificar la salida de una pagina HTML
- Seguir
sufriendo utilizando con expresiones regulares y como nos ayudan bastante (aunque no las entendamos luego de 1 hora) - Utilizar los Response.Filter (Filtros para la salida)
- Conocer y ver una implementación (de terceros) de PageParserFilter
Que opciones tenemos para minimizar el HTML
- Sobrescribimos el método Render de una pagina, y “limpiamos” con expresiones regulares
- Sobrescribimos el método Render y limpiamos con componentes como el YUI Compressor for .NET
- Utilizamos la formidable clase en C# para minimizar CSS realizada por Michael Ash que es la que utiliza el YUI Compressor for .NET
- Un poco mas “técnicos”… Utilizamos Response.Filter
- Vamos hasta “al infinito y mas allá”. Utilizamos PageParserFilter.
Aqui nos ayudamos con un componente que ya lo tiene implementado - [Idea a futuro] Componente del IIS para limpiar caracteres en blanco… (no lo tengo implementado, si alguien tiene un ejemplo bienvenido en los comentarios)
OPCION 1: Sobrescribimos el método Render de una pagina, limpiamos con expresiones regulares
Aquí sobrescribimos el evento Render para obtener la cadena de salida y tratarla
protected override void Render(HtmlTextWriter writer)
{
//Obtemos el HTML resultante dentro de un TextWriter
TextWriter tw = new StringWriter();
HtmlTextWriter htmlWriter = new HtmlTextWriter(tw);
//Renderizamos la pagina
base.Render(htmlWriter);
htmlWriter.Close();
//Obtenemos la cadena del HTML
string htmlResultante = tw.ToString();
//Limpiando
string htmlMin = YODA.Web.HTMLUtil.MinificarHTML(htmlResultante);
//Escribiendo la salida
writer.Write(htmlMin);
}
Haciendo uso de expresiones regulares simples podremos Minificar el HTML, solo para algunos patrones (espacios entre tags, saltos de linea, tabs, espacios en blanco mayores que 2
namespace YODA.Web
{
public class HTMLUtil
{
public HTMLUtil()
{
//
// TODO: Add constructor logic here
//
}
public static string MinificarHTML(string htmlCadena)
{
Regex regEspaciosEntreTags = new Regex(@">(?! )\s+", RegexOptions.Singleline);
Regex regSaltoDeLinea = new Regex(@"([\n\s])+?(?<= {2,})<", RegexOptions.Singleline);
Regex regSaltoDeLinea2 = new Regex(@"\r\n", RegexOptions.Singleline);
Regex regTabs = new Regex(@"\t", RegexOptions.Singleline);
Regex regEspaciosEnBlanco = new Regex(@"\s{2,}", RegexOptions.Singleline);
htmlCadena = regEspaciosEntreTags.Replace(htmlCadena, ">");
htmlCadena = regSaltoDeLinea.Replace(htmlCadena, "<");
htmlCadena = regSaltoDeLinea2.Replace(htmlCadena, string.Empty);
htmlCadena = regTabs.Replace(htmlCadena, string.Empty);
htmlCadena = regEspaciosEnBlanco.Replace(htmlCadena, string.Empty);
return htmlCadena.ToString();
}
}
}
Puedes buscar en regexlib.com algunos ejemplos. En el ejemplo para descargar dejo varias implementaciones.
NOTA IMPORTANTE: Este código no es del todo óptimo, pero sirve como ejemplo. Ya que espacios de variables en javascript var vble1 = “ valor 1”; lo optimiza por los espacios; Los saltos de linea no tiene en cuenta si lo que viene es una funcion js, y asi sucesivamente. Vuelvo a insistir, antes que el lector se acuerde de mi familia, que esto es un ejemplo de como modificar
OPCION 2: Sobrescribimos el método Render y limpiamos con componentes como el YUI Compressor for .NET

NOTA Opción 2: Aquí utilice el componente pero dentro del componente la clase que hace la magia de minimizar es un código de autoría de Michael Ash
Asi que lo dejo también dentro de ejemplo a descargar en la carpeta App_Code
NOTA: No podremos utilizar Microsoft Ajax Minifier solo tenemos dos métodos que nos nos ayudan a minimizar JS y CSS incluso de obuscar para minimizar el nombre de variables

Opcion 1.1, 2.1 (Tip): Heredar comportamiento (sobreescribir el Render en un solo lugar)
Ambas Opciones (1, 2) que sobrescriben el Render pueden estar en una Pagina Base para luego heredarla o en una Master base, como el ejemplo
namespace YODA.Web
{
public class MasterPageBase : System.Web.UI.MasterPage
{
public MasterPageBase()
{
//
// TODO: Add constructor logic here
//
}
protected override void Render(HtmlTextWriter writer)
{
//Obtemos el HTML resultante dentro de un TextWriter
TextWriter tw = new StringWriter();
HtmlTextWriter htmlWriter = new HtmlTextWriter(tw);
//Renderizamos la pagina
base.Render(htmlWriter);
htmlWriter.Close();
//Obtenemos la cadena del HTML
string htmlResultante = tw.ToString();
//Limpiando
string htmlMin = YODA.Web.HTMLUtil.MinificarHTML(htmlResultante);
//Escribiendo salida
writer.Write(htmlMin);
}
}
}
hay que hacer que la Master de nuestro sitio herede de la MasterPageBase que creamos
public partial class SiteWithOverrrideRender : YODA.Web.MasterPageBase
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
Aqui debemos crear un filtro, que es una clase que hereda en este ejemplo de MemoryStream y realice las acciones correspondientes
“(…)Cuando se crea un objeto Stream y se establece la propiedad Filter para el objeto Stream, toda la salida HTTP enviada por Write pasa por el filtro.(…)”
namespace YODA.Web
{
public class MinificarHTMLFiltro : MemoryStream
{
public Stream HtmlStream { get; set; }
public MinificarHTMLFiltro(Stream htmlStream)
{
this.HtmlStream = htmlStream;
}
public override void Write(byte[] buffer, int offset, int count)
{
string contenidoEnElBuffer = UTF8Encoding.UTF8.GetString(buffer);
//Acciones a realizar
//Accion 1: Minificar salida limpinado tabs/espacios en blanco
contenidoEnElBuffer = YODA.Web.HTMLUtil.MinificarHTML3(contenidoEnElBuffer);
HtmlStream.Write(UTF8Encoding.UTF8.GetBytes(contenidoEnElBuffer), offset, UTF8Encoding.UTF8.GetByteCount(contenidoEnElBuffer));
}
}
}
Y lo utilizamos en el Load de la pagina, simple no?
protected void Page_Load(object sender, EventArgs e)
{
Response.Filter = new MinificarHTMLFiltro(Response.Filter);
}
Como ya lo dije, aquí es utilizando PageParserFilter, pero no comente que es un poco difícil (y doloroso) crear una clase que herede de PageParserFilter y haga lo que necesitamos, ya que deben construir todos los controles del árbol de controles de una pagina…
Gracias a enlace que compartió Jason Ulloa en los foros de MSDN, pude llegar hasta aquí:
Que es una implementación de PageParserFilter justamente para el objetivo de limpiar los espacios en blanco. Y podremos utilizarlo en ASP.NET Webforms y en ASP.NET MVC
Como lo implementamos, configuramos?
- Creamos nuestro PageParserFilter (o utilizamos un componente)
- Registramos en la seccion pages del web.config
<system.web>
<pages pageParserFilterType="YODA.Web.MinificarHTMLPageParserFilter">
</pages>
...
En
el ejemplo Omari Нa que lo puedes descargar
<system.web>
<pages pageParserFilterType="Omari.Web.UI.WhiteSpaceCleaner, WhiteSpaceCleanerForWebFormsAndMVC2">
</pages>
...
OPCION 5: [Idea a futuro] Componente del IIS para limpiar caracteres en blanco…minimizar
Mejorar… siempre! Para los mas experimentados… Comprimir todo en el IIS. CREAR UN COMPONENTE
Podríamos armar un componente para el IIS para que comprima el HTML resultante de cualquier pagina… ya existe? por favor háganmelo saber a través de los comentarios
Ganamos algo? Algunos inconvenientes
Comente mas arriba que hay otros lugares en nuestra app web para empezar a optimizar, porque no siempre ganamos haciendo esto de compactar la salida HTML
Problemas que podemos tener:
- Como la minificacion es automática, el documento HTML mal formado puede ocasionar una salida “no bien formada” (con errores para el renderizado en el navegador) (incluso eliminando porciones de contenido)
Por qué puede estar mal formado? porque somos nosotros simples mortales lo que escribimos parte del mismo. Todavía no es todo “autogenerado”, todavía ;)
Es decir: Si alguien se le fue “los dedos” y escribió <br> /> y al limpiar entre tags no sabremos que puede pasar.
Mas arriba comente algo sobre por ejemplo limpiar bien los saltos de linea que esta demas (y el problema de javascript)
- Hay que tener en cuenta el tiempo que involucra tener esta funcionalidad y realizarla por cada pagina, en cada peticion
Con las otras tecnicas se puede mejorar.
Tips/Recomendaciones
Esto de compactar HTML ( o CSS y JS) tiene que ir de la mano de otras técnicas para optimiza porque no es tampoco nada recomendable estar haciendo esto siempre, ya que es un punto que hay procesamiento. Por este motivo podemos ayudarnos con:
- Comprimir el contenido que se envía al cliente (típicamente con Gzip) ya la mayoría de los “mortales” utilizamos navegadores modernos que soportan recibir este tipo de contenido.
- Cachear un poco el contenido en el servidor y en el cliente cuando se pueda,
Algunos tips a tener en cuenta para optimizar aplicaciones web pueden verlo aquí:
Dejo otros enlaces mas abajo.
Síntesis
Se aprendió además de la idea de minificar el HTML, varias opciones de como modificar la salida HTML de nuestros proyectos con ASP.NET Webforms.
Ejemplo para descargar
Enlaces
- Optimizando sitio web
- PagePageFilter
Hoy me toco modificar un template de un proceso que tenemos en nuestro TFS, siempre lo modificamos online (agregar nuevos campos) pero hoy con Diego decidimos que es hora de hacerlo en un template nuevo, porque que la tarea ardua de modificar sobre el proyecto “en caliente” no era bueno repetirlo. Además el proceso ya se estandarizó en nuestro sistema de calidad.
Antes que nada… “las fuentes”!
Si quieres hablar, rezar, pensar o realizar alguna acción sobre TFS y tienes a la lengua castellana como “natural” es imposible no caer en algunos de los 1000 post de El Bruno que tiene sobre TFS (y otros temas), como se diría por aquí “el que sabe… sabe”. Los invito a visitar su blog… Así que de ahí tomo de referencia para el presente articulo, también dejo enlaces al final.
Empezamos con lo general
Para modificar todo es tocar y retocar XML, pero por por suerte tenemos las Team Foundation Server Power Tools que tiene el Process Template Editor
para VS2008 tienes TFS 2008: Power Tools (Oct/2008)
Cuando instalas tienes en Herramientas el Process Template Editor y puedes editar un work item directamente desde el servidor

O puedes abrir un Template de proceso…

Descargar/Publicar Template de Procesos
Aqui el primer paso su queremos armar un nuevo template modificando uno actual
En el menu del VS que es Team > Team Foundation Server Settings > Process Template Manager…

…o también desde el menú contextual arriba del nombre del servidor en el Explorador

Y desde alli podremos ralizar el Download de un proceso y su correspondiente Upload

Y como seguimos? Modificando los workitems necesarios…
Mas adelante publico otro ejemplo de modificar un Workitem (el que tuvimos que agregar), pero tienes este HowTo de elBruno [TFS2008] HowTo: Agregar un campo personalizado a la definición de un WorkItem
Espero que les sirva.
Enlaces
Una vez mas tuve la grata tarea de estar al frente de un curso de Control+F, al igual que el año pasado.
Terminamos a finales de Noviembre las 160 horas de formación de .NET Junior en el marco de las Becas Control+F
, en los laboratorios de la UTN FRRE
Aquí los Padawan…

El curso
En esta versión del curso de 160hs menos horas que el curso anterior del año pasado, esta vez en UTN FRRE
tenia dos cursos, unos de ellos los daba Hernán Zini (que este año no blogueo fotos todavía)
Temas:
Las Becas Control+F?
Que son las becas? es una iniciativa de capacitación en tecnologías informáticas organizada por el Ministerio de Trabajo de la Nación (Argentina)
en conjunto con CESSI
y empresas de primera línea del sector informático
Un poco mas de fotos…



… por si no se dan cuenta nos vimos desde Agosto hasta Noviembre… desde pulóveres hasta remeras 
Mas info
Más artículos
Página siguiente >