lunes, 8 de julio de 2013

SQL SERVER 2008 R2: Limitar conexiones de inicios de sesión con Triggers DDL.

Hay veces que es interesante impedir que un Inicio de Sesión  establezca más de una conexión con el servidor. Para ello SQL Server 2008 R2 incorporar los llamados Desencadenadores (triggers) DDL. Este artículo nos servirá para poner un ejemplo de estos interesantes triggers, que a diferencia de los que usamos normalmente, que se disparan al realizar modificaciones, inserciones o borrados de datos de una tabla, estos se disparan ante alteraciones en la estructura de la base de datos, por ejemplo, o cualquier otro evento sobre el servidor.

Según la MSDN de microsoft, un Desencadendor DDL es: "Los desencadenadores DDL se inician en respuesta a una variedad de eventos de lenguaje de definición de datos (DDL). Estos eventos corresponden principalmente a las instrucciones de Transact-SQL que comienzan por las palabras clave CREATE, ALTER, DROP, GRANT, DENY, REVOKE o UPDATE STATISTICS. Algunos procedimientos almacenados del sistema que ejecutan operaciones de tipo DDL también pueden activar desencadenadores DDL". Podéis encontrar mas información aquí.

Centrandonos en el ejemplo:  Vamos a limitar que un inicio de sesión no pueda realizar mas de tres conexiones simultaneas al Servidor de SQL Server 2008 R2. Para ello vamos a programar un trigger DDL sobre el Inicio de Sesión deseado y el evento de LOGON.

Lo primero que hacemos es crear un nuevo Inicio de Sesión con el Management Studio, para ello hacemos click derecho sobre la carpeta "Seguridad", luego en Nuevo/Inicio de Sesión. Lo configuramos como en la imagen.


Ahora vamos a darle todos los permisos posibles, es decir, será administrador.



Creamos por último el trigger DDL para controlar el número de conexiones:


01 USE master;
02 GO
03 CREATE TRIGGER limite3conexiones_usuarioLimitado
04 ON ALL SERVER WITH EXECUTE AS 'usuarioLimitado'
05 FOR LOGON
06 AS
07 BEGIN
08 IF ORIGINAL_LOGIN()= 'usuarioLimitado' AND
09 (SELECT COUNT(*) FROM sys.dm_exec_sessions
10 WHERE is_user_process = 1 AND
11 original_login_name = 'usuarioLimitado') > 3
12 ROLLBACK;
13 END;

Para probarlo, tan solo tenemos que realizar varias conexiones con el inicio de sesión que hemos creado, hasta superar el limite.



miércoles, 26 de junio de 2013

SQLServer 2008. Importación masiva de datos de un fichero delimitado a una tabla.

Como todos sabréis, y sino os lo digo yo, SQL Server 2008 (en todas sus versiones) nos ofrece un abanico enorme de posibilidades para realizar cualquier cosa que podamos imaginar sobre una base de datos. En este artículo me centrare en insertar grandes volúmenes de datos en una tabla de una tacada. 

Como ya he comentando, tenemos muchísimas posibilidades, pero si los datos están en un fichero externo, es decir, no viene de otra base de datos ya sea en nuestro servidor u otro (enlace al artículo sobre vinculación de servidores), dichas posibilidades se van recortando, aun así, son muchas. Entre ellas y por mi propia experiencia, las que más me gustan son dos:  mediante XML y con ficheros delimitados.

En esta ocasión vamos a explicar como lo haríamos con ficheros delimitados, donde nuevamente, tenemos varias posibilidades, siendo la que explicaré a continuación no la mas rápida (de implementar), pero si la más segura y efectiva. Este proceso sería muy útil a la hora de hacer una importación/exportación de datos.
Teniendo la tabla:
01 CREATE TABLE poetas
02 (
03    CODIGO integer not null PRIMARY KEY,
04    NOMBRE VARCHAR(150) not null,
05    APELLIDOS VARCHAR(150) not null,
06    DIRECCION VARCHAR(150) null,
07    LOCALIDAD VARCHAR(150) null,
08    PROVINCIA VARCHAR(150) null
09 )
Y teniendo un fichero con el siguiente formato: 
01 1|Gustavo Adolfo|Bécquer| Conde de Barajas|Sevilla|Sevilla##
Donde el carácter "|" es el separador de campos, y los caracteres "##" el separador de linea. Además necesitamos un XML con la definición de campos del fichero a usar relacionándolos con los campos y tipos de las columnas de la tabla donde se van a insertar los datos. El formato sería: 

01 <?xml version="1.0"?> 
02 
03 <BCPFORMAT xmlns="http://schemas.microsoft.
04 com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.
05 org/2001/XMLSchema-instance"> 
06 
07 <RECORD> 
08 
09 <FIELD ID="1" xsi:type="CharTerm" TERMINATOR="|"/> 
10 <FIELD ID="2" xsi:type="CharTerm" TERMINATOR="|"/> 
11 <FIELD ID="3" xsi:type="CharTerm" TERMINATOR="|"/> 
12 <FIELD ID="4" xsi:type="CharTerm" TERMINATOR="|"/> 
13 <FIELD ID="5" xsi:type="CharTerm" TERMINATOR="|"/> 
14 <FIELD ID="6" xsi:type="CharTerm" TERMINATOR="##"/> 
15 
16 </RECORD> <ROW> 
17 
18 <COLUMN SOURCE="1" NAME="CODIGO" xsi:type="SQLSMALLINT"/> 
19 <COLUMN SOURCE="2" NAME="NOMBRE" xsi:type="SQLNVARCHAR"/> 
20 <COLUMN SOURCE="3" NAME="APELLIDOS" xsi:type="SQLNVARCHAR"/> 
21 <COLUMN SOURCE="4" NAME="DIRECCION" xsi:type="SQLNVARCHAR"/> 
22 <COLUMN SOURCE="5" NAME="LOCALIDAD" xsi:type="SQLNVARCHAR"/> 
23 <COLUMN SOURCE="6" NAME="PROVINCIA" xsi:type="SQLNVARCHAR"/> 
24 
25 </ROW> </BCPFORMAT> 


Solo nos faltaría ejecutar una instrucción como esta:


01 INSERT INTO poetas(CODIGO, NOMBRE, APELLIDOS, DIRECCION, LOCALIDAD, PROVINCIA) 
02 SELECT  CODIGO, NOMBRE, APELLIDOS, DIRECCION, LOCALIDAD, PROVINCIA
03 FROM OPENROWSET(BULK 'C:\exportacion\poetas.txt', 
04 FORMATFILE='C:\exportacion\poetas.xml' 
05 ) as t1 ;
Aclarar que igual que lo hacemos con una sentencia INSERT, podríamos hacerlo con un UPDATE o un DELETE sin más problemas. También deciros que esta instrucción disparará una sola vez los triggers que tengamos  en la tabla, por lo que deberemos programarlos para que traten el conjunto de datos y no solo un registro.

domingo, 23 de junio de 2013

Exprés: ¿Qué ha indexado google de mi web?

Es muy interesante saber que páginas (y como) tiene google indexadas de nuestra web, o de cualquier otra. Para ello nos ofrece esta utilidad, muy usada en el mundo de SEO. Se trata del comando site:dominio.com. para usarla tan solo tenemos que ir www.google.es y escribir en el buscador site:dominioquequeramosver.loquesea. (También para subdominios)

Nos aparecerán todas las páginas que google tiene indexadas sobre dicho dominio. Con esto podremos comprobar si estan correctas todas nuestras URLs amigables por ejemplo, o si hay algunas páginas que no queremos que google indexe. 

Para limitar la indexación de google, usaremos el fichero robots.txt introduciendo una directiva "Disallow:paginaquenoqueremosqueaparezca" con las URLs que queramos bloquear. También podríamos usar un subdominio, o una carpeta cuyo contenido no queramos que indexe.

Por último, comentar que esta utilidad de google nos da la posibilidad de pasarle parámetros de búsqueda siguiendo a la instrucción de la forma: site:dominioquequeramosver.loquesea palabrasdebusqueda. De esta forma, filtraremos los resultados obteniendo tan solo las páginas donde aparezcan las palabras de búsqueda que le pasamos como parámetro.

miércoles, 19 de junio de 2013

Ejemplo de encriptación de campos de una base de datos de SQL Server 2008 con Clave

Todos las empresas quieren proteger sus datos de terceros, e incluso de sus propios trabajadores. Lo que muchos "jefes" no tienen tan claro es que siempre hay dos niveles de protección... uno físico y uno lógico. De nada sirve poner un sistema de seguridad por software de ultima generación y "super seguro" si cualquiera tiene acceso al disco físico donde se almacenan los datos. Como respuesta a la pregunta, si alguien se lleva mi base de datos, ¿puede acceder a los datos?. La respuesta siempre es SI. Lo único que podemos hacer es ponerlo más difícil.

Por tanto, desde mi punto de vista, lo más importante es tener un sitio seguro donde almacenar los datos y que físicamente  tan sólo personal autorizado puede acceder a ellos. Además hay que complementar esto con una buena seguridad por software que nos evite el máximo número de ataques posible. ¿y si todo esto falla? la solución pasa por encriptar parte o la totalidad de nuestra base de datos, que si bien no es infalible, nuevamente complicamos un poco mas las cosas a "los malos".

Hay varias formas de encriptar una base de datos de SQL Server 2008, una de las más sencillas es la siguiente. Encriptar los campos de las tablas que no queramos que nadie conozca con una clave. Está claro que si perdemos la clave... nos resultará un poco complicado obtener nosotros mismos esos datos, pero como siempre, por "fuerza bruta" se pueden obtener. Por esto decimos, que lo único que conseguiremos es complicar un poco mas las cosas a "los malos". En próximas entradas hablaré de otros métodos de encriptación de las bases de datos de SQL Server 2008. Es importante saber también  que este método de encriptación esta disponible en la versión Express.

Un ejemplo: 

01 create database DBConClave 
02 
03 GO 
04 
05 use DBConClave 
06 
07 GO 
08 
09 create table Clientes 
10 ( 
11 id integer identity primary key, 
12 nombre varchar(100), 
13 apellidos varchar(200), 
14 cif varchar(20), 
15 ccc VARBINARY(8000) -- este es el campo que vamos a cifrar. 
16 ) 
17 
18 GO 
19 
20 -- insertamos un registro con clave 
21 -- es la menor protección pero también la que requiere menos recursos 
22 INSERT INTO Clientes (nombre, apellidos, cif, ccc) 
23 VALUES ('Sandra', 'Matos', '1231231',ENCRYPTBYPASSPHRASE('mipassword','123132131321')) --mispassword es la clave de cifrado 
24 
25 GO 
26 
27 --si hacemos un select normal no podemos obtener el ccc 
28 SELECT * FROM CLientes 
29 
30 
31 --para poder obtener el ccc del cliente deberíamos pasarle también la clave. 
32 SELECT nombre, apellidos, cif, CONVERT(VARCHAR(300), 
33 DECRYPTBYPASSPHRASE('mispassword',ccc)) as ccc 
34 FROM Clientes



sábado, 15 de junio de 2013

Exprés: Crear Base de Datos SQL Server 2008 en cualquier directorio

En SQL Server 2008 las bases de datos se crean por defecto en el directorio que viene configurado, si bien, es posible cambiarlo. Pero, ¿y si queremos almacenar nuestras bases de datos en otra ubicación ?. El script que deberemos utilizar es el siguiente:

01 create database prueba2 
02 on (name = prueba2_dat, filename = 'D:\Databases\prueba2_dat.mdf') 
03 log on (name = prueba2_log, filename = 'D:\Databases\prueba2_log.ldf') 
04 collate modern_spanish_ci_As



Incluso podemos decirle que el directorio de destino sea una carpeta de red (de otro equipo), pero en este caso, deberemos asignar permisos de Control Total en dicha carpeta para el usuario de Windows que inicia el servicio de SQL Server. Esta opción no es recomendable, porque dejamos en manos de la red el tiempo de respuesta que pueda tener nuestra base de datos, con lo que ello conlleva.

miércoles, 12 de junio de 2013

Vinculación de servidores con SQL Server 2008

Hay veces que necesitamos obtener en una sola consulta datos de tablas de diferentes bases de datos. Si estas bases de datos, están en el mismo servidor, no tenemos ningún problema, haciéndolo de la manera siguiente, lo tendríamos solucionado. Por ejemplo:

01 SELECT * FROM BD1.esquema.Tabla
02 UNION ALL
03 SELECT * FROM bd2.esquema.Tabla

En este caso estamos haciendo una simple unión (recordar que las campos que se devuelvan en cada subconsulta deben ser del mismo tipo y en el mismo orden). Pero que pasa si las bases de datos están en servidores diferentes, SQL Server 2008 nos proporciona las características necesarias para solventar este problema.

Lo primero que tendremos que hacer es agregar el servidor remoto al nuestro (al que estamos conectado) de la siguiente forma:

01 EXEC sp_addlinkedserver 'SERVIDORREMOTO\INSTANCIA', N'SQL Server';

Ahora,  de una forma similar a como lo haciamos con diferentes bases de datos pero en el mismo servidor, lo haremos pero anteponiendo  el servidor remoto.


01 SELECT * FROM [SERVIDORREMOTO\INSTANCIA].[BD1].[esquema].[Tabla]
02 UNION ALL
03 SELECT * FROM [SERVIDORLOCAL\INSTANCIA].[bd2].[esquema].[Tabla]


Para definir el Inicio de Sesión y la clave con la que queremos acceder al servidor, usaremos el procedimiento :sp_addlinkedsrvlogin, por ejemplo: exec sp_addlinkedsrvlogin 'h5sefsomzv.database.windows.net,1433', 'FALSE', NULL, 'iniciodesesion', 'claveiniciodesesion';

sábado, 8 de junio de 2013

Informes con ReportViewer sobre modelo de Entity Framework

Creamos un nuevo proyecto Windows Forms con el Framework 4.0. Agregamos, como siempre, el nuestro modelo de datos de Entity Framework, usando esta vez el enfoque "Model first". Para ello pulsamos botón derecho sobre el proyecto, y Agregar/Nuevo elemento. Buscamos "ADO.NET Entity Data Model", le damos un nombre y creamos nuestro modelo. En mi caso:


Cuando tengamos el modelo creado, hacemos botón derecho sobre el, y pulsamos en "Generar Base de datos desde el modelo", seleccionamos el origen de datos deseado así como la base de datos y generámos las instrucciones DDL de nuestra base de datos. Lo ejecutamos para crear el metadata y listo para trabajar.

A nuestro formulario principal vamos a añadir un componente ReportViewer donde mostraremos el informe. Un ComboBox para seleccionar la familia de productos y un botón para ejecutar la acción.

Ahora vamos a crear el informe, para ello nuevamente pulsamos botón derecho sobre el proyecto, Agregar / Nuevo elemento y esta vez seleccionamos en la sección de Reporting, Informe (Report), Le damos un nombre y comenzamos a crear el informe.



En este paso vamos a crear los conjuntos de datos que usaremos en el informe, para ello en la barra de tareas "Datos del informe" hacemos botón derecho sobre Conjunto de Datos y le damos a Agregar nuevo conjunto de datos. Le damos un nombre y pulsamos en Nuevo / Objeto y buscamos en nuestro modelo, la entidad que queramos usar, en mi caso "Articulos". Este proceso lo repetimos con todas las entidades que queramos involucrar en nuestro informe.



Ahora solo tenemos que diseñar el informe como queramos, agregando gráficos, tablas, etc. y vinculando los conjuntos de datos a los distintos componentes.



Que pasa si queremos hacer un gráfico con datos de dos conjuntos de datos diferentes, una solución puede ser la siguiente:

Creamos una nueva Clase y la llamamos Vistas, por ejemplo. Dentro vamos a crear metodos que nos devuelve un objeto que satisface la interfaz IQueryable que nos devuelve los datos obtenidos desde el modelo de Entity Framework con consultas LINQ. por ejemplo:

01         public IQueryable<DetalleCompraVenta> getCompraVenta(
02         )
03         {
04             return (from c in this.getTotalVentas()
05                     select new DetalleCompraVenta
06                     {
07                         articulo = c.articulo,
08                         ventas = c.ventas,
09                         compras = 0
10                     }).Union(from d in this.getTotalCompras()
11                              select new DetalleCompraVenta
12                              {
13                                  articulo = d.articulo,
14                                  ventas = 0,
15                                  compras = d.compras
16                              }
17                     );
18         }

Por último, para agregar un conjunto de datos al informe con los datos de estos metodos, lo haremos de la misma forma que hicimos con las entidades del modelo. Ahora solo nos falta vincular este conjunto de datos al gráfico que queramos y listo.

El resultado del ejemplo es algo como esto:



Podéis descargar el ejemplo completo aquí.

martes, 4 de junio de 2013

Exprés: Consulta Linq sobre modelo Entity Framework con producto cartesiano.

Para realizar una consulta en LINQ con un producto cartesiano, lo haremos de la siguiente manera. Suponemos un contexto con las entidades "articulo" y "almacen" y necesitamos un listado con todos los artículos por almacenes. Seria lo siguiente:
01 IQueryable datos = (from c in contexto.articulos  
02    from d in contexto.almacenes                    
03    select new                    
04    {                        
05     articulo= c.articulo,                        
06     almacen = d.almacen                    
07    });
Suponemos que tenemos un contexto con las entidades "cliente" y "proveedor" cuya clave primaria es código en ambas. Además suponemos que si el código es el mismo, se refiere al mismo individuo, por tanto la consulta quedaría así:
01 IQueryable datos = (from c in contexto.clientes          
02    from d in contexto.proveedores                   
03    where c.codigo == d.codigoq                    
04    select new                    
05    {                        
06     codigo = c.articulo,                        
07     ventas = c.ventas,                        
08     compras = d.compras                    
09    });
También podría hacerse con un join:
01 IQueryable datos = (from c in contexto.clientes         
02    join d in contexto.proveedores 
03     on c.codigo equals d.codigo                    
04    select new                    
05    {                        
06     codigo = c.articulo,                        
07     ventas = c.ventas,                        
08     compras = d.compras                    
09    });
Hasta aquí el artículo exprés.

viernes, 24 de mayo de 2013

Esqueleto de aplicación con pestañas en vez de formularios en C#

Están muy de moda las pestañas en las aplicaciones. Estas nos permiten tener bastante ordenadas todas las ventanas de la aplicación con las que estemos trabajando simultáneamente.

La idea consiste en usar "Controles de Usuarios" (UserControl) en vez de formularios tradicionales y abrirlos y crearlos en tiempo de ejecución en pestañas (TabPage) de un TabControl.

Empezamos por crear un nuevo proyecto de Windows Forms y le añadimos un menú (MenuStrip) con todas las opciones que necesitemos. Añadimos un TabControl para ir abriendo las distintas pestañas y un botón para cerrarlas.




Para crear los "Controles de Usuario" haremos botón derecho sobre el proyecto, luego Agregar / Nuevo Elemento y seleccionamos "Control de Usuario".



La función que se propone para abrir las pestañas es:
01 private void nuevaPestana(UserControl uc, String titulo)
02 {
03         int indice = indicePestanaAbierta(titulo);
04         if (indice < 0) {
05                 TabPage tab = new TabPage();
06                 tab.Text = titulo;
07                 tab.Controls.Add(uc);
08                 tabMain.TabPages.Add(tab);
09                 tabMain.SelectedTab = tab;
10         } else {
11                 //si ya existe vamos a limpiar el usercontrol y volverlo a cargar en la misma pestaña
12                 tabMain.TabPages[indice].Controls.Clear();
13                 tabMain.TabPages[indice].Controls.Add(uc);
14                 tabMain.SelectedTab = tabMain.TabPages[indice];
15         }
16 }
Con esta función controlamos también que si ya existe esa pestaña, nos la refresque en vez de abrir otra.
01 private int indicePestanaAbierta(String titulo)
02 {
03         Boolean enc = false;
04         int i = 0;
05         while ((i < tabMain.TabPages.Count) && (enc == false)) {
06                 if (tabMain.TabPages[i].Text == titulo)
07                         enc = true;
08                 else
09                         i++;
10         }
11         if (enc)
12                 return i;
13         else
14                 return -1;
15 }
El evento del botón para cerrar las pestañas sería algo así.
01 private void btnCerrar_Click(object sender, EventArgs e)
02 {
03         int indice = tabMain.SelectedIndex;
04         if (indice > 0) { //no queremos cerrar nunca la inicial.
05                 tabMain.TabPages.Remove(tabMain.SelectedTab);
06                 tabMain.SelectedIndex = indice - 1;
07                 GC.Collect();
08         }
09 }
El evento de cualquiera de las opciones de menú para abrir las pestañas sería:
01 private void configuracionGeneralToolStripMenuItem_Click(object sender, EventArgs e)
02 
03 {
04         UCconfiguracionGeneral uc = new UCconfiguracionGeneral();
05         nuevaPestana(uc, "Configuración");
06 
07 }
El ejemplo completo, se puede descargar desde aquí.

martes, 21 de mayo de 2013

Quitar estado "Suspect" de Bases de Datos de SQL Server 2008

Hay veces que nuestras bases de datos pueden aparecer "sin previo aviso" en estado Suspect... normalmente esto ocurre porque el dispositivo físico donde están los ficheros de la base de datos no se ha inicializado antes que el propio servicio de SQL Server 2008. Por tanto la base de datos se marca de esta forma para impedir su utilización. Si este es el motivo, normalmente basta con reiniciar el servicio de SQL Server y la base de datos aparecerá disponible de nuevo. 

Muchas veces esto no es suficiente, o realmente lo que provoca el estado Suspect es una corrupción de alguno de los ficheros de la base de datos, para estos casos podemos seguir el siguiente proceso.


Ponemos la base de datos en estado de emergencia
01 ALTER DATABASE miBaseDeDatos SET EMERGENCY; 
Ponemos la base de datos en modo de usuario único para asegurarnos que solo nosotros trabajamos en ella
01 ALTER DATABASE miBaseDeDatos SET SINGLE_USER; 
Realizamos la reparación de la base de datos permitiendo la pérdida de datos (el asumir pérdida de datos puede no ser necesario, para no permitirlo usaremos el parametro REPAIR_REBUILD)
01 DBCC checkdb ('miBaseDeDatos', REPAIR_ALLOW_DATA_LOSS);
Volvemos a poner a poner la base de datos disponible.

01 ALTER DATABASE miBaseDeDatos SET ONLINE; 
Por último permitimos multiples conexiones.
01 ALTER DATABASE miBaseDeDatos SET MULTI_USER;

viernes, 17 de mayo de 2013

Ejemplo de uso de exepciones personalizadas en SQL Server

A veces es interesante crear excepciones personalizadas en una base de datos para que se disparen cuando realicemos una acción que no queramos permitir. Para explicarlo, propongo el siguiente ejemplo:
01 create database BDExcepciones 
02 GO
03 use BDExcepciones 
04 GO
05 create table Coches
06 ( 
07  id int identity primary key, 
08  marca varchar(20),   
09  descripcion varchar(100), 
10  matricula varchar(20) 
11 ) 
12 GO
Introducimos unos datos en la tabla
01 insert into Coches (marca, descripcion, matricula)  
02  VALUES ('Mercedes', 'El Coche del Roman Azul Cielo', 'NOTEPEGA'); 
03 insert into Coches (marca, descripcion, matricula)  
04  VALUES ('Opel Corsa', 'El corsita', 'PALABODA');  
05 insert into Coches (marca, descripcion, matricula)  
06  VALUES ('Rover 25', 'A ver lo que dura', 'ROTO');  
07 insert into Coches (marca, descripcion, matricula)         
08  VALUES ('Ford Focus', 'El coche nuevo', 'COCHENUEVO');  
Creamos excepciones.Los identificadores deben ser a partir del 50001 y la severidad debe ser 16 para que se trate como una excepción. Siempre hay que definir el mensaje en ingles y luego en español.
01 use master go
02 sp_addmessage 50002, 11, 'Ya existe un coche con esa matricula', 'us_english'; 
03 go
04 sp_addmessage 50002, 11, 'Ya existe un coche con esa matricula', 'spanish'; 
05 go
06 sp_addmessage 50003, 16, 'Ya existe un coche con esa matricula', 'us_english'; 
07 go
08 sp_addmessage 50003, 16, 'Ya existe un coche con esa matricula', 'spanish'; 
09 go
10 use BDExcepciones go 
Para lanzarla usaremos: RAISERROR (50002, 11, 1). Creamos un procedimiento que inserta un coche y si su matricula existe nos devuelve una excepcion. 
01 ALTER PROCEDURE InsertarCoche
02  @marca varchar(20),
03  @descripcion varchar(100),
04  @matricula varchar(20) AS
05 BEGIN
06  SELECT * FROM Coches WHERE matricula = @matricula 
07  if @@ROWCOUNT = 0 
08  BEGIN
09   INSERT INTO Coches (marca, descripcion, matricula) VALUES (@marca, @descripcion, @matricula);
10  END 
11  ELSE 
12  BEGIN
13   RAISERROR (50002, 11, 1)
14  END
15 END; 
16 -- lo probamos 
17 execute InsertarCoche 'cochenuevo', 'para cuando', 'ROTO'; 
Ahora vamos a hacer el mismo ejemplo pero en el trigger antes de insertar.
01 ALTER PROCEDURE InsertarCoche
02  @marca varchar(20),
03  @descripcion varchar(100),
04  @matricula varchar(20) AS
05 BEGIN
06  SELECT * FROM Coches WHERE matricula = @matricula 
07  if @@ROWCOUNT = 0 
08  BEGIN
09   INSERT INTO Coches (marca, descripcion, matricula) VALUES (@marca, @descripcion, @matricula);
10  END 
11  ELSE 
12  BEGIN
13   RAISERROR (50002, 11, 1)
14  END
15 END; 
16 -- lo probamos 
17 execute InsertarCoche 'cochenuevo', 'para cuando', 'ROTO'; 
Aunque personalmente, a estas alturas prefiero usar la base de datos tan solo como almacén de datos, y delegar a la capa de negocio toda la programación que de otra forma iría en ella, puede que en algún momento nos sea de utilidad.

miércoles, 15 de mayo de 2013

Ejemplo de Aplicación sobre diferentes motores de base de datos con Entity Framework

Uno de las características que más me gusta el Entity Framework es la abstracción del motor de base de datos y que nos permite crear aplicaciones sin atarnos a un motor especifico, pudiendo (teniendo en cuenta ciertas limitaciones) que la misma aplicación funcione sobre distintos motores de bases de datos.


Para el ejemplo vamos a seguir el enfoque "model first", es decir primero crearemos el modelo la herramienta de visual studio y luego generaremos las instrucciones DDL para crear la base de datos en SQLSERVER.


Creamos un proyecto de Visual estudio nuevo como aplicación de consola. Yo uso Visual Studio 2012 premium y compilando la aplicación para el .Net Framework 4. Pulsamos botón derecho sobre el proyecto, y Agregar nuevo modelo. Seleccionamos ADO.NET Entity data model y seleccionamos la opción Empty Model ya que vamos a usar el enfoque "model first".




Una vez creemos el modelo como en la imagen, vamos a generar el código DDL para SQLServer 2008. Deberemos tener instalados los conectores apropiados, yo uso los de Devart (podéis descargarlos aquí  Hay que tener en cuenta a la hora de crear el modelo las limitaciones de tipos de datos que tengan cada motor.


Para ello pulsamos botón derecho y en propiedades vemos que tenemos seleccionada en DDL Generation Template la de SQLServer (SSDLToSQL10.tt), y luego botón derecho de nuevo en el modelo y le damos a Generate database from model y seguimos el asistente. repetiremos este proceso para los tres motores, cambiando en las propiedades del modelo la template DDL que queremos usar, de esta forma conseguimos los script de las bases de datos para los motores que queramos, en mi caso: SQLServer, MySQL, y SQLite.

Ahora vamos a crear los ficheros ssdl que mapean cada motor de base de datos, para ello agregamos un nuevo elemento al proyecto de tipo XML y lo llamamos por ejemplo "modeloMySQL.ssdl" y le activamos la propiedad de copiar siempre al directorio de destino. hacemos lo mismo con los distintos motores de bases de datos.


Hacemos botón derecho sobre el modelo (el .edmx) y le damos a "abrir con" y seleccionamos Editor XML(texto), tenemos que copiar el nodo completo que se llama "Schema" y pegarlo en los .ssdl que creamos en el paso anterior. Por ultimo buscamos el atributo provider del nodo Schema y cambiamos su valor por el provider necesario. Los atributos Schema de los nodos EntitySet los dejaremos vacíos para MySQL y SQLite.
<Schema Namespace="ModeloSQLServer.Store" Alias="Self" Provider="Devart.Data.MySql" ProviderManifestTok ... 
Por ultimo para terminar de configurar las conexiones, creamos las cadenas de conexión en el app.config de la siguiente manera:
01 <connectionStrings>
02 <add name="ModeloSQLServerContainer"
03 
04 connectionString="metadata=res://*/ModeloSQLServer.csdl|res://*/ModeloSQLServer.ssdl|res://*/ModeloSQLServer.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=pc-matos\sqlexpress;initial catalog=ejEFmultiDB;persist security info=True;user id=matos;password=blabla;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
05 <add name="ModeloMySQLContainer"
06 
07 connectionString="metadata=res://*/ModeloSQLServer.csdl|ModeloMySQL.ssdl|res://*/ModeloSQLServer.msl;provider=Devart.Data.MySql;provider connection string=&quot;user id=root;host=localhost;database=ejEFmultiDB;persist security info=True&quot;" providerName="System.Data.EntityClient" />
08 
09 <add name="ModeloSQLiteContainer"
10 
11 connectionString="metadata=res://*/ModeloSQLServer.csdl|ModeloSQLite.ssdl|res://*/ModeloSQLServer.msl;provider=Devart.Data.SQLite;provider connection string=&quot;data source=C:\SQLite\ejEFmultiDB.db&quot;" providerName="System.Data.EntityClient" />
12 
13 </connectionStrings>
Vamos a empezar a escribir código y lo primero que haremos es incluir el ensamblado System.Configuration a las referencias y pondremos su respectivo using.

Para crear el contexto en función de la conexión a cada motor de base de datos, lo hacemos de la siguiente forma:
01 string conexionMySql = ConfigurationManager.ConnectionStrings["ModeloMySQLContainer"].ConnectionString;
02 using (ModeloSQLServerContainer context = new ModeloSQLServerContainer(conexionMySql))
03 {
04         //bloque de código que queramos realizar.
05 }
El ejemplo completo, se puede descargar desde aquí.