EF 4 + CTP 5 = Code First: Conventions IV

En el último post sobre convenciones miramos con usar las características de plugable conventions en la nueva CTP de Entity Framework. En esa ocasión, vimos como utilizar una clase publica llamada AtributeConfigurationConvention, gracias a la cual podemos asociar convenciones a atributos, generalmente los definidos en Data Annotations.

En el caso de no necesitar/querer basar las convenciones en ningun tipo de atributo y solamente usar las características de reflexión, además de la clase anterior podemos hacer uso de una nueva interfaz incluída en esta CTP, concrétamente en una interfaz de nombre IConfigurationConvention, esta interfaz, es similar a la anterior, en el sentido en que sus parámetros genéricos  permiten establecer si aplica a un tipo o propiedad y el elemento de configuración en concreto ( EntityTypeConfiguration, PropertyConfiguration, DecimalPropertyConfiguration …)

Como ejemplo práctico veremos como crear una convención que establezca como clave de la entidad a aquellas propiedades que terminen con el postfijo KEY:

 

El proceso de establecer la convención, es exáctamente igual que para el caso anterior, agregar la convención en la propiedad Conventions de nuestro model builder.

 

 

Espero que esto os ayude,

Un saludo

Unai

[Evento] Mongo DB – NOSQL para SI.NET y NO.NET

El próximo 25 de Enero tendré el placer de volver junto a mis amigos del Centro de Excelencia de Software de Navavarra para impartir una charla sobre Mongo DB. En esta ocasión tendré el placer y orgullo de impartir la sesión con mi buen amigo Carlos Segura. En esta sesión trataremos de cubrir desde los porques de la existencia de bases de datos no relacionales y pormenores acerca de Mongo DB, desde elementos básicos hasta replica set, sharding y otros elementos importantes para la escalabilidad. Además, como no todo es .NET veremos como utilizar y trabajar con Mongo desde .NET y desde Ruby On Rails..

 

Para los que podáis asistir allí nos veremos

Saludos

Unai

[hoMONGOus DB]Índices, profiling en Mongo DB – II

En la anterior entrada sobre MongoDb e índices, empezamos por explicar las principales características de estos elementos en Mongo. A lo largo de la siguiente entrada, intentaremos profundizar un poco más en indices y profiling.

Al igual que existe en otras bases de datos relacionales y nosql, mongodb dispone de un sistema para forzar a las consultas a utilizar un determinado índice, algo que por regla general se conoce como un hint. Esta misma palabra es la que se utiliza en MongoDb y su uso es tan simple como se ve en la siguiente figura:

pic5

En código en C# el mismo trabajo que el de la figura anterior sería el siguiente:

 

Realmente, hacer un hint, suele ser innecesario, puesto que el optimizador de consultas suele elegir la mejor manera de ejecutar una consulta de entre la eleccion de distintos planes de ejecución.

Para los que conozcais MongoDb, sabreis que esta base de datos documental dispone de soporte para datos geoespaciales, y por lo tanto, los indices también se pueden crear sobre este tipo de datos, así por ejemplo para crear un indice geoespacial sobre una clave llamada gps bastaría con realizar lo siguiente db.mapa.ensureIndex({“gps”:”2d”}) dónde el  valor de la clave 2d es obligatorio para indicar que es un índice geoespacial. Una vez creado este índice, las consultas geoespaciales podrán hacer uso del mismo, db.mapa.find({“gps”,{“$near”:[40,100]}}).

Hasta ahora, tanto en esta entrada como en la entrada anterior hemos visto el trabajo con indices en MongoDB, por supuesto, el uso de indices, como también en cualquier base de datos relacional, nos permite acelerar nuestras consultas sobremanera. Sin embargo, realmente, hasta ahora no sabemos como ver cuales son las consultas costosas  en una base de datos de mongodb.  Para hacer este trabajo, MongoDb nos permite establecer un nivel de profiling, por defecto desactivado. Este nivel de profiling, que podemos ver mediante la función db.getProfilingLevel(), se puede modificar mediante la función setProfilingLevel, método que acepta dos parámetros ( el nivel de profiling y el tiempo minimo), así por ejemplo si ejecutamos

db.setPRofilingLevel(1,100) estaremos ordenando logear las consultas cuyo tiempo de ejecución sea superior a los 100 ms ( consultas consideradas lentas por defecto ). Si queremos logear todas las operaciones, basta con indicar como valor 2 el primero de los parámetros de la función, db.setProfilingLevel(2).

 

Una vez que hemos activado el profiling, podemos revisar la información dentro de la colección db.system.profile.find(), como se ve a continuación:

 

pic6

Dentro de los documentos contenidos en la colección anterior, podemos ver la información de las consultas realizadas con el fin de revisar aquellas cuyos tiempo de ejecución sean demasiado altos…

Espero que tanto esta entrada como la anterior os resultara de interés….

 

Saludos

Unai

[hoMONGOus DB]Índices, profiling en Mongo DB – I

NOTA PRELIMINAR: Antes de nada, noes mi propósito profundizar en las raices de NoSQL y ni siquiera ponerme a explicar posibles escenarios o motivaciones de uso, para eso ya hay mucha información por la web. Por supuesto, tampoco pretendo hacer una introducción a Mongo DB, para eso también tenemos una estupenda web de referencia, www.mongodb.org con una cantidad de información terrible, y en diferentes idiomas para que no exista la excusa del inglés. Además si visitas Amazon también podrás ver información sobre libros de esta base de datos nosql.

Para serle sincero, MongoDB, es una de estas cosas que te sorprenden, y no por el hecho de ser una base de datos NOSQL, puesto que ya tenía experiencias con alguna de ellas. Desde que por temas de trabajo he empezado a trabajar con MongoDB,  hace ya un tiempo largo, su simplicidad y sus “terribles” capacidades y la forma de explotarlas me han parecido maravillosas. Siempre que tenga tiempo, que es casi nunca, intentaré escribir algunas de las cosas curiosas de MongoDb o bien algunos truquillos de interés, lógicamente, puesto que la temática de este blog es .NET trataré de enfocarme en temas que tengan que ver con las API .NET de manejo de MongoDB.

Una de las cosas en las que uno primero piensa, bueno, por lo menos eso fué lo que me pasó a mi, es en como funciona el motor de consultas y las similitudes que esos pudieran tener con un motor relacional. Lógicamente, lo primero es pensar en si existe el concepto de índices y como estos están implementados. Para MongoDB, los índices son prácticamente idénticos, en realidad la filosofía es exáctamente igual,  a los indices que pudiéramos tener en una base de datos relacional, lógicamente con la diferencia de que estos índices se expresan como documentos ( mongodb es una base de datos documental) y aplican sobre documentos. Conceptos como la densidad y la ordenación aplican de igual forma tanto en una base de datos relacional como en esta base documental.

En el momento en el que una base de datos es creada en MongoDb, esta automáticamente dispone de una colección, llamada system.idexes que permite almacenar la información de nuestros índices, en la siguiente figura puede ver como obtener la información de los distintos índices almancenados. Por supuespic1to, esta misma información la podemos obtener desde nuestros programas escritos en C#, no obstante, como iremos viendo código más adelante por ahora dejamos simplemente la información de los pasos seguidos dentro de una shell de mongo.

Como puede observar, en la imagen anterior, cada indice esta formado por un documento con tres claves, el nombre, el namespace y la clave del índice. En el caso de la figura anterior, el índice tiene por nombre “name” y aplica a la colección system.users en su clave “_Id” ( clave por defecto de todos los documentos si no se especifica su BsonId).

Con esto, podremos decir entonces, que las consultas sobre la colección system.users que utilicen un filtro u ordenación por “_id” utilizarán el índice adecuado. Para demostrarlo, realizaremos los siguientes pasos en nuestra shell.

pic2

En la consulta anterior, ni se filtra ni se ordena por nuestra clave de índice, por lo tanto, al igual que en cualquier base de datos relacional se realiza un table scan. Para verlo, podríamos utilizar un comando llamado explain, gracias al cual podríamos obtener un documento con la información del plan seguido, en el cual podremos ver desde el tiempo de ejecución hasta el cursor utilizado y el número de elementos escaneados.

 

Si variamos un poco la consulta, por ejemplo ordenando por la clave _id del documento y volvemos a mostrar la información del método explain, veremos como en este caso la cosa cambia.

 

pic3 Fíjese, en la imagen anterior, como ahora nuestra consulta utiliza un cursor BtreeCursor  en la clave _id y por lo tanto esta consulta, dependiendo de la densidad, podrá realizarse de una forma mucho más optima.

 

 

 

 

 

 

Crear nuevos indices, a mayores de los indices creados por defecto en cualquier colección sobre el elemento _id, es realmente sencillo, desde código podríamos usar algo como lo mostrado a continuación:

 

En este ejemplo de código, se establece un nuevo índice sobre las claves FirstName, de forma ascendente, y Age de forma descendente, a mayores, por supuesto, se establece una opción de no único. El mismo ejemplo desde la shell podría haberse escrito como db.Customers.ensureIndex({“FirstName”:1,”Age”:-1}).

Si una vez hecho esto, realizaramos por ejemplo la una consulta sobre el nombre de un cliente podríamos revisar la utilización o no del índice:

 

pic4

 

NOTA: Para aquellos que no os guste trabajar con una shell, os recomiendo probar el GUI MongoVue, tengo que agradecer desde aquí a Sergio Jimenez el conocimiento de su existencia.

 

En la siguiente entrada continuaremos viendo más sobre indices, explain de consultas, hint y algo de profiling…

 

Saludos

Unai

EF 4 + CTP 5 = CodeFirst : Mapping II

En la anterior entrada sobre mapping, exploramos distintas posiblidades acerca del mapeo con herencia dentro de esta última CTP. A lo largo de la siguiente entrada veremos dos nuevos casos, Entity Spliting y el uso de enumerados, algo que suele ser una pregunta desgraciadamente habitual.

Con el fin de mostrar un ejemplo de Entity Splitting, es decir, una entidad que se mapea a dos tablas, partiremos del siguiente ejemplo de entidad:

El objetivo, es hacer que esta entidad se mapee con dos tablas (una join de las mismas repesenta cada entidad ) llamadas Customers CustomersAddresses. Para ello, dentro de nuestro espacio de mapeo en Code First, este escenario no se puede hacer mediante anotaciones, podemos escribir las siguientes lineas.

 

Si probamos este código, o bien revisamos el diagrama de las tablas generadas, nos prodremos dar cuenta como por defecto las claves generadas para estas dos tablas son de tipo identity, y por lo tanto, no podremos garantizar las uniones e inserciones de los mismos. Para que este escenario funcione correctamente tendremos que eliminar este patron de generación para las claves. Para ello, podemos utilizar el atributo DatabaseGeneration o bien hacerlo por código.

Tal y como comentamos al principio de esta entrada terminarmos hablando del uso de los malditos enumerados dentro de una entidad. Aunque en realidad, Code First no introduce nada realmente nuevo al respecto. Si con nuestras entidades POCO/STE utilizábamos clases parciales para definir una propiedad con un enumerado, internamente manejaba el valor de una columna, en Code First el truco es idéntico, con la salvedad que no necesitamso una clase parcial porque podemos ignorar el mapping de cualquier propiedad ( con una anotación o bien mediante código en el model builder).

Supongamos que tenemos una entidad Order de la siguiente forma:

Si en esta propiedad, quisiéramos incluir un enumerado para marcar los tipos de prioridad del pedido, podríamos modificarla como sigue:

Lógicamente, Priority no debería ser un elemento a mapear, no sabríamos como, por lo tanto, tenemos que indicarle de alguna manera que cuando se realize el mapping de esta entidades este elemento sea ignorado. Para ello, podríamos hacerlo de las siguientes formas:

 

1º Usando el atributo NotMapped

2º En la configuración de nuestro model builder

  

 

Bueno, hasta aquí esta entrada, la verdad es que ahora mismo no se si habrá tercera parte de mapping, imagino que si pero………

Saludos

Unai

EF 4 + CTP 5 = Code First : Mapping I

En el post sobre la publicación de mi último libro, relacionado con ADO.NET EF 4, un compañero de comunidad Eduard Tomás, me hizo algunos comentarios acerca de la CTP 5 y lo que el consideraba que era realmente de interés. La verdad, es que los primeros 6 post sobre esta CTP 5 distaban un poco de lo que el recomendaba, por eso, aunque la verdad no tenía pensado escribir en exceso sobre esto he decidido al final hacerlo. Esta, y posteriores entradas se dedicarán a diferentes escenarios de mapeo de entidades. Empezaremos por la herencia, intentando mostrar las técnicas habituales y como se pueden representar estas dentro de un modelo relacional.

Herencia TPT

Para mostrar un ejemplo de herencia TPT partiremos del siguiente ejemplo, trivial por otra parte, de modelo de entidades:

Una vez que ya tenemos definida nuestra jerarquía, para realizar el mapeo TPT, que como ya sabrá consiste en disponer una tabla con los elementos comunes  y una tabla por cada uno de los tipos de la misma, solamente tendremos que especificar lo siguiente nuestro código de mapeo.

 

Por supuesto, de la misma forma que hemos hecho el mapeo en código también lo podemos hacer mediante atributos y las respectivas convenciones de las que hemos hablado en entradas anteriores. A continuación, vemos como usar el atributo Table para especificar la tabla a la que se tiene que mapear cada entidad.

Nota: En estos dos ejemplos la clave primaria de Product es de tipo identidad. Por defecto todas las claves de tipo entero llevan asociada la convención StoreGeneratedIdentityKeyConvention. Si no desea este comportamiento puede usar el atributo DatabaseGeneration o bien especificar en el mapeo de esta propiedad que se elimine este patrón de Identidad. A continuación se muestran tips para estas dos formas:

 

Herencia TPC

 

TPC al contrario que TPT no dispone los datos comunes de una jerarquía dentro de una tabla, en este caso, cada una de las tablas asociadas a la jerarquía contiene también los datos comunes. Este tipo de estrategias suelen dar mejor resultado cuando se consulta más habitualmente los datos especificos de las entidades que los comunes a un conjunto de ellas, puesto que se evita el join que hay que realizar en TPH.

 

Puesto que no hay datos comunes, entonces nuestra entidad Product, más arriba ahora no tendrá una materialización, por lo tanto la macaremos como abstract. Una vez hecho esto, mostramos como realizar la configuración de esta herencia.

 

Herencia TPH

 

Para TPH, herencia con un discriminador y una sola tabla, el mapeo es realmente sencillo. Por supuesto, sin necesidad de tener que incluir el discriminador dentro de nuestras entidades, algo que no es parte de las mismas. Partiendo del mismo ejemplo de entidades que en TPT, el mapeo se podría hacer de la siguiente forma:

 

NOTA: Fíjese que el nombre del discriminador es ProductType, elemento que no aparece en las entidades pero si se generara dentro de la tabla resultante.

 

Saludos

Unai

EF 4 + CTP 5 = Code First : Conventions III

En los dos entradas anteriores sobre convenciones, I y II, se han detallado tanto el significado como la jerarquía de las mismas, así como el funcionamiento real y su relación con Data Annotations. A lo largo de esta entrada trataremos de ver como crear una nueva convención, la cual además de fines formativos tenga validez en la realidad.

 

Los antecedentes

El mapeo por defecto de una relación uno a muchos en esta CTP tiene asociada una convención por la cual este tipo de relaciones tienen establecida la restricción de borrado en cascada. Así, si por ejemplo, partiéramos del siguiente código, podríamos ver en la base de datos la opción OnDelete Cascade para la relación entre Customer y Order.

Por supuesto, esta convención de borrado en cascada para las relaciones uno a muchos puede ser eliminada por código quitando la misma de la lista de convenciones como podemos ver a continuación:

 

El problema de esta solución es que requiere de un código adicional. Además, al quitar la convención estamos eliminando esta característica de todas las posibles relaciones.. Nuestro objetivo será el de crear un atributo de Data Annotations, similar al MaxLenght que nos sirva de convención y con el cual podrámos establecer si una propiedad de navegación llevará asociada una restricción en cascada o no.

La solución

El primer paso que realizaremos será el de definir nuestro atributo, recuerde de la anterior entrada que todas las convenciones de tipo AttributeConfigurationConvention<,,> llevan asociadas un atributo, y como, además, si necesitaramos un atributo distinto a los definidos en Data Annotations podemos crear el nuestro.

 

 

Una vez creado el atributo, pasaremos a crear nuestra convención. Puesto que la misma queremos que aplique a las relaciones tendremos que seleccionar PropertyInfo y NavigationPropertyConfiguration como los dos primeros elementos de nuestro genérico. A continuación podemos ver el código de nuestra nueva convención:

Por supuesto, esta convención hay que agregarla a la lista de convenciones:

Ahora, para utilizarla simplemente tenemos que decorar nuestras propiedades de navegación como sigue:

 

Espero que os resulte de interés

Saludos

Unai

EF 4 + CTP 5 = Code First : Conventions II

En el post anterior sobre convenciones en EF 4 + CTP 5 introdujimos al lector en el significado de las convenciones y como obtener y manejar la lista de las mismas presente de forma automática. A mayores, también expusimos los distintos contratos y clases base con las que las convenciones han sido creadas. De todos estos contratos base, en esta CTP, solamente tenemos uno de forma pública, es decir, solamente uno de estos contratos es el que debemos usar para crear nuestras propias convenciones.

Este contrato es AttributeConfigurationConvention, y tiene como firma principal de su clase lo siguiente:

Como se puede observar en esta firma, hay varios elementos fundamentales que nos permite expresar como va a ser nuestra convención y a que se va a aplicar. En primer lugar, el parámetro genérico TMemberInfo nos permitirá especificar a que tipo de elemento se aplica la convención, de tal forma que si este parámetro fuera un PropertyInfo la convención sería para alguna de las propiedades de una entidad y si fuera por ejemplo Type sería una convención relativa a la entidad en si misma, el nombre de la tabla po ejemplo. El segundo de los parámetros genéricos, TConfiguration, se refiere a los distintos  elementos de configuración que podemos aplicar, los contenidos dentro de la jerarquía de ConfigurationBase. Lógicamente puede ver toda la jerarquía con cualquier herramienta de reflexión, aquí exponemos algunos elementos interesantes que están contenidos dentro de la misma: StringPropertyConfiguration, DecimalPropertyConfiguration,ForeignKeyConstraintConfiguration, EntityTypeConfiguration.

El último de los elementos del genérico es TAttribute, cuya una restricción es que sea un Atributo.En realidad, este elemento está aquí para permitirnos asociar una convención a un atributo con el que podamos decorar nuestras clases. Fíjese que aquí es dónde entra una de las features que más llama la atención de esta y anteriores CTP que no es más ni menos que la integración con DataAnnotations. Esta integración con Data Annotations no es más que el soporte de los diferentes atributos que definen dentro de las convenciones, así, si en Data Annotations tenemos un atributo MaxLength entonces tendremos una convención que hará uso de este atributo, a continuación se muestra el código de una posible convención haciendo uso de este atributo:

 

Por supuesto, para ciertas convenciones necesarias no hay un atributo dentro de Data Annotations que nos permita hacer una similitud, como podría ser MaxLength, para estos casos, EntityFramework.dll, librería de esta CTP 5, extiende el conjunto de atributos de este namespace. Un ejemplo de ello pueden ser los atributos TableAttributeColumnAttribute.

Con esto acaba esta segunda entrega, en la siguiente, trataremos de explicar paso a paso los pasos para implementar una nueva convención, esta convención, trataremos que además de fines formativos sirva también en la vida real…..

 

Saludos

Unai

 

EF 4 + CTP 5 = Code First : Conventions I

Si buscamos en la RAE la definición de una convención  podremos ver como esta se puede definir como “Norma o práctica admitida tácitamente, que responde a precedentes o a la constumbre” así como “Ajuste y concierto entre dos personas o entidades”. En nuestro contexto, podríamos decir que una convención es una norma por defecto gracias a la cual no tendremos que realizar ciertas configuraciones de mapeo para nuestras entidades.

Si partiéramos de un ejemplo tan sencillo como el siguiente, y en el utilizaramos nuestro contexto o bien inicializaramos la base de datos con una llamada a CustomerContext.Database.CreateIfNotExist() podríamos ver como, sin necesidad de especificar configuración ninguna, se ha extraido el esquema de la base de datos.

 

Concrétamente, a patir de esta entidad Customer el esquema de la tabla generada es:

CREATE TABLE [dbo].[Customers](
    [CustomerId] [int] IDENTITY(1,1) NOT NULL,
    [FirstName] [nvarchar](4000) NULL,
    [LastName] [nvarchar](4000) NULL,
PRIMARY KEY CLUSTERED
(
    [CustomerId] ASC
)

Como habrá observado, EF ha interpretado que la propiedad CustomerId tiene que ser la clave primaria de la tabla a generar. Esto, es una convención, concretamente una convención que dice que si hay una propiedad igual al nombre de la clase pero terminada en Id o bien una propiedad de nombre Id esta será la clave primaria, otra convención especifica que si es de tipo entero la misma debe de establecerse como Identity. El nombre de la tabla también es una convención, que dice que se obtiene de pluralizar el nombre de la entidad. Y  así, más convenciones que podríamos seguir extrayendo de este sencillo ejemplo.

En esta CTP, la quinta ya, las convenciones está definidas por una jerarquía de subclases que implementan un contrato común de nombre IConvention, la simple definición de este contrato lo vemos en el siguiente fragmento de código

Debajo de IConvention, en ocasiones en varios niveles de jerarquía, tenemos los elementos que más nos importan a nosotros, concretamente AttributeConfigurationConvention, IDbConvention y IEdmConvention, puesto que estos  representan los contratos reales con los que se han implementado las distintas convenciones.

 

Antes de avanzar más profundamente en las convenciones, algo que haremos en siguientes entradas a esta, veremos como ver/modificar las convenciones que por defecto tenemos incluidas en cualquier elemento de tipo DbContext.

  

Si vieramos la salida de pantalla de un uso de CustomerContext podríamos observar las diferentes convenciones que tenemos incluidas por defecto.

 

Untitled

Por supuesto, esta colección de convenciones puede ser modificada, quitar convenciones que no deseamos o bien agregar nuevas convenciones. En el siguiente ejemplo de código eliminamos una convención que marca para las relaciones OneToMany la restricción en cascada para el borrado, llamada OneToManyCascadeDeleteConvention.

 

Hasta aquí hemos llegado con la primera parte de las convenciones, en posteriores capítulos hablaremos más profundamente sobre las mismas, viendo como se han creado asi como la manera de crear nuevas convecciones.

 

Saludos

Unai