Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta bases de datos. Mostrar todas las entradas
Mostrando entradas con la etiqueta bases de datos. Mostrar todas las entradas
lunes, 21 de septiembre de 2009

A finales del agosto, James Gregory anunció la publicación de la versión 1.0 de Fluent NHibernate, una librería que ofrece una ágil alternativa a los espesos archivos de configuración de NHibernate.

Su API permite configurar desde el código de una aplicación, de forma fluida la mayoría de las veces, los mapeos entre la estructura de una base de datos relacional y el modelo de objetos que utiliza. Así, evitaremos la manipulación de grandes archivos XML, a la vez que podremos beneficiarnos de la validación en tiempo de compilación y, por supuesto, de posibilidades como la refactorización y el intellisense durante el desarrollo.

El siguiente código muestra el mapeo de la entidad Cat con sus propiedades, algunas de ellas con restricciones, y relaciones con otras entidades a uno (References) y a varios (HasMany); el nombre de la tabla y campos en el modelo relacional es el mismo que el de las propiedades, gracias a la convención sobre configuración, lo que permite simplificar código respecto a su equivalente XML:

public class CatMap : ClassMap<Cat>
{
  public CatMap()
  {
    Id(x => x.Id);
    Map(x => x.Name)
      .Length(16)
      .Not.Nullable();
    Map(x => x.Sex);
    References(x => x.Mate);
    HasMany(x => x.Kittens);
  }
}

Como podemos observar, el equivalente XML es mucho más verboso:

<?xml version="1.0" encoding="utf-8" ?>  
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"  
  namespace="QuickStart" assembly="QuickStart">  
 
  <class name="Cat" table="Cat">  
    <id name="Id">  
      <generator class="identity" />  
    </id>  
 
    <property name="Name">  
      <column name="Name" length="16" not-null="true" />  
    </property>  
    <property name="Sex" />  
    <many-to-one name="Mate" />  
    <bag name="Kittens">  
      <key column="mother_id" />  
        <one-to-many class="Cat" />  
      </bag>  
  </class>  
</hibernate-mapping>

Otra de las ventajas que aporta es el auto-mapping, que hace utilización intensiva del principio de convención sobre configuración para generar de forma automática mapeos de aquellas entidades que atiendan a unas normas preestablecidas (aunque modificables). El siguiente código muestra la forma tan sencilla de crear los mapeos de todas las entidades definidas en el espacio de nombres MiApp.Entidades, dentro del ensamblado donde se definió la entidad Producto:

var autoMappings = AutoMap.AssemblyOf<Producto>()
  .Where(t => t.Namespace == "MiApp.Entidades");

Además del mapeo objeto-relacional, el software abarca también la configuración del acceso a datos de NHibernate a través de su interfaz de programación. El siguiente código muestra la inicialización de la conexión a una base de datos SQL Server 2005, tomando la cadena de conexión del AppSettings y mapeando automáticamente las entidades que se encuentren definidas en un namespace concreto:
 
var sessionFactory = Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2005
      .ConnectionString(c => c.FromAppSetting("connectionString"))
    .Mappings(m => m.AutoMappings.Add(
      AutoMap.AssemblyOf<Producto>(type => type.Namspace.EndsWith("Entidades"))))
  .BuildSessionFactory();

Y por último, hay otra característica muy interesante vistas a la realización de pruebas unitarias sobre los mecanismos de persistencia. El código mostrado a continuación crea una instancia de la clase Empleado, la inserta en la base de datos, realiza una lectura de la entidad y la compara con la original, de forma automática:

[Test]
public void EmpleadoMapeaCorrectamente()
{
    new PersistenceSpecification<Empleado>(session)
        .CheckProperty(emp => emp.Id, 1)
        .CheckProperty(emp => emp.Nombre, "José")
        .CheckProperty(emp => emp.Apellidos, "Aguilar")
        .VerifyTheMappings();
}

Más información en la wiki del proyecto. Y las descargas, desde aquí.

Publicado en: Variable not found.

miércoles, 9 de septiembre de 2009

Logo de NHibernate Hace unas semanas, Oren Eini (o Ayende Raihen, como se le suele conocer) comunicaba la liberación de la versión 1.0 del proveedor de Linq para NHibernate, una característica altamente demandada por los usuarios desde la aparición del lenguaje de consulta integrado en .NET.

Aunque será incluido como parte del producto NHibernate en versiones venideras, han decidido liberar la actual release del proveedor como paquete independiente para que pueda comenzar a utilizarse desde este momento. Está siendo testado en multitud de aplicaciones en producción desde hace varios años, y al parecer el funcionamiento es más que correcto.

¿Y cómo puede ayudarte este proveedor, si eres usuario de NHibernate? El siguiente ejemplo, tomado de Caffeinated Coder muestra cómo una consulta a base de datos puede simplificarse y hacerse mucho más legible utilizando Linq, además de beneficiarse del tipado fuerte, intellisense y comprobaciones en tiempo de compilación:

Utilizando el API de NHibernate:

public IList<Call> GetCallsByDate(DateTime beginDate, int interpreterId)   
{   
    ICriteria criteria = Session.CreateCriteria(typeof(Call))
        .CreateAlias("Customer", "Customer")
        .Add(Restrictions.Gt("StartTime", beginDate))
        .Add(
            Restrictions.Or(
                Restrictions.Lt("EndTime", DateTime.Now), Restrictions.IsNull("EndTime"))
            )
        .Add(Restrictions.Eq("Interpreter.Id", interpreterId))
        .AddOrder(Order.Desc("StartTime"))
        .AddOrder(Order.Desc("Customer.Name")); 
        return criteria.List<Call>() as List<Call>;
}

Utilizando Linq:

public IList<Call> GetCallsByDateWithLinq(DateTime beginDate, int interpreterId)  
{   
    var query = from call in Session.Linq<Call>()
        where call.StartTime > beginDate
            && (call.EndTime == null || call.EndTime < DateTime.Now )   
            && call.Interpreter.Id == interpreterId
        orderby call.StartTime descending, call.Customer.Name
        select call;
 
    return query.ToList();
}

Podéis descargar tanto los binarios como el código fuente desde la página del proyecto en SourceForge.

Publicado en: Variable not found.

domingo, 2 de noviembre de 2008
Como muchos otros desarrolladores, soy un sufridor del chirriar entre los modelos relacionales y de objetos (desajuste de impedancias lo llaman otros ;-)). Y dado que principalmente me dedico a la construcción de software centrado en datos, estoy especialmente sensibilizado con el tema ;-).

Conozco sistemas ORM, y en especial NHibernate, que ayudan a aliviar en gran parte esta falta de concordancia, por lo que estaba deseando ver la gran apuesta de Microsoft en este ámbito, máxime después de juguetear con su hermano pequeño, y quizás ya difunto, Linq to SQL, y ver que se quedaba bastante corto en muchos aspectos.

El libro "ADO.NET Entity Framework. Aplicaciones y servicios centrados en datos" publicado por Krasis Press hace algo más de un mes, me ha parecido una lectura muy recomendable para acercarse a esta nueva tecnología de la mano de fenónemos como Unai Zorrilla, Octavio Hernández y Eduardo Quintás.

