Categories
Mono Windows Forms

Relación entre las propiedades MinDate, MaxDate y Value del control DateTimePicker

Hoy mientras corregía detalles de una pequeña aplicación, me topé con un error que involucraba a las propiedades mencionadas en el título de la entrada. El código a grandes rasgos era el siguiente:

[csharp num=11 start=1]using System;
using System.Windows.Forms;

class App
{
public static void Main (string[] args)
{
DateTimePicker dtp = new DateTimePicker();

dtp.MinDate = new DateTime(2007, 10, 01);
dtp.MaxDate = DateTime.Now;

// Simular la acción del usuario
dtp.Value = DateTime.Now.AddSeconds(10);
}
}[/csharp]

Bien, las propiedades MinDate y MaxDate sirven para hacer que el valor de la propiedad Value esté en ese rango de fechas, lo interesante de ésto es visualmente también se aplica la restricción, es decir fechas fueras de ese rango están deshabilitdadas.

Retomando el punto, el problema en el código mostrado es que en la línea 11 se asigna la fecha y hora del momento en que se ejecuta esa línea, por lo que si un usuario intenta seleccionar la fecha actual segundos o minutos más tarde, se producirá una excepción del tipo ArgumentOutOfRangeException..

La solución para este problema es asignar un valor para MaxDate que esté lo más cercano posible al día siguiente:

csharp:

using System;
using System.Windows.Forms;

class App
{
        public static void Main (string[] args)
        {              
                DateTimePicker dtp = new DateTimePicker();
               
                dtp.MinDate = new DateTime(2007, 10, 01);
                dtp.MaxDate = DateTime.Today.AddDays(1).AddTicks(-1);

                // Simular la acción del usuario
                dtp.Value = DateTime.Now.AddSeconds(10);
        }
}

Como anécdota de esta entrada, mientras hacía pruebas con los ejemplos en Ubuntu*, extrañamente no pasaba lo mismo que con el .NET Framework de Microsoft. Luego de darle una mirada al código, me percaté que Mono no implementa correctamente esta parte, así que tuve que hacer el reporte correspondiente que pasado unas horas ya lo solucionaron. 😉

*: Mi disco duro pasó a mejor vida 🙁 e instalé ubuntu en otro disco que tenía por ahí, supongo que toda esta semana usaré Ubuntu al 100% -- hace tiempo que no usaba un entorno gráfico en Linux. 🙂

Categories
.NET MySQL

Generar backups de bases de datos MySQL desde .NET

En general prefiero realizar los backups desde scripts, pero muchas veces esta tarea se debe realizar desde la misma aplicación. Algo a tomar muy en cuenta si opta por este camino, es que los backups -- en lo posible -- deben ser generados con las herramientas que el motor de base de datos trae para este fin.

En los ejemplos se muestra el uso de mysqldump y la clase Process, que según MSDN proporciona acceso a procesos locales y remotos, y permite iniciar y detener procesos del sistema local:

csharp:

public void Backup(string  args)
{
        ProcessStartInfo psi = new ProcessStartInfo(ConfigurationManager.AppSettings["MysqlDump"], args);
        string filename = Path.Combine(workingDir, DateTime.Now.ToString(@"yyyy-MM-dd.\sql"));
       
        using (StreamWriter writer = new StreamWriter(filename, false, Encoding.UTF8))
        {
                using(Process process = new Process())
                {
                        psi.CreateNoWindow = true; // Evita que el proceso se inicie en una nueva ventana.
                        psi.UseShellExecute = false; // Evita que se use el shell del sistema operativo para iniciar el proceso.
                        psi.RedirectStandardOutput = true; // Escribir la salida en Process.StandarOuput
                        psi.StandardOutputEncoding = Encoding.UTF8; // Codificación de los datos de salida
       
                        process.StartInfo = psi;
                        process.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
                        {
                                writer.WriteLine(e.Data);
                        };
       
                        process.Start();
                        process.BeginOutputReadLine(); // Lectura asincrónica del stream de salida
                        process.WaitForExit(); // Esperar a que el proceso termine.
                }
        }
}

