Categories
.NET ASP.NET Desarrollo de Software Miniposts Windows Forms

Screencast: Integración continua

Interesante screencast sobre Integración Continua realizado Scott Hanselman y Jay Flowers en DotNetRocks TV, hacen uso de CI Factory para explicar este tema.

Categories
.NET Expresiones Regulares

Expresiones regulares más entendibles en .NET

Seguramente más de uno que ha tenido la oportunidad de revisar código ajeno donde se hacía uso de expresiones regulares, sufrió de algún modo para entender algunos patrones; esto sucede generalmente porque pocos suelen poner nombres a los grupos que definen y menos aún usan comentarios dentro de los patrones.

El siguiente ejemplo ilustra de manera básica un patrón confuso:

csharp:

using System;
using System.Text.RegularExpressions;

class Program
{
        static void Main(string[] args)
        {

                string patern = @"(https?://)([a-z\d-]+\.)*([a-z\d-]+)((\.[a-zA-Z]+){1,2})(/.*$)?";

                RegexOptions options = RegexOptions.IgnoreCase;

                Match match = Regex.Match("http://google.com", pattern, options);

                foreach (Group group in match.Groups)
                {
                        Console.WriteLine(group.Value);
                }           
        }
}

Ese mismo ejemplo, usando la opción RegexOptions.IgnorePatternWhitespace permite entender más fácilmente la expresión regular mostrada:

csharp:

