Como decíamos hace unos días, los generadores de código C# nos brindan la posibilidad de crear al vuelo código C# e incluirlo en nuestros proyectos en tiempo de compilación.
Por no alargar demasiado el post, vimos un sencillísimo ejemplo de implementación, pero ahora vamos a crear algo más complejo que podría ayudarnos a solucionar un problema que tendría difícil solución de no contar con esta característica del compilador.
1. Definición de objetivos
El reto al que vamos a enfrentarnos ya lo expusimos en el post anterior como un caso de uso simple de los generadores de código, así que vamos a reproducir la descripción del escenario.
Imaginemos que en nuestra aplicación tenemos clases que representan operadores matemáticos como SumOperator
, MultiplyOperator
, DivideOperator
, SubtractOperator
. Imaginad también que nos interesa tener un tipo enum Operators
donde aparezca un miembro por cada operador disponible, algo como:
public enum Operators
{
Sum,
Multiply,
Divide,
Subtract
}
El problema que tiene enfocar esto de forma manual es que resultaría sencillo implementar una nueva clase operador y olvidar crear su correspondiente entrada en la enumeración Operators
. Aquí es donde vienen al rescate los generadores de código :)
Lo que implementaremos hoy es un generador de código C# que creará la enumeración por nosotros en tiempo de compilación, manteniéndola sincronizada en todo momento con las clases que tengamos definidas en el proyecto. Para ello, crearemos un generador llamado OperatorsEnumGenerator
que:
- En la fase de análisis de código recopilará las clases del proyecto a compilar cuyo nombre finalice por
Operator
. - En la fase de generación de código creará el enum con los miembros registrados anteriormente.
¡Vamos allá!
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: .net, c#, generadores, metaprogramación
Seguramente muchos coincidiremos en que una de las novedades más interesantes de la última versión del compilador de C# es lo que oficialmente han denominado C# Source Generators, o generadores de código fuente de C#.
Muy resumidamente, esta característica añade un nuevo paso en la compilación en el cual los desarrolladores podemos introducir componentes propios (generadores) que inspeccionen el código de la aplicación que está siendo compilada y generen nuevos archivos, que a su vez pueden ser compilados e incluidos en los ensamblados resultantes. Su objetivo, tal y como se declara en su documento de diseño, es posibilitar la metaprogramación en tiempo de compilación.
Veámoslo con un ejemplo donde, además de explicarlo mejor, se puede mostrar su utilidad. Imaginad que en nuestra aplicación tenemos clases que representan operadores matemáticos como SumOperator
, MultiplyOperator
, DivideOperator
, SubtractOperator
, y todos ellos heredan de una clase base Operator
. Imaginad también que nos interesa tener un tipo enumerado enum Operators
donde aparezca un miembro por cada operador disponible, algo como:
public enum Operators
{
Sum,
Multiply,
Divide,
Subtract
}
Muy probablemente os habéis encontrado alguna vez con un escenario similar y habéis sufrido la dificultad de mantener sincronizada la enumeración con las clases que heredan de Operator
: cada vez que aparezca un operador nuevo e implementemos la clase operador que lo representa, tendremos que acordarnos de ir a Operators
y añadir el miembro.
Pues bien, aunque simple, esto sería un caso de uso bastante claro para los generadores de código fuente de C#. Gracias a ellos, podríamos crear un componente generador que examine nuestro código en busca de herederos de Operator
y genere al vuelo, siempre en tiempo de compilación, un archivo de código con la enumeración Operators
.
A todos los efectos, es como si esa enumeración la hubiéramos escrito a mano, porque podremos usarla con normalidad, aparecerá en intellisense, etc., pero la diferencia es que será generada cada vez que compilemos el proyecto, asegurando así que siempre será correcta y completa.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: .net, c#, generadores, metaprogramación, novedades
Ya hace tiempo que se lanzó .NET 5, pero seguro que algunos os habéis dado cuenta de que cuando desde Visual Studio creamos determinados tipos de proyecto, como bibliotecas de clases o proyectos de consola, por defecto éstos utilizan como target .NET Core 3.1 en lugar de .NET 5.
No se trata de un error; desde Microsoft justifican esta decisión porque .NET 5 no es una versión LTS, y prefieren que por defecto los proyectos sean creados usando una versión con mayor tiempo de soporte, como .NET Core 3.1.
Esto tiene fácil solución, porque tras crearlo simplemente deberíamos acceder a las propiedades del proyecto o editar el archivo .csproj
y modificar ajustar el target framework a nuestro antojo, pero, ¿cómo podríamos evitar tener que hacer esto cada vez?
try/catch
porque el mero hecho de usar este tipo de bloque de código afectaba al rendimiento.
La verdad es que de forma intuitiva se puede adivinar que esto no debería ser así. Si no se producen excepciones en un código que tengamos envuelto en un
try/catch
se debería ejecutar virtualmente a la misma velocidad que si no usáramos este tipo de bloque. Cosa distinta es si producen excepciones: ahí el rendimiento sí se verá seriamente penalizado, pero no es culpa del try/catch
sino del propio sistema de gestión de excepciones de .NET.Pero claro, lo dicho anteriormente es sólo cierto si somos capaces de demostrarlo, así que usaremos nuestro viejo conocido
BenchmarkDotnet
para desmontar este mito.Podríamos decir que NDepend es un clásico en el mundo de las herramientas que nos ayudan a mejorar la calidad de nuestro software. Creado por Patrick Smacchia en 2004, pasó a ser comercial en 2007, y desde entonces ha ido mejorando con el objetivo de permitirnos analizar nuestros proyectos desde una perspectiva que difícilmente podríamos conseguir con otras herramientas.
Ya le echamos un vistazo por aquí hace muchos años, y tenía interés por ver cómo había evolucionado el producto y cómo se había adaptado a los cambios en plataformas y tecnologías que se han producido desde entonces, para lo que Patrick me ha brindado amablemente una licencia del producto.
Pero no contento con eso, y sabiendo que tenemos afición a regalar productos para desarrolladores de vez en cuando, también ha cedido una licencia de desarrollador, valorada en 399 Euros para sortear entre los lectores del blog :)
Nota: es importante aclarar que lo que vais a leer a continuación no ha sido filtrado ni condicionado en ningún momento por el equipo de NDepend.
Venga, lo confieso: yo también he generado desde mis aplicaciones contenidos HTML y los he enviado al cliente en un archivo con extensión XLS, incluso modificando el content-type, para que pareciera un documento de hoja de cálculo. Durante años. Y también le he dicho a mis clientes que el molesto mensaje que aparece al abrirlo desde Excel, el que indica que el contenido del archivo no coincide con la extensión del mismo, es algo normal.
Pero esto se acabó desde que descubrí ClosedXML, un magnífico componente para .NET basado en el estándar OpenXML que permite la generación de archivos Excel “de verdad”, con formato, estilos, fórmulas, rangos, filtros, y casi todo lo que se nos pueda ocurrir.
Publicado por José M. Aguilar a las 9:15 a. m.
Etiquetas: .net, asp.net, closedxml, componentes, desarrollo
System.ComponentModel.DataAnnotations
:CreditCardAttribute
, que puede ser utilizado para validar números de tarjeta de crédito.EmailAddressAttribute
, que validará direcciones de correo electrónico.FileExtensionsAttribute
, para validar extensiones en nombres de archivo.PhoneAttribute
, que indica cuándo una propiedad debe contener un número de teléfono válido.UrlAttribute
, que comprobará si el contenido de una propiedad es una URL válida.CompareAttribute
, que antes estaba disponible en System.Web.Mvc y ha “ascendido” a anotación de datos general, permite validar la igualdad de dos propiedades.
También se ha incluido soporte para el atributo
MembershipPasswordAttribute
, recientemente incluido en System.Web.Security
, que comprueba si una contraseña cumple los requisitos establecidos para las mismas en el membership provider.Publicado en Variable not found.
En este post vamos a ver cómo realizar validaciones basadas en anotaciones de forma manual, lo cual puede tener su utilidad en gran número de escenarios.
.Net
- Mono in 2011
Miguel de Icaza
Asp.net
- How to combine a Worker Role with a MVC4 Web Role into a single instance
Liam Cavanagh - ASP.NET MVC + Selenium + IISExpress
Stephen Walther - Model Binding with Dropdown List in ASP.NET 4.5
Abhijit Jana - Knockout 2.0.0 released
Steve Sanderson - Free eBook: OWASP Top 10 for .NET developers
Troy Hunt - Solving Duplicate Content with Distinct URLs Issue in ASP.NET MVC
Imran Baloch's - New Request validation features in ASP.Net 4.5
K. G. Sreeju Nair - Using CORS to access ASP.NET services across domains
Dave Ward - HttpRequestBase vs HttpRequest
Eduard Tomás - Debugging Application_Start and Module Initialization with IIS and Visual Studio
Rick Strahl - Rotate an ASP.NET Image Control using HTML 5 Canvas
Suprotim Agarwal - Retoques a MVC y Razor para proyectos grandes–Una recomendación
Lucas Ontivero - High-Performance ASP.NET Caching
Peter Vogel - Microsoft blogging platform gains 33% performance boost after adopting RequestReduce
Matt Wrocks
Azure / Cloud
- Azure Monitor for Windows Phone 7 is out!
Ibon Landa - Libro gratuito sobre Windows Azure (Parte IV): Diseño y herramientas
CampusMVP - SQL Azure Q4 2011 Service Release
Ibon Landa
Data access
- MongoDB Best Practices
Inés Sombra - Inserciones masivas en SQL Server CE vs SQL Server vs MongoDb (y II)
Josué Yeray - Raven DB VI, La hora de las consultas…
Unai Zorrilla
Html/Css/Javascript
- JavaScript as a First Language
John Resig - The output element in HTML5
Richard Clark - Excssive, herramienta online para comprimir archivos CSS
Zach Will (Vía desarrolloweb.com) - Meet the CSS4 selectors
Catalin Rosu - JavaScript for C# developers: calling functions and the ‘this’ variable
Julian M. Bucknall - Top 10 “Must Follow” JavaScript Best Practices
Salman Siddiqui - Online Javascript Compression Tool
BrightBit - The rise and rise of JavaScript
Dan North - Using the JavaScript Prototype Property to Structure and Extend Code
Dan Wahlin's - Multiple Attribute Values
Chris Coyier - The Seven Deadly Sins Of JavaScript Implementation
Christian Heilmann (vía @alegrebandolero)
Visual Studio/Complementos
- C# + ReSharper = Awesome: Tip #5 – Replace Constructor with Factory Method
Alvin Ashcraft - ReSharper 6.1, dotCover 1.2 and dotTrace 4.5.2 Performance Released
Hadi Hariri - C# + ReSharper = Awesome: Tip #4 – Convert Abstract Class to Interface
Alvin Ashcraft - C# + ReSharper = Awesome: Tip #3 – Convert Into LINQ Expression
Alvin Ashcraft
Publicado en Variable not found
Aprovecho además para pediros opinión sobre un nuevo formato de presentación de los enlaces, usando categorizaciones. De esta forma podréis acceder directamente a aquellos cuya temática os interese, en lugar de tener que leerlos todos para ver si hay alguno que al que valga la pena echar el vistazo. ¿Qué os parece? ¿Mejor así?
.Net
- Formatting Enumeration Constants
BlackWasp - Install-Package Roslyn
Kirill Osenkov - Return Multiple Values from Methods with Tuples
Peter Vogel - C# Fundamentals: Returning Zero or One Item As IEnumerable<T>
James Michael Hare - .Net Reflector y ILSpy, ¿podrían inferir mejor el código a partir del IL?
Lucas Ontivero
Asp.net
- [Video] ASP.NET vNext - Filtrado de datos - Value Provider Attributes
Luis Ruiz - How to use SignalR and Knockout in an ASP.NET MVC 3 web application to handle real-time UX updates
Justin Schwartzenberger - MVC and HTML5 Web Workers
Dean hume - Introducing SignalR.EventStream
Ben Dornis - Update jqGrid Html helper for ASP.NET MVC
Robin van der Knaap - Implement secure ASP.NET MVC applications
Jovan Popovic - Which is the Fastest Webserver? Apache, IIS, Nginx, LightHttpd or G-Wan?
WebPerformance (vía @campusmvp) - OWASP Top 10 for .NET developers part 10: Unvalidated Redirects and Forwards
Troy Hunt - Espresso Tip: IHttpHandler and IsReusable
David Neal - Introducción a less
Sergio León - Automatically trim html controls in a asp.net mvc project
Richard Wilde - On deploying ASP.NET MVC site as a desktop application
Andrei Marukovich - Todo sobre AutoEventWireUp en páginas ASP.NET Web Forms
José Manuel Alarcón - Using QUnit with Razor Layouts
Phil Haack - ASP.NET MVC ViewModel usage and pick your best pattern
Kazi Manzur Rashid - MVC Route/URL Generation Unit Tester
Codeplex - Easy URL rewriting in ASP.NET 4.0 web forms
Jalpesh Vadgama - Looking into Web performance in ASP.Net applications
Nikolaos Kantzelis - [Vídeo] ASP.NET vNext - Selección de datos (SelectMethod)
Luis Ruiz - Using SignalR to broadcast a slide deck
Marteen Balliauw
Azure / Cloud
- Utilizar Windows Azure AppFabric Caching como session provider
Gisela Torres - VMware Cloud Foundry cloud platform now supports .Net
Mary-Joe Foley - Now Available: SQL Azure Q4 2011 Service Release
Azure Team (vía @ibonilm) - The SQL Azure Team Unveils a New Server Management UI
Roger Jennings - Differences Between the Storage Emulator and Windows Azure Storage Services
MSDN
Conceptos
- Algorithms Course Materials
Jeff Erickson - Hash Functions
Bret Mulvey
Data access
- RavenDB (IV) La identidad de los documentos
Unai Zorrilla - Data Access Performance Comparison in .NET
Luis Rocha
Html/Css/Javascript
- 10 Best jQuery Form Plugins
jQuery4u - Your jQuery: Now With 67% Less Suck
Scott Kosman - JavaScript – Add Commas To Number
Shai Raiten - Frontend SPOF
Steve Souders - The 30 Most Effective jQuery Plugins
Awcore - Animations in HTML5
Florian Rappl - HTML 5 Input Types - How useful is this really going to be?
Rick Strahl - The CSS profilers are coming!
Lennart Schoors - A Key Code Checker for DOM Keyboard Events
Rick Strahl
Visual Studio/Complementos
- [HowTo] Agregar Ficheros a un Proyecto Existente
Javier Torrecilla - C# + ReSharper = Awesome: Tip #2 – Create Field
Alvin Ashcraft - NuGet 1.6 Release Notes
Nuget Team - [How To] Agregar un elemento de menú a Visual Studio.
Javier Torrecilla - Go To Definition for JavaScript Functions in Visual Studio 2011
Abhijit Jana - Introducing CSSCop - FxCop for stylesheets
Mads Kristensen
Otros
- La fruta más alta siempre sabe mejor
José Manuel Alarcón
Publicado en Variable not found
A grandes rasgos, esta característica nos permite especificar valores por defecto para los parámetros de nuestros métodos, ahorrándonos tiempo de codificación:
class Program
{
public static void Main(string[] args)
{
Muestra(); // Imprime 1,1
Muestra(8); // Imprime 8,1
Muestra(3,4); // Imprime 3,4
Console.ReadKey();
}
static void Muestra(int a=1, int b=1)
{
Console.WriteLine(a + "," + b);
}
}
Desde siempre, ignorante de mí, había pensado que esto no era más que una triquiñuela del compilador, un azucarillo sintáctico destinado a facilitarnos la creación de sobrecargas de forma rápida, pero, ahora que lo he estudiado algo más a fondo, resulta que no es así. De hecho, los parámetros opcionales están soportados a nivel de plataforma, y funcionan de forma algo extraña (o al menos diferente a lo que podía esperarse), por lo que es conveniente conocerlos bien para no cometer determinados errores.
En primer lugar, me ha llamado la atención que la detección de la ausencia de parámetros en la llamada y la asignación de los valores por defecto no la realiza el método en el que se han definido. Es decir, sobre el ejemplo anterior, no es el método
Muestra()
el que comprueba si se han suministrado valores para los parámetros a
y b
, ni el que le asigna los valores por defecto en caso contrario. Esta "comprobación" se realiza en tiempo de compilación (!).Esto lo demostramos muy fácilmente si descompilamos esta misma aplicación con ayuda del imprescindible Reflector, que nos mostrará el siguiente código:
class Program
{
public static void Main(string[] args)
{
Muestra(1, 1);
Muestra(8, 1);
Muestra(3, 4);
Console.ReadKey();
}
public static void Muestra([Optional, DefaultParameterValue(1)] int a,
[Optional, DefaultParameterValue(1)] int b)
{
Console.WriteLine(a + ", " + b);
}
}
Como se puede observar, se ha generado un método
Muestra()
cuyos parámetros incluyen atributos que indican su opcionalidad y el valor por defecto en cada caso.Pero lo curioso está en el método
Main()
, desde donde se hacen las llamadas, el que podemos observar que las invocaciones a Muestra()
incluyen valores para todos los parámetros, como si se tratara de constantes especificadas directamente por el desarrollador.Por tanto, no hay nada mágico en los métodos con parámetros opcionales, ni sobrecargas, ni código de comprobación o asignación insertado de forma automática. Es el propio compilador el que, en el momento de generar el código IL, extrae los valores por defecto de los parámetros no especificados en la llamada examinando los atributos presentes en la signatura y los introduce en la invocación.
Y es aquí justo donde hay que tener cuidado al utilizar los parámetros opcionales. Dado que el valor de los parámetros se determina en tiempo de compilación y se incluyen como constantes en el código IL generado, pueden producirse efectos no deseados si trabajamos con distintos ensamblados.
Veámoslo con un ejemplo, que, aunque poco real, creo que ilustrará un escenario donde los parámetros opcionales podrían jugarnos una mala pasada.
En el siguiente código, perteneciente al ensamblado
LogicaNegocio.dll
, vemos un método CalculaImporteconIva()
, que retorna un importe aplicándole el impuesto (IVA) correspondiente:public class LogicaNegocio
{
public double CalculaImporteConIva(double importe, double iva = 0.16)
{
return importe + importe*iva;
}
}
Así, podemos tener un ensamblado externo, pongamos
ERP.exe
, que haga uso de este método de la siguiente forma:public void muestraDesglose(double importe)
{
double importeTotal = logica.CalculaImporteConIVA(importe)
// Mostrarlo...
}
En el momento de compilación de
ERP.exe
, la llamada anterior quedará en el ensamblado resultante igual que si hubiéramos hecho esta llamada: double importeTotal = logica.CalculaImporteConIVA(importe, 0.16)
Si ahora se produce una subida de IVA (como lamentablemente va a ocurrir en breve), acudiríamos a modificar el valor por defecto del parámetro
iva
en el método CalculaImporteConIva()
y recompilaríamos LogicaNegocio.dll
:public class LogicaNegocio
{
public double CalculaImporteConIva(double importe, double iva = 0.18)
{
return importe + importe*iva;
}
}
Sin embargo, si no recompilamos
ERP.EXE
desde éste seguiríamos enviándole el valor anterior (0.16, recordad que este valor aparece como constante en el ensamblado), lo que podía provocar algún problema. Es decir, si queremos mantener la coherencia del sistema, nos veríamos obligados a recompilar todos los ensamblados que referencien LogicaNegocio.dll
.Conclusión: en métodos públicos, y especialmente en aquellos que serán consumidos desde ensamblados externos, es conveniente utilizar parámetros opcionales sólo cuando los valores constantes sean “verdades universales”, como las constantes matemáticas o datos que con toda seguridad no van a cambiar. No es buena idea utilizarlos para reflejar valores variables o constantes de lógica de negocio, con posibilidades de cambio aunque sean remotas.
Por último, comentar que aunque este post está centrado en C#, todas estas precauciones son igualmente válidas para Visual Basic .NET.
Publicado en: Variable not found
Si trabajamos con un array, podemos consultar la propiedad
Length
; si se trata de una colección, podemos utilizar la propiedad Count
, que también nos devolverá el mismo dato de forma directa.Sin embargo, cuando procesamos información es frecuente tratar con tipos de datos enumerables cuya implementación exacta desconocemos, y en los que no tenemos acceso a ninguna propiedad que nos permita conocer el número de elementos exactos que tiene almacenados. Por ejemplo, esto ocurre cuando obtenemos un conjunto de datos en forma de
IEnumerable
o trabajamos con LINQ sobre alguna fuente de información.Pues bien, en estos casos hay que ser algo prudentes con la forma de consultar el número de elementos. Me he topado con código
Count()
, facilitado por LINQ para las enumeraciones y otras colecciones de datos, con objeto de saber si una lista estaba vacía:var prods = services.GetProductsByCategory(category);
if (prods.Count() > 0)
{
// Hacer algo con los elementos de la lista
}
else
{
// No hay elementos
}
Seguro que ya os habréis percatado de que eso está mal, muy mal. El método
Count()
recorrerá uno por uno los elementos para contarlos, retornando el número exacto de ellos. En escenarios de un gran número de elementos, o cuando es costoso obtener cada elemento (como al traerlos de una base de datos) puede suponer un consumo de recursos enorme.Y justo por esto existe el método
Any()
, que comprueba únicamente si existe al menos un elemento en la colección. Internamente este método itera sobre la colección retornando true
cuando encuentra el primer elemento, por lo que es mucho más eficiente que el anterior:var prods = services.GetProductsByCategory(category);
if (prods.Any()) // Mucho mejor :-)
{
// Hacer algo con los elementos de la lista
}
else
{
// No hay elementos
}
La utilización de
Any()
también es interesante para comprobar la existencia de elementos que cumplan una condición, expresada en forma de predicado; retornará true
cuando encuentre el primer elemento que lo haga cierto:if (pedidos.Any(p => p.Pendiente))
{
// Mostrar alerta, hay al menos un pedido pendiente
}Estamos en la puerta de un Estadio y queremos saber si vamos a ser los primeros en entrar al recinto… ¿le preguntaríamos al portero el número exacto de aficionados que ya están dentro para, si es cero, saber que somos los primeros? ¿O nos bastaría simplemente con preguntarle si hay alguien? ;-P
Publicado en: Variable not found
Hey: ¡estoy en Twitter!
Publicado por José M. Aguilar a las 12:01 a. m.
Etiquetas: .net, buenas prácticas, c#, desarrollo, linq, trucos, vb.net
Recientemente se ha publicado la tercera versión de NDepend, que ofrece interesantes novedades respecto a las anteriores, como la integración absoluta con Visual Studio, el soporte para soluciones multi-proyecto, potentes mecanismos de búsqueda, edición múltiple de CQL, o el seguimiento de cambios, además de las tradicionales características del producto.
Para habilitar esta característica es necesario instalar el plugin en el IDE, que se realiza desde el propio entorno visual de NDepend:
De esta forma, ya no es necesario acudir a la herramienta Visual NDepend para realizar búsquedas, comprobar reglas o navegar a través de la base de código: lo haremos directamente desde VS, utilizando los menús contextuales. Y gracias a ello, podemos disfrutar de las nuevas opciones de navegación, que nos permitirá surcar el código utilizando rutas distintas a las habituales:
Además, como comenta Patrick Smacchia, padre de la criatura, el rendimiento del entorno prácticamente no se resiente, dado que los análisis se ejecutan en segundo plano de forma incremental.
Recordar, por último, que NDepend es una aplicación comercial, pero dispone de una versión limitada gratuita utilizable por universidades, desarrolladores open source e incluso, durante un tiempo determinado, de prueba en proyectos comerciales.
Página del producto: http://www.ndepend.com/
Publicado en: Variable not found
Hey, ¡estoy en twitter!
Publicado por José M. Aguilar a las 11:49 p. m.
Etiquetas: .net, buenas prácticas, calidad, desarrollo, herramientas
Cada vez que tengo que forzar la validación de los datos de un formulario Webforms mediante javascript me veo obligado a preguntarle a Google, ese que todo lo sabe, cómo era el nombre de la función. Cosas de la edad, supongo ;-)
Así que, a modo de auto-recordatorio y con la intención de que pueda ser útil a alguien más, ahí va: la función se llama Page_ClientValidate()
. Retorna “true” si, una vez evaluados todos los validadores de la página, el valor de los campos es correcto (al menos en cliente; otra cosa son las comprobaciones en servidor, p.e., las definidas en un CustomValidator
).
Y como ejemplo de uso, puede valer el siguiente. Se trata de un botón de envío en un formulario donde se compone un correo electrónico:
1: ...
2: <asp:Button ID="btnEnviar" runat="server" Text="Enviar mail"
3: OnClick="btnEnviar_Click"
4: OnClientClick="return confirmar();"
5: />
6: ...
7:
8: <script type="text/javascript">
9: function confirmar() {
10: if (!Page_ClientValidate()) // Fuerza la validación en cliente
11: return false;
12:
13: return confirm('¿Seguro que desea realizar el envío?');
14: }
15: </script>
Como se puede observar, en el atributo OnClientClick
del botón incluye un script en el que se retorna el valor devuelto por la función confirmar
. Si el retorno es false, se cancela el Postback, evitando así que se invoque al evento btnEnviar_Click
que es el que realiza el envío del mail propiamente dicho.
En el cuerpo de la función confirmar()
, en primer lugar, validamos la página; si ésta no supera el proceso, los validadores habrán mostrado sus mensajes de error y retornamos falso, haciendo que se anule el postback. Si la validación es correcta, solicitamos al usuario que confirme la acción y retornamos su decisión.
Publicado en: Variable not found.
Publicado por José M. Aguilar a las 11:40 p. m.
Etiquetas: .net, asp.net, desarrollo, nivel básico, trucos, validadores, web
Encuentro en el blog de Gunnar Peipman un post sobre el nuevo método string.IsNullOrWhiteSpace, aparecido en .NET Framework 4.0 Beta 2, cuya misión es retornar un booleano indicando si la cadena pasada como parámetro contiene un nulo, está vacío, o exclusivamente caracteres de espaciado (espacios, saltos de línea, tabulaciones, etc.), un escenario bastante frecuente cuando estamos, por ejemplo, validando formularios o cualquier tipo de datos de entrada.
Por si no podemos esperar hasta la llegada de la nueva versión del framework, Gunnar propone una solución temporal basada en crear un método de extensión sobre la clase string
que realice la misma tarea:
public static class StringHelper
{
public static bool IsNullOrWhiteSpace(this string s)
{
if (s == null)
return true;
return (s.Trim() == string.Empty);
}
}
string a = null;
string b = " ";
string c = "\n";
string d = "Hey!";
Console.Write (a.IsNullOrWhiteSpace()); // True
Console.Write (b.IsNullOrWhiteSpace()); // True
Console.Write (c.IsNullOrWhiteSpace()); // True
Console.Write (d.IsNullOrWhiteSpace()); // False
Y para los fieles a Visual Basic .NET, ahí va el código equivalente:
Imports System.Runtime.CompilerServices
Public Module StringHelper
<Extension()> _
Public Function IsNullOrWhiteSpace(ByVal s As String) As Boolean
If s Is Nothing Then
Return True
End If
Return s.Trim() = String.Empty
End Function
End Module
Publicado en: Variable not found.