Aunque en el código mostrado es más sencillo realizar algún proceso sobre los datos generados, hay que tener muy en cuenta la codificación de los datos en los backups, puesto que por un descuido se puede generar fácilmente basura.

Si se quiere evitar estos problemas de codificación, se podría usar algo como:

csharp:

public void Backup(string args)
{
    string filename = Path.Combine(workingDir, DateTime.Now.ToString(@"yyyy-MM-dd.\sql"));
    ProcessStartInfo psi = new ProcessStartInfo("cmd.exe",
                                                string.Format("/c \"\"{0}\" {1} > \"{2}\"\"",
                                                              ConfigurationManager.AppSettings["MysqlDump"], args,
                                                              filename));

    psi.CreateNoWindow = true;
    psi.UseShellExecute = false;

    using (Process process = new Process())
    {
        process.StartInfo = psi;
        process.Start();
        process.WaitForExit();
    }
}

En fin, usar uno u otro método va a depender de los requerimientos de la aplicación.

Categories
.NET Miniposts PostgreSQL WordPress

Enlaces varios

Categories
ASP.NET

20 tips para mejorar el rendimiento de aplicaciones ASP.NET

Me tomé la libertad de hacer una traducción libre (con algunos comentarios míos agregados) de "20 Tips to Improve ASP.net Application Performance":

  1. Deshabilitar el estado de sesión: Si no se usa, entonces es mejor desactivarlo puesto que aparte de consumir recursos del servidor, generalmente también lo hace en el tráfico HTTP entre servidor y cliente al propagar el ID de la sesión. Esta opción se puede desactivar a nivel de la aplicación (elemento sessionState del web.config) o para páginas individuales (propiedad EnableSessionState).
  2. Activa el buffer de la página: Para reducir la comunicación entre cliente y servidor es mejor enviar los contenidos en bloque.

    When you run your ASP.NET application by using the ASP.NET process model, it is even more important to have buffering enabled. The ASP.NET worker process first sends responses to IIS in the form of response buffers. After the ISAPI filter is running, IIS receives the response buffers. These response buffers are 31 KB in size., After IIS receives the response buffers, it then sends that actual response back to the client. With buffering disabled, instead of using the entire 31-KB buffer, ASP.NET can only send a few characters to the buffer. This causes extra CPU processing in both ASP.NET as well as in IIS. This may also cause memory consumption in the IIS process to increase dramatically.

  3. Evitar la validación en el lado del servidor: En el artículo original el autor recomienda realizar la validación sólo en el cliente (javascript), pero en mi opinión, esto es un error, puesto que depender solamente de javascript para este tipo de cosas es como no hacer nada.
  4. Usar el control Repeater y evitar en lo posible el uso de los controles DataList, DataGrid y DataView: Los últimos no son recomendados porque generan mucho código HTML, pero en ASP.NET 2 esto se puede solventar usando los Control Adapters o usando otra forma para desarrollar aplicaciones ASP.NET.
  5. Usar HttpResponse.IsClientConnected: Verficar si el cliente todavía sigue conectado antes de realizar operaciones costosas.

    Consider using the HttpResponse.IsClientConnected property to verify if the client is still connected before processing a request and performing expensive server-side operations. However, this call may need to go out of process on IIS 5.0 and can be very expensive. If you use it, measure whether it actually benefits your scenario.

  6. Usar HTTPServerUtility.Transfer en lugar de Response.Redirect: El último envía las cabeceras necesarias (Location) al cliente y éste hace una nueva petición al servidor Web en base a esa cabecera.
  7. Usar siempre Page.IsValid cuando se trabaja con los controles de validación: Esto principalmente se debe a lo que comentaba en el punto 3, la única forma de asegurar que los datos cumplen con las reglas de validación definidas en esos controles -- de validación --, es verificando el valor de esa propiedad.
  8. Desplegar las aplicaciones en modo Release: Esto es para que se hagan las optimizaciones necesarias al momento de compilar la página y/o aplicación, además de otros
    Leer el siguiente importantes aspectos.
  9. Deshabilitar el seguimiento de página: No hay ninguna razón válida para que esta característica esté habilitada en una aplicación en producción.
  10. Page.IsPostBack es tu amigo: Hay acciones/código que puede evitarse cuando el cliente realiza una petición (envía la página a través del método POST).
  11. Minimizar el número de excepciones: En una aplicación y escenario ideal no habrá este tipo de problemas, pero como nosotros siempre nos equivocamos por el mismo hecho de ser humanos, tenemos que tomar medidas necesarias para reducir al mínimo posible el número de excepciones que genera una aplicación. Una lectura recomendada al respecto.
  12. Usar la cache: Poner en cache aquellos datos que no cambian mucho. Por otro lado, usar el cache de salida para páginas o partes de páginas que no cambian frecuentemente.
  13. Usar/crear cache por petición: Usar HttpContext.Items para mantener los datos que se necesitan en las diferentes etapas del proceso que sigue una página ASP.NET antes de llegar al cliente.
  14. Usar StringBuilder para operaciones intensivas con cadenas.
  15. Deshabilitar el ViewState: Toda la "magia" de los controles de servidor de ASP.NET dependen de esta característica: los datos se serializan en campos ocultos que se envían/reciben en cada petición. Así que si alguien quiere "venderles" ASP.NET con la idea de que hacer aplicaciones de escritorio es igual que hacer aplicaciones Web, ya saben cuáles son los inconvenientes.
  16. Usar paginación: Sólo recuperar los datos necesarios para la página que se visualiza, de preferencia esto se debe realizar desde la base de datos.
  17. Usar App_offline.htm para actualizar las aplicaciones.
  18. Usar ControlState en lugar del ViewState para los controles: En el artículo original se recomienda el uso del control de estado, pero hasta donde sé, da lo mismo usar uno u otro, porque el control de estado también tiene los mismos inconvenientes que el ViewState con la única diferencia que éste no puede deshabilitarse.
  19. Usar el bloque finally para asegurarse que todos los recursos son liberados adecuadamente, aunque también se puede usar la instrucción using para este caso.
  20. Usar Option Strict y Option Explicit, supongo que alguno que desarrolle en VB.NET puede justificar esta parte.