using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main(string[] args)
    {

        string pattern = @"
               (?# Ejemplo de comentario: captura el protocolo http o https incluyendo ://)
               (?<protocol>https?://)
               (?<subDomain>[a-z\d-]+\.)*
               # Algún otro comentario relevante #
               (?<domain>[a-z\d-]+)
               (?<TLD>(\.[a-zA-Z]+){1,2})
               (?<requestUri>/.*$)?
           ";       
        RegexOptions options = RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase;

        Match match = Regex.Match("http://google.com", pattern, options);

        foreach (Group group in match.Groups)
        {
            Console.WriteLine(group.Value);
        }
    }
}

Como se puede apreciar, el segundo ejemplo da una idea más clara de lo que captura el patrón definido.

Si desarrollan en .NET y usan expresiones regulares, no hay excusas para hacer sufrir a los que revisan o dan mantenimiento al código que hicieron 😉

Categories
.NET ASP.NET Windows Forms

Exportar reportes a PDF desde código

A partir de Visual Studio 2005, se incluyeron nuevos controles (ReportViewer) y clases (LocalReport, ServerReport, etc) con los cuales se pueden diseñar reportes tanto para aplicaciones de escritorio como para aplicaciones Web.

Una característica útil de estas nuevas clases, al igual que los reportes diseñados en Crystal Reports, es que permiten automatizar el proceso de conversión de los reportes a diferentes a diferentes formatos: MS Excel, PDF e Imagenes (en formato TIFF).

Revisando documentación acerca de este conjunto de clases encontré un ejemplo publicado por Raj Kaimal que justamente exporta los reportes a formato PDF desde ASP.NET usando el método Render de la clase LocalReport:

csharp:

private void RenderReport()
{
    LocalReport localReport = new LocalReport();
    localReport.ReportPath = Server.MapPath("~/Productos.rdlc");

    ReportDataSource reportDataSource = new ReportDataSource("Ejemplo_Productos", GetData());
    localReport.DataSources.Add(reportDataSource);

    string reportType = "PDF";

    string mimeType;
    string encoding;
    string fileNameExtension;
   
    //The DeviceInfo settings should be changed based on the reportType
    //http://msdn2.microsoft.com/en-us/library/ms155397.aspx

    string deviceInfo =
    "<DeviceInfo>" +
    "  <OutputFormat>PDF</OutputFormat>" +
    "  <PageWidth>8.5in</PageWidth>" +
    "  <PageHeight>11in</PageHeight>" +
    "  <MarginTop>0.5in</MarginTop>" +
    "  <MarginLeft>1in</MarginLeft>" +
    "  <MarginRight>1in</MarginRight>" +
    "  <MarginBottom>0.5in</MarginBottom>" +
    "</DeviceInfo>";

    Warning[] warnings;
    string[] streams;
    byte[] renderedBytes;
   
    //Render the report
    renderedBytes = localReport.Render(
        reportType,
        deviceInfo,
        out mimeType,
        out encoding,
        out fileNameExtension,
        out streams,
        out warnings);
   
    Response.Clear();
    Response.ContentType = mimeType;
    Response.AddHeader("content-disposition", "attachment; filename=productos." + fileNameExtension);
    Response.BinaryWrite(renderedBytes);
    Response.End();
}

En la primera porción de código resaltada, reportType puede tomar los siguientes valores: PDF, Excel o Image; los valores para deviceInfo varían de acuerdo al formato que se use y están documentados en MSDN

Por si alguien está interesado, he preparado un pequeño ejemplo basado en el código mostrado.

Categories
.NET NHibernate PostgreSQL

Mapeo de secuencias de PostgreSQL en NHibernate

En PostgreSQL, para que una tabla tenga un campo autoincrementable se puede usar SERIAL o BIGSERIAL, éstos últimos en realidad no son tipos de datos reales puesto que internamente se crean secuencias para los campos que usan esta sintáxis, por ejemplo:

sql:

CREATE TABLE blog_category (
    id serial NOT NULL,
    name character varying,
    description character varying
);
code:

test=> \d blog_category
                                 Tabla «public.blog_category»
   Columna   |       Tipo        |                       Modificadores
-------------+-------------------+------------------------------------------------------------
 id          | integer           | not null default nextval('blog_category_id_seq'::regclass)
 name        | character varying |
 description | character varying |
 

Para representar representar esa tabla en NHibernate, hay que hacer algo adicional a lo que normalmente se hace con otro tipo de bases de datos:

xml:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Buayacorp.Domain.BlogCategory, Buayacorp.Domain" table="Blog_Category">

    <id name="Id" type="Int32" unsaved-value="0">
      <column name="Id" length="4" sql-type="int" unique="true"/>
      <generator class="native">
        <param name="sequence">blog_category_id_seq</param>
      </generator>
    </id>

    <property name="Name" type="String">
      <column name="Name" length="255" sql-type="varchar"/>
    </property>
    <property name="Description" type="String">
      <column name="Description" length="3000" sql-type="varchar"/>
    </property>
    <bag name="Blogs" inverse="true" lazy="true" cascade="all-delete-orphan">
      <key column="category_id"/>
      <one-to-many class="Buayacorp.Domain.Blog, Buayacorp.Domain"/>
    </bag>
  </class>
</hibernate-mapping>

En las líneas resaltadas se puede apreciar que para mapear el campo id es necesario especificar el nombre de la secuencia definida en la base de datos, esto es porque para generar los nuevos valores para ese campo se usa la función nextval, que justamente toma como parámetro ese nombre. Por ejemplo, para la siguiente porción de código:

csharp:

using System;
using System.Collections.Generic;
using System.Text;
using NHibernate.Cfg;
using NHibernate;
using Buayacorp.Domain;
using System.Xml;

namespace Buayacorp.Demo
{
    static class Program
    {
        static void Main()
        {
            Configuration cfg = new Configuration();
            cfg.AddAssembly("Buayacorp.Domain");
            ISessionFactory factory = cfg.BuildSessionFactory();

            ISession session = null;
            ITransaction transaction = null;
            try
            {
                session = factory.OpenSession();
                transaction = session.BeginTransaction();

                BlogCategory cat = new BlogCategory("Tecnología", "Blogs con información sobre lo último en tecnología...");
                session.Save(cat);

                Console.WriteLine(cat.Id); // cat.Id ya tiene un valor generado
               
                transaction.Rollback();
            }
            finally
            {
                if (session != null)
                    session.Close();
            }
        }
    }
}

Las consultas que se ejecutan en el servidor de base de datos son:

code:

2007-04-22 17:44:38 PET LOG:  sentencia: SELECT oid, typname FROM pg_type WHERE typname IN ('unknown', 'refcursor', 'char', 'bpchar', 'varchar', 'text', 'name', 'bytea', 'bit', 'bool', 'int2', 'int4', 'int8', 'oid', 'float4', 'float8', 'numeric', 'inet', 'money', 'date', 'time', 'timetz', 'timestamp', 'timestamptz', 'point', 'lseg', 'path', 'box', 'circle', 'polygon')
2007-04-22 17:44:38 PET LOG:  sentencia: BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

2007-04-22 17:44:38 PET LOG:  sentencia: select nextval ('blog_category_id_seq')

2007-04-22 17:44:38 PET LOG:  sentencia: ROLLBACK
2007-04-22 17:44:38 PET LOG:  sentencia: unlisten *

Para los curiosos, pueden descargar el archivo que contiene el código mostrado en esta entrada.

Categories
.NET AJAX ASP.NET CSRF Miniposts Seguridad XSS

Bug XSS en ASP.NET 2.0 y video sobre XSS, CSRF, Ajax Hacking

Para los interesados: