6. Caso práctico con PostgreSQL 9.2.1
6.1. Instalación de las herramientas
Las herramientas que hay que instalar son las siguientes:
- el SGBD: [http://www.enterprisedb.com/products-services-training/pgdownload#windows];
- una herramienta de administración: EMS, SQL Manager para PostgreSQL, Freeware [http://www.sqlmanager.net/fr/products/postgresql/manager/download].
En los siguientes ejemplos, el usuario «postgres» tiene la contraseña «postgres».
Ejecutemos PostgreSQL y, a continuación, la herramienta [SQL Manager Lite for PostgreSQL], con la que vamos a administrar el SGBD.
![]() |
- En [1], iniciamos SGBD y PostgreSQL desde los servicios de Windows;
- en [2], se inicia el servicio;
Ahora iniciamos la herramienta [SQL Manager Lite for MySQL], con la que vamos a administrar los servicios SGBD y [3].
![]() |
- En [4], creamos una nueva base de datos;
- en [5], indicamos el nombre de la base de datos;
![]() |
- en [5], nos conectamos como postgres / postgres;
- en [6], se introduce cierta información;
- en [7], se valida la orden SQL que se va a ejecutar;
![]() |
- en [8], se ha creado la base de datos. Ahora debe registrarse en [EMS Manager]. La información es correcta. Se ejecuta [OK];
- en [9], nos conectamos a ella;
- en [10], [EMS Manager] muestra la base de datos, que por el momento está vacía. Cabe señalar que las tablas pertenecerán a un esquema denominado «public [11]».
Ahora vamos a conectar un proyecto VS 2012 a esta base de datos.
6.2. Creación de la base de datos a partir de las entidades
Empezamos duplicando la carpeta del proyecto [RdvMedecins-SqlServer-01] en [RdvMedecins-PostgreSQL-01] y [1]:
![]() |
- en [2]; en VS 2012, eliminamos el proyecto [RdvMedecins-SqlServer-01] de la solución;
![]() |
- en [3], el proyecto se ha eliminado;
- en [4], añadimos otro. Este se incluye en la carpeta [RdvMedecins-PostgreSQL-01] que hemos creado anteriormente;
![]() |
- en [5], el proyecto cargado se llama [RdvMedecins-SqlServer-01];
- en [6], le cambiamos el nombre a [RdvMedecins-PostgreSQL-01];
![]() |
- en [7], se añade otro proyecto a la solución. Este se encuentra en la carpeta [RdvMedecins-SqlServer-01] del proyecto que hemos eliminado de la solución anteriormente;
- en [8], el proyecto [RdvMedecins-SqlServer-01] se ha vuelto a incorporar a la solución.
El proyecto [RdvMedecins-PostgreSQL-01] es idéntico al proyecto [RdvMedecins-SqlServer-01]. Tenemos que realizar algunas modificaciones. En [App.config], vamos a modificar la cadena de conexión y el [DbProviderFactory], que debemos adaptar a cada SGBD.
<!-- cadena de conexión a la base de datos -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- el proveedor de fábrica -->
<system.data>
<DbProviderFactories>
<add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
- línea 3: el usuario y su contraseña;
- líneas 7-9: el DbProviderFactory. La línea 8 hace referencia a un DLL y un [Npgsql] que no tenemos. Se obtiene mediante NuGet y [1]:
![]() |
- en [2], en el campo de búsqueda se escribe la palabra clave postgresql;
- en [3], se selecciona el paquete [Npgsql]. Se trata de un conector ADO.NET para PostgreSQL;
![]() |
- en [4], se han añadido dos referencias;
- en [5], en [App.config], hay que introducir la versión correcta de DLL. Se encuentra en sus propiedades.
En el archivo [Entites.cs], hay que adaptar el esquema de las tablas que se van a generar:
[Table("MEDECINS", Schema = "public")]
public class Medecin : Personne
{...}
[Table("CLIENTS", Schema = "public")]
public class Client : Personne
{...}
[Table("CRENEAUX", Schema = "public")]
public class Creneau
{...}
[Table("RVS", Schema = "public")]
public class Rv
{...}
Anteriormente, al crear una base de datos PostgreSQL, vimos que las tablas pertenecían a un esquema denominado «public».
Configuramos la ejecución del proyecto:
![]() |
- en [1], le damos otro nombre al ensamblado que se va a generar;
- en [2], así como otro espacio de nombres por defecto;
- en [3], se indica el programa que se va a ejecutar.
En este punto, no hay errores de compilación. Ejecutemos el programa [CreateDB_01]. Se produce la siguiente excepción:
Recordamos haber tenido el mismo error con MySQL y Oracle. Está relacionado con el tipo del campo Timestamp de las entidades. Realizamos la misma modificación que con Oracle. En las entidades, sustituimos las tres líneas
[Column("TIMESTAMP")]
[Timestamp]
public byte[] Timestamp { get; set; }
por las siguientes:
[ConcurrencyCheck]
[Column("VERSIONING")]
public int? Versioning { get; set; }
Cambiamos el tipo de la columna de byte[] a int?. En el SGBD, utilizaremos procedimientos almacenados para incrementar este entero en una unidad cada vez que se inserte o modifique una línea.
Realizamos la modificación anterior en las cuatro entidades y, a continuación, volvemos a ejecutar la aplicación. Entonces obtenemos el siguiente error:
La línea 1 indica que el conector ADO.NET de PostgreSQL no puede eliminar la base de datos existente. Exactamente igual que con Oracle. Por lo tanto, nos vemos obligados a crear la base de datos [RDVMEDECINS-EF] manualmente con la herramienta [EMS Manager for PostgreSQL]. No describiremos todos los pasos, sino solo los más importantes.
La base de datos PostgreSQL tendrá la siguiente estructura:
Las tablas
![]() |
- en [1], ID es la clave primaria de tipo serial. Este tipo PostgreSQL es un entero generado automáticamente por el SGBD.
![]() |
![]() |
![]() |
![]() |
Las distintas tablas tienen las claves primarias y externas que tenían esas mismas tablas en los ejemplos anteriores. Las claves externas tienen el atributo ON, DELETE y CASCADE.
Las secuencias
Al igual que en Oracle, aquí hemos creado secuencias. Se trata de generadores de números consecutivos. Hay cinco: [1].
![]() |
- En [2], vemos las propiedades de la secuencia [CLIENTS_ID_SEQ]. Genera números consecutivos de 1 en 1, desde el 1 hasta un valor muy grande.
Todas las secuencias siguen el mismo modelo.
- [CLIENTS_ID_seq] se utilizará para generar la clave primaria de la tabla [CLIENTS];
- [MEDECINS_ID_seq] se utilizará para generar la clave primaria de la tabla [MEDECINS];
- [CRENEAUX_ID_seq] se utilizará para generar la clave primaria de la tabla [CRENEAUX];
- [RVS_ID_seq] se utilizará para generar la clave primaria de la tabla [RVS];
- [sequence_versions] se utilizará para generar los valores de las columnas [VERSIONING] de todas las tablas.
Los disparadores
Un disparador es un procedimiento ejecutado por SGBD antes o después de un evento (inserción, modificación, eliminación) en una tabla. Tenemos cuatro: [1]:
![]() |
Veamos el código DDL del disparador [CLIENTS_tr] que alimenta la columna [VERSIONING] de la tabla [CLIENTS]:
- líneas 1-3: antes de cada operación INSERT o UPDATE en la tabla [CLIENTS];
- línea 4: se ejecuta el procedimiento [public.trigger_versions()].
El procedimiento [public.trigger_versions()] es el siguiente:
- línea 2: NEW representa la línea que se va a insertar o modificar. NEW. «VERSIONING» es la columna [VERSIONING] de esta línea. Se le asigna el siguiente valor del generador de números: «sequence_versions». De este modo, la columna ["VERSIONING"] cambia cada vez que se realiza una operación INSERT / UPDATE en la tabla [CLIENTS].
Los disparadores [MEDECINS_tr, CRENEAUX_tr, RVS_tr] funcionan de manera análoga. Las cuatro columnas ["VERSIONING"] obtienen sus valores de la misma secuencia.
El script de generación de las tablas de la base de datos PostgreSQL y [RDVMEDECINS-EF] se ha colocado en la carpeta [RdvMedecins / databases / postgreSQL]. El lector podrá cargarlo y ejecutarlo para crear sus tablas.
Una vez hecho esto, se pueden ejecutar los distintos programas del proyecto. Ofrecen los mismos resultados que con SQL Server, salvo el programa [ModifyDetachedEntities], que se cuelga por la misma razón por la que se colgaba con Oracle. El problema se resuelve de la misma manera. Basta con copiar el programa [ModifyDetachedEntities] del proyecto [RdvMedecins-Oracle-01] al proyecto [RdvMedecins-PostgreSQL-01].
El programa [LazyEagerLoading] se bloquea con la siguiente excepción:
El código erróneo es el siguiente:
using (var context = new RdvMedecinsContext())
{
// franja n.º 0
creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
Console.WriteLine(creneau.ShortIdentity());
}
En la línea n.º 1 de la excepción, el error señalado sugiere que se trata de una unión, ya que LEFT es una palabra clave de la unión. Dado que la línea 4 del código anterior solicita la carga inmediata de la dependencia [Medecin] de una entidad [Creneau], EF ha realizado una unión entre las tablas [CRENEAUX] y [MEDECINS]. Pero parece que el conector ADO.NET ha generado una orden SQL incorrecta. Reescribimos el código de la siguiente manera:
using (var context = new RdvMedecinsContext())
{
// franja n.º 0
creneau = context.Creneaux.Find(idCreneau);
Console.WriteLine(creneau.ShortIdentity());
// se fuerza la carga del médico asociado
// esto es posible porque aún estamos en un contexto abierto
Medecin medecin = creneau.Medecin;
}
- línea 4: buscamos la franja horaria sin unión;
- línea 8: recuperamos la dependencia que faltaba.
Funciona. De nuevo observamos que el cambio en SGBD tiene un impacto en el código. De hecho, lo que está causando el problema aquí no es el SGBD, sino su conector ADO.NET.
6.3. Arquitectura multicapa basada en EF 5
Volvemos a nuestro caso práctico descrito en el apartado 2.
![]() |
Comenzaremos por crear la capa de acceso a los datos [DAO]. Para ello, duplicamos el proyecto de consola VS 2012 [RdvMedecins-SqlServer-02] en [RdvMedecins-PostgreSQL-02] [1]:
![]() |
- en [2], eliminamos el proyecto [RdvMedecins-SqlServer-02];
![]() |
- en [3], se añade un proyecto existente a la solución. Se toma de la carpeta [RdvMedecins-PostgreSQL-02] que se acaba de crear;
- en [4], el nuevo proyecto tiene el mismo nombre que el que se ha eliminado. Vamos a cambiarle el nombre;
![]() |
- en [5], hemos cambiado el nombre del proyecto;
- en [6], se modifican algunas de sus propiedades, como en este caso el nombre del ensamblado;
- en [7], se elimina la carpeta [Models] para sustituirla por la carpeta [Models] del proyecto [RdvMedecins-PostgreSQL-01]. De hecho, ambos proyectos comparten las mismas plantillas.
![]() |
- en [8], las referencias actuales del proyecto;
- en [9], se ha añadido el conector ADO.NET de PostgreSQL con la herramienta NuGet.
En el archivo [App.config], se sustituye la información de la base SQL Server por la de la base PostgreSQL. Esta información se encuentra en el archivo [App.config] del proyecto [RdvMedecins-PostgreSQL-01]:
<!-- cadena de conexión a la base de datos -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- el proveedor de fábrica -->
<system.data>
<DbProviderFactories>
<add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
Los objetos gestionados por Spring también cambian. Actualmente tenemos:
<!-- Configuración de Spring -->
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="rdvmedecinsDao" type="RdvMedecins.Dao.Dao,RdvMedecins-SqlServer-02" />
</objects>
</spring>
La línea 7 hace referencia al ensamblado del proyecto [RdvMedecins-SqlServer-02]. El ensamblado es ahora [RdvMedecins-PostgreSQL-02].
Una vez hecho esto, estamos listos para ejecutar la prueba de la capa [DAO]. Antes hay que asegurarse de rellenar la base de datos (programa [Fill] del proyecto [RdvMedecins-PostgreSQL-01]). El programa de prueba falla con la siguiente excepción:
En la línea 13, el mensaje indica que el error se ha producido en el método [GetCreneauxMedecin] de la capa [DAO]. Este es el siguiente:
// lista de franjas horarias de un médico concreto
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// lista de franjas horarias
try
{
// apertura del contexto de persistencia
using (var context = new RdvMedecinsContext())
{
// se recupera el médico con sus franjas horarias
Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
// se devuelve la lista de franjas horarias del médico
return medecin.Creneaux.ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
En la línea 11, se reconoce la palabra clave Include, que ya había provocado un fallo en un programa anterior. El código anterior puede sustituirse por el siguiente:
// lista de franjas horarias de un médico concreto
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// lista de franjas horarias
try
{
// apertura del contexto de persistencia
using (var context = new RdvMedecinsContext())
{
// se devuelve la lista de franjas horarias del médico
return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
El nuevo código parece incluso más coherente que el anterior. Sea como fuere, esta vez el programa de prueba se ejecuta correctamente.
Creamos el archivo DLL del proyecto tal y como se hizo con el proyecto [RdvMedecins-SqlServer-02] y reunimos eltodos los archivos DLL del proyecto en una carpeta [lib] creada dentro de [RdvMedecins-PostgreSQL-02]. Estas serán las referencias del proyecto web [RdvMedecins-PostgreSQL-03] que viene a continuación.
![]() |
Ya estamos listos para crear la capa [ASP.NET] de nuestra aplicación:
![]() |
Partiremos del proyecto [RdvMedecins-SqlServer-03]. Duplicamos la carpeta de este proyecto en [RdvMedecins-PostgreSQL-03] y [1]:
![]() |
- en [2], con VS 2012 Express para la web, abrimos la solución de la carpeta [RdvMedecins-PostgreSQL-03];
- en [3], cambiamos tanto el nombre de la solución como el del proyecto;
![]() |
- en [4], las referencias actuales del proyecto;
- en [5], las eliminamos;
- por [6], para sustituirlas por referencias a DLL, que acabamos de guardar en una carpeta [lib] del proyecto [RdvMedecins-PostgreSQL-02].
Ahora solo nos queda modificar el archivo [Web.config]. Sustituimos su contenido actual por el contenido del archivo [App.config] del proyecto [RdvMedecins-PostgreSQL-02]. Una vez hecho esto, ejecutamos el proyecto web. Funciona. No hay que olvidar rellenar la base de datos antes de ejecutar la aplicación web.


