Si alguien está interesado en leer información más útil y específica para mejorar el rendimiento de las aplicaciones ASP.NET, puede darle una mirada a Improving ASP.NET Performance y Scalable Apps with Asynchronous Programming in ASP.NET.

Categories
.NET

Configurar opciones de envío de correo en .NET 2

Una forma para que las instancias de la clase SmtpClient se auto configuren es especificar sus valores en la sección mailSettings del archivo de configuración (funciona tanto en aplicaciones Web como de escritorio).

xml:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.net>
    <mailSettings>
      <smtp deliveryMethod="Network">
        <network
          host="smtp.dominio.com"
          port="25"
          userName="usuario@dominio.com"
          password="contraseña"
        />

      </smtp>
    </mailSettings>
  </system.net>
</configuration>

Las instancias de SmtpClient usan los valores definidos en esa sección si es que no se especifica nada al momento de crear el objeto:

csharp:

using System.Net.Mail;

class Program
{
    static void Main()
    {
        MailMessage mensaje = new MailMessage("usuario@dominio.com", "alex@dominio.com");
        mensaje.Subject = "Prueba";
        mensaje.Body = "Contenido";

        SmtpClient client = new SmtpClient();

        client.Send(mensaje);
    }
}

Una limitación de este método, es que no hay forma de especificarle a través del archivo de configuración, que use SSL para la autenticación y envío de los correos.