Comienza con una breve introducción en la que se explica la necesidad de tecnologías que ayuden a salvar la distancia entre el mundo relacional de las bases de datos y el mundo más conceptual, presentado como modelos de clases a nivel de diseño software, ofreciendo seguidamente una descripción general de este nuevo marco de trabajo. A partir de ahí, va profundizando en los distintos componentes de Entity Framework, siempre de forma muy práctica y basándose en ejemplos bastante cercanos a la realidad. Se recogen, entre otros, los siguientes contenidos:
  • Creación de modelos conceptuales, usando tanto los diseñadores de Visual Studio como modificando a mano los archivos XML que describen el modelo, útil para acceder a características no soportadas por el IDE.

  • Entity Client, el proveedor ADO.NET para acceso, aunque algo rudimentario, a datos del modelo

  • eSQL, el lenguaje de consulta de Entity Framework.

  • Implementación de consultas con ObjectQuery<T> y Linq to Entities.

  • Mecanismos de actualización de datos, incluyendo las técnicas de seguimiento de cambios en objetos, transaccionalidad y concurrencia, entre otros aspectos.

  • Ejemplos de uso de Entity Framework con WCF, enlaces a datos desde controles Windows Forms, WPF o ASP.NET.

  • Servicios de datos de ADO.NET, el mecanismo de publicación de modelos de Entity Framework a través de HTTP.

En definitiva, se trata en mi opinión de un libro muy claro y didáctico, recomendable para desarrolladores que quieran obtener un buen nivel de conocimiento de esta nueva tecnología partiendo desde cero, y para tener siempre a mano cuando comencemos a practicar y utilizar Entity Framework.

Desde la web de la editorial se puede descargar el índice y varias páginas de la introducción en PDF, lo que os permitirá echarle un vistazo, así como realizar el pedido online.

Publicado en: www.variablenotfound.com.
martes, 27 de noviembre de 2007
Aunque he visto que es conocido desde hace tiempo, yo me he enterado ahora de que en http://www.connectionstrings.com/ hay una excelente recopilación de cadenas de conexión a fuentes de datos de todo tipo:
  • Motores de bases de datos SQL Server, SQL Server 2005, SQL Server 2005 Compact Edition, Oracle, MySQL, Interbase, IBM DB2, Sybase, Informix, Ingres, Mimer SQL, Lightbase, Postgre SQL, Paradox, Firebird, AS/400 (iSeries), Pervasive, SQLBase, Progress y Caché.

  • Archivos de datos Excel 2007, Excel, texto plano, Access 2007, Access, Visual FoxPro / FoxPro 2.x DBF / FoxPro, SQLite y Filemaker.

  • Otras fuentes, como MS Project, Active Directory, Exchange, Lotus Notes, DSN y UDL.

Seguro que algún despistado (como el menda, todo sea dicho), todavía no lo conocía. Y es que no hay nada como internet para tener a mano toda la información que necesitamos.

Publicado en: Variable not found
domingo, 24 de junio de 2007
Hace unos días, visitando Caso Patológico, encontré un código javascript mediante el cual podía obtenerse de forma muy sencilla el contenido del portapapeles del visitante de una página, siempre que éste utilizara un navegador inseguro.

De hecho, si visitáis la página con IE6, podréis observar a la derecha un aviso en el que se advierte de la inseguridad a la que estáis expuestos al utilizarlo, mostrando además parte del contenido de vuestro portapapeles, es decir, lo último que habéis copiado (control+c), por ejemplo desde un editor de textos.

Si combinamos esta idea con la capacidad de ASP.NET AJAX, framework del que ya llevo publicados varios post, para invocar desde cliente funcionalidades en servidor, resulta un proyecto tan simple como interesante: crear una base de datos en servidor con el contenido del portapapeles de los visitantes de una página. Desde el punto de vista tecnológico, vamos a ver cómo podemos utilizar Ajax para comunicar un script cliente con un método de servidor escrito en C#; desde el punto de vista ético, lo que vamos a hacer pertenece un poco al lado oscuro de la fuerza: vamos a enviar al servidor (y éste lo va a almacenar) información importante, que puede llegar a ser muy sensible, sin que el usuario se percate de lo que está ocurriendo.

Para ello crearemos una página ASP.NET en la que introduciremos un código script que, una vez haya sido cargada, obtenga el contenido textual del portapapeles y lo envíe, utilizando un PageMethod, al servidor, quien finalmente introducirá este contenido y la dirección IP del visitante en una base de datos local. Como almacén vamos a usar SQL Express, pero podríamos portarlo fácilmemente a cualquier otro sistema.

Empezaremos desde el principio, como siempre. En primer lugar, recordar que es necesario haberse instalado las extensiones ASP.NET AJAX, descargables gratuitamente en esta dirección. Una vez realiza esta operación, podremos crear en Visual Studio 2005 un sitio web Ajax-Enabled, utilizando una de las plantillas que se habrán instalado en el entorno.

Después de esta operación, el entorno habrá creado por nosotros un sitio web con todas las referencias y retoques necesarios para poder utilizar AJAX. En particular, tendremos un Default.aspx cuyo único contenido es un control de servidor ScriptManager, del que ya hemos hablado en otras ocasiones.

Pues bien, acudiendo al código de la página (.aspx), introducimos ahora el siguiente script:


<script type="text/javascript">
$addHandler(window, "onload", salvaClipboard);
function salvaClipboard()
{
if (window.clipboardData)
{
var msg=window.clipboardData.getData('Text');
if (typeof(msg) != "undefined" &&
(msg != "") && (msg != null))
{
PageMethods.SaveClipboard(msg);
}
}
}
</script>

 

Nótese, en primer lugar, la forma en la que añadimos un handler al evento OnLoad de la ventana. El alias $addHandler, proporcionado por el framework Ajax en cliente, nos facilita la vinculación de funciones a eventos producidos sobre los elementos a los que tenemos acceso desde Javascript.

En segundo lugar, fijaos la forma tan sencilla de obtener el contenido del portapapeles de windows: window.clipboardData.getData('Text');. Los ifs previos y posteriores son simples comprobaciones para que el script no provoque errores en navegadores no compatibles con estas capacidades.

Por último, una vez tenemos en la variable msg el texto, lo enviamos vía un PageMethod al servidor, que lo recibirá en el método estático correspondiente, definido en el code-behind de la misma página (default.aspx.cs). Como hemos comentado en otras ocasiones, es el ScriptManager el que ha obrado el milagro de crear la clase PageMethods e introducir en ella tantos métodos como hayan sido definidos en el servidor y así facilitar su llamada de forma directa desde el cliente.

Vayamos ahora al lado servidor. En el code-behind sólo hemos tenido que incluir el siguiente código en el interior de la clase:


[WebMethod()]
public static void SaveClipboard(string texto)
{
string client =
HttpContext.Current.Request.UserHostAddress;
SqlConnection conn =
new SqlConnection(Settings.Default.ConnStr);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "Insert into Portapapeles "+
"(Ip, Clipboard) Values (@ip, @texto)";
cmd.Parameters.AddWithValue("ip", client);
cmd.Parameters.AddWithValue("texto", texto);
try
{
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
catch (Exception ex)
{
// Nada que hacer!
}
}

 

Simple, ¿eh? La llamada a PageMethod.SaveClipboard en el cliente invoca al método estático del mismo nombre existente en la página del servidor, siempre que éste haya sido adornado con el atributo System.Web.Services.WebMethod. El parámetro "texto", con el contenido del portapapeles del cliente, se recibe como string de forma directa y transparente, sin necesidad de hacer ninguna conversión ni operación extraña.

Una vez obtenida también la IP del visitante usando el HttpContext (hay que recordar que el método es estático y por tanto no tiene acceso a las propiedades de la propia página donde está definido), se establece la conexión con la base de datos y se almacena la información.

Y eso es todo, amigos. Como habéis podido comprobar, es realmente sencillo utilizar Ajax para enviar desde el cliente información al servidor utilizando scripting y el framework AJAX proporcionado por Microsoft. Si esto lo unimos a la capacidad de extraer información local del equipo del visitante en determinados navegadores, los resultados pueden ser espectaculares y realmente peligrosos. Afortunadamente, las nuevas generaciones de browsers (IE7 incluido) se toman la seguridad algo más en serio y hacen más difícil la explotación de este tipo de funciones.

Finalmente, como siempre, indicar que he dejado en Snapdrive el proyecto completo, base de datos incluida, para que podáis probarlo (AjaxClipboardSpy.zip). Ojo, para que todo funcione debéis cambiar la ruta del archivo .MDF de SQL Express sobre el archivo Settings.settings de la aplicación.
lunes, 30 de abril de 2007
Ya comentaba en otros posts (como este y este otro) qué eran los GUID (Globally Unique IDentifiers), y lo buenos que podían resultar como identificadores de elementos de forma única y universal.

Sin embargo, su utilidad se vería reducida en la práctica si su creación fuera compleja. Pero nada más lejos de la realidad, la obtención de un Guid es extremadamente sencilla:


// C#:
Guid g = Guid.NewGuid(); // Obtiene un nuevo GUID
Console.WriteLine(g); // Lo muestra por consola



' Visual Basic .Net:
Dim g as Guid = Guid.NewGuid() ' Obtiene un nuevo GUID
Console.WriteLine(g) ' Lo muestra por consola



Podemos observar que la clase (estructura) Guid (incluida en el namespace System) contiene un método estático que nos permite generar y obtener objetos de este tipo de forma directa. Además, recordemos que cada vez que generamos un GUID tenemos la seguridad casi absoluta de que será único, lo que hace que podamos utilizarlo directamente como identificador del elemento deseado.
miércoles, 4 de abril de 2007
Indiscutiblemente, los nulos son una de las peores pesadillas del desarrollador, y causa de una gran cantidad de errores en tiempo de ejecución de aplicaciones. La falta de herramientas para tratarlos apropiadamente hacen a veces compleja la convivencia con la oveja negra del rebaño de los valores posibles a aplicar a una variable.

Y es que con poco que hayáis desarrollado, seguro que alguna vez habéis encontrado la difícil tarea de asignar un valor nulo a una variable de tipo valor (int, float, char...) donde no encaja más que usando artimañas difíciles de realizar, trazar y documentar. Por ejemplo, dada una clase Persona con un propiedad de tipo entero llamada Edad, ¿qué ocurre si cargamos un objeto de dicha clase desde una base de datos si en ésta el campo no era obligatorio?

A priori, fácil: al leer de la base de datos comprobamos si es nulo, y en ese caso le asignamos a la propiedad de la clase el valor -1. Buen parche, sin duda.

Sin embargo, optar de forma general por esta idea presenta varios inconvenientes. En primer lugar, para ser fieles a la realidad, si quisiéramos almacenar de nuevo este objeto en la base de datos, habría que realizar el cambio inverso, es decir, comprobar si la edad es -1 y en ese caso guardar en el campo un nulo.

En segundo lugar, fijaos que estamos llevando a la clase artificios que no tienen sentido en el dominio del problema a resolver, en la entidad a la que representa. Mirándolo un poco desde lejos, ¿qué sentido tiene una edad negativa en una entidad Persona? Ninguno.

En tercer lugar, existe un problema de coherencia en las consultas. Si tengo en memoria una colección de personas (realizada, por ejemplo usando generics ;-)) y quiero conocer las que tienen edad definida, debería comprobar por cada elemento si su propiedad Edad vale -1; sin embargo, al realizar la misma consulta en la base de datos debería preguntar por el valor NULL sobre el campo correspondiente.

Cierto es que podríamos llevar también a la base de datos el concepto "-1 significa nulo" en el campo Edad, pero... ¿no estaríamos salpicando a la estructura de datos con una particularidad (y limitación) del lenguaje de programación utilizado? Otra idea bizarra podría ser introducir la edad en un string y problema solucionado: las cadenas, al ser un tipo referencia pueden contener nulos sin problemas, lo que pasa es que las ordenaciones saldrían regular ;-)

Por último, ¿y si en vez de la edad, donde claramente no pueden existir negativos se tratase de una clase CuentaCorriente y su propiedad SaldoActual? Aquí sí que se ve claramente que esto no es una solución válida.

Soluciones, aparte de las comentada, hay para todos los gustos. Se podría, por ejemplo, añadir una propiedad booleana paralela que indicara si el campo Edad es nulo (un trabajazo extra, sobre todo sin son varias las propiedades que pueden tener estos valores), o encapsular la edad dentro de una clase que incorporara la lógica de tratamiento de este nulo.

En cualquier caso, las soluciones posibles son trabajosas, a veces complejas, y sobre todo, demasiado artificiales para tratarse de algo tan cotidiano como es un simple campo nulo.

Conscientes de ello, los diseñadores de C# han tenido en cuenta en su versión 2.0 una interesante característica: los nullables types, o tipos anulables (traducción libre), un mecanismo que permite introducir el nulo en nuestras vidas de forma no traumática.

La siguiente línea generaba un error en compilación, que decía, y no le faltaba razón, que "no se puede convertir null en 'int' porque es un tipo de valor":
int s = null; 

Ahora, en C# 2.0, es posible hacer lo siguiente:

int? s;
s = null;
s = 1;

Obsérvese la interrogación junto al tipo, que es el indicativo de que la variable s es de tipo entero, pero que admite también un valor nulo.

Por dentro, esto funciona de la siguiente forma: int? es un alias del tipo genérico System.Nullable<int>. De hecho, podríamos usar indistintamente cualquiera de las dos formas de expresarlo. Internamente se crea una estructura con dos propiedades de sólo lectura: HasValue, que retorna si la variable en cuestión tiene valor, y Value, que contiene el valor en sí.

Se entiende que una variable con HasValue igual a false contiene el valor nulo, y si intentamos acceder al mismo a través de Value, se lanzará una excepción.

Sin embargo, la principal ventaja que tienen es que se utilizan igual que si fuera un tipo valor tradicional. Los tipos nullables se comportan prácticamente como ellos y ofrecen los mismos operadores, aunque hay que tener en cuenta sus particularidades, como se aprecia en el siguiente código:

int? a = 1;
int? b = 2;
int? intNulo = null;
bool? si = true;
bool? no = false;
bool? niSiNiNo = null;
Console.WriteLine(a + b); // 3
Console.WriteLine(a + intNulo); // Nada, es nulo
Console.WriteLine(a * intNulo); // Nada, es nulo
Console.WriteLine(si & no); // false
Console.WriteLine(si & no); // true
Console.WriteLine(si & niSiNiNo); // Nada, es nulo
Console.WriteLine(no & niSiNiNo); // false
Console.WriteLine(si | niSiNiNo); // true
Console.WriteLine(no | niSiNiNo); // Nada, es nulo

Curioso en los booleanos, en los que el valor nulo se puede interpretar como un quizás. De esta forma es fácil prever el resultado de una operación lógica: "Verdad ó Quizás" resuelve como verdadero, ó "Falso y Quizás" es falso, por ejemplo.
martes, 27 de marzo de 2007
En un post anterior comentaba la idoneidad de un GUID para identificar de forma única e inequívoca elementos como podían ser los registros de una tabla de una base de datos.

Tradicionalmente utilizo claves artificiales, más concretamente autonuméricos, para casi todo. Incluso a veces más de la cuenta, en esas ocasiones en las que es antinatural no usar claves naturales, valga la rebuscada frase. Sin embargo, la facilidad con la que se manejan estos identificadores, la maximización del rendimiento y espacio ocupado hacen que olvide cualquier otro criterio y me incline hacia esos números consecutivos que automáticamente el sistema genera para nosotros.

Sin embargo, cualquiera que haya usado autonuméricos para diseñar una aplicación medianamente compleja se habrá topado con una serie de inconvenientes como:

  • la imposibilidad para predecir su valor. En otras palabras, hay veces que debemos dividir las sentencias o interfaces de introducción de datos en tablas enlazadas en varios pasos, con objeto de obtener los autonuméricos asignados en algunas de ellas y poder establecerlos en las tablas que vinculan a éstas.
  • no son consecutivos, determinados acontecimientos como la cancelación de una transacción pueden hacer que aparezcan huecos en las asignaciones, lo que puede provocar el efecto pánico de borrado accidental.
  • son problemáticos a la hora de volcar información entre tablas o bases de datos distintas. Por ejemplo, para exportar una serie de tablas relacionadas mediante IDs de una base de datos a otra, suele ser necesario la creación de scripts o incluso aplicaciones relativamente complejas que traduzcan los identificadores de la base de datos origen a los asignados en la de destino, manteniendo las relaciones intactas. Labor de monos, vaya.
  • en el mismo escenario anterior, puede ocurrir que a la hora de realizar fusiones entre tablas un identificador concreto esté ocupado en ambas, lo que hace necesario de nuevo la creación de aplicaciones de volcado.

A estos casos, seguro que habituales, hay que añadir otros escenarios más complejos y menos cotidianos, como los relativos a bases de datos distribuidas, sincronizaciones o replicaciones.

Los GUID pueden solucionar en parte estos problemas, puesto que ofrecen las ventajas de la unicidad, ampliada más allá del alcance de la simple tabla, a la vez que permiten una manipulación más directa por parte del usuario, es decir:

  • los GUID son únicos de verdad, y de forma universal. Vamos, que no se van a repetir (recordemos que existen más de 1038 valores distintos) ni siquiera en tablas distintas, ni en bases de datos diferentes. Ideal para entornos distribuidos, mezclas de datos, volcados, etc.
  • pueden ser generados por aplicaciones, no es necesario esperar a crear un registro para obtener el GUID que será asignado a un registro; podemos generarlo de forma anticipada desde nuestra aplicación y utilizarlo a nuestro antojo.

Pero como casi todo en la vida, esto tiene su precio. Los principales inconvenientes son el mayor consumo de espacio, con la consiguiente merma del rendimiento en consultas y actualizaciones, dispersión de los valores creados (problemático en el uso de clústers o agrupaciones por valores) y, para mi gusto, casi lo peor de todo: la dificultad para depurar (¿quién ve práctico buscar en una tabla diciendo select * from clientes where id={BAE7DF4-DDF-3RG-5TY3E3RF456AS10} en vez de select * from clientes where id=17?).

domingo, 25 de marzo de 2007

El tipo de datos GUID existe, y es una opción a la hora de crear un campo, en sistemas gestores de bases de datos como SQL Server, pero, cosas de la ignorancia, nunca había pensado que pudieran ser una opción razonable para identificar una fila en una tabla.

Sin embargo, recientemente me he topado con una aplicación en la que, observando su base de datos, se utilizaban como identificadores únicos de registro valores de tipo GUID, lo cual me ha llamado la atención y me ha llevado a profundizar un poco en el tema.

Pero, ¿qué es un GUID? Según su definición formal, se trata de una implementación del estándar UUID (Universally Unique Identifier, Identificador Unico Universal), promovido por la OSF (Open Software Foundation), que propone un método de identificación única ideal para entornos distribuidos en los que no existe un coordinador central. En otras palabras, permite que nodos dispersos puedan establecer identificadores de forma única a entidades o unidades de información de forma que se pueda asegurar razonablemente que no van a existir duplicidades en los mismos, y todo ello sin necesidad de crear un punto común de comprobaciones.

A efectos prácticos, un GUID es un número de 16 bytes, o 128 bits, escrito habitualmente en forma de chorizo hexadecimal del tipo "550e8400-e29b-41d4-a716-446655440000", generado utilizando algoritmos pseudoaleatorios.

Pero diréis que un algoritmo pseudoaleatorio no garantiza su unicidad, y es cierto. Sin embargo, 16 bytes ofrecen un universo de 2128 valores posibles (del orden de 3·1038), lo que hace prácticamente imposible que dos GUID se repitan.

Esto los hace especialmente atractivos como identificadores únicos: registros en bases de datos, identificación de componentes (como COM o DCOM), registro de documentos, artículos, posts y un largo etcétera.