viernes, 29 de julio de 2011

Entity Framework: Actualizar modelo desde base de datos


El asistente que aparece cuando pulsamos la opción ‘Actualizar modelo desde base de datos’ es la manera que tenemos en Entity Framework de actualizar el modelo a partir de los cambios realizados en la base de datos subyacente.

Después de realizar pruebas sobre qué cambios refleja automáticamente y cuáles no, mi conclusión y opinión es que sólo lleva a cabo de forma automática aquellas tareas que son seguras y no romperán la compatibilidad con nuestro código. Es decir, al fin y al cabo, EF no pretende ser más que un mapeador de modelos físicos (bases de datos) y modelos conceptuales (modelos del dominio). La relación 1:1 entre el modelo físico y el modelo conceptual podría o no existir en función del modelo que hayamos diseñado. Siendo así, EF no quiere meterse en problemas y la mayoría de decisiones que hay tomar cuando se detectan cambios en el modelo físico, recaen en manos del programador.

A continuación se exponen algunas de las situaciones que normalmente sucederán en una base de datos en la que se apoya un desarrollo.

Se agrega una nueva tabla

Se debe seleccionar la nueva tabla en la pestaña ‘Agregar’ del ‘Asistente para actualizar’.

Se agrega una nueva columna en una tabla

Se agrega automáticamente una nueva propiedad mapeada a la nueva columna en la entidad.

Hay que fijarse que la nueva propiedad se agrega como última propiedad de la entidad. Además, no podemos ordenar las propiedades en la entidad desde el diseñador ni desde la ventana ‘Detalles de la asignación’ (en VS2012 ya sí, tenemos menú contextual – Mover propiedades). Por supuesto, tampoco funciona cortar y pegar porque cuando se pega, las propiedades también se anexan al final de la entidad. Si por algún motivo, que las propiedades de la entidad no estén ordenadas nos quita el sueño, la única solución parece ser editar manualmente el fichero edmx y en concreto la sección CSDL y la ordenación de elementos Property dentro del elemento EntityType correspondiente.

En general, no se recomienda editar manualmente el fichero edmx, pero hay situaciones en las que no hay más remedio (no así en ésta última, que al fin y al cabo, es sólo un motivo estético). En cualquier caso, si editamos el fichero edmx, podría darse la situación de que el diseñador visual no funcionara y entonces y a partir de ese momento, ya todos los cambios tendríamos que realizarlos a mano sobre el fichero edmx.

Se agrega una nueva tabla sin PK

En EF todas las entidades tienen que tener una clave definida, así que si durante el proceso de actualización EF se encuentra una tabla sin PK, determinará que todas las columnas que no acepten nulos serán la clave primaria en nuestra entidad. Puedes visitar el siguiente enlace donde se habla de ello.

Si desde el diseñador y para la propiedad ‘Clave de entidad’, la establecemos a False para todas las propiedades de un entidad, obtendremos el siguiente error:

image

Por ello, parece que es innegociable que una entidad tenga definida una clave.

Se elimina una columna de una tabla

La propiedad asociada en la entidad no se elimina y tenemos el error ‘La propiedad <NombrePropiedad> no está asignada’. Siendo así, hay que eliminar manualmente la propiedad de la entidad.

Se cambia el nombre de una columna en una tabla

Esto provoca que se agregue una nueva propiedad a la entidad con el nuevo nombre de la columna. Por otro lado, la antigua propiedad de la entidad que estaba mapeada al nombre antiguo aún persiste y tenemos el error ‘La propiedad <NombrePropiedad> no está asignada’. Al igual que en el caso anterior, hay que eliminar manualmente la propiedad de la entidad.

Se cambia la PK de una tabla

El modelo de base de datos (SSDL) sí refleja el cambio, pero el modelo conceptual (CSDL) y el mapeo (CDL) no reflejan ningún cambio. Siendo así, la entidad aún tiene como clave principal el mapeo original y este cambio tendrá que ser realizado de forma manual en el modelo.

En cualquier caso, EF nos informa de ello con este error ‘Todas las propiedades (<Entidad>.<AntiguoCampoPK>) de EntitySet <Entidad> se deben asignar a las propiedades de clave (<Entidad>.<NuevoCampoPK>) de la tabla <Entidad>.’.

Se elimina una tabla

Esto conlleva que se elimina su información en la sección SSDL (Storage) y MSL (Mapping) pero no se elimina la entidad (sigue estando en la sección CSDL). Eliminar la entidad es responsabilidad nuestra y tendrá que ser realizado de forma manual.

Se cambia el nombre de una tabla

En realidad esto se traduce en que la pestaña ‘Eliminar’ del ‘Asistente para actualizar’, eliminará la tabla con el nombre antiguo (aplicando la misma lógica que el punto anterior ‘Se elimina una tabla’) y que además deberíamos seleccionar la nueva tabla (perdón, quise decir misma tabla con distinto nombre) en la pestaña ‘Agregar’. Finalmente, bien eliminamos la entidad que estaba mapeada al antiguo nombre de tabla o bien volvemos a mapear esta entidad a la nueva tabla.

Se cambia el tipo de una columna o si admite o no nulos.

En ambos casos, la entidad no refleja cambio alguno y tendremos que hacer estos cambios de forma manual.

La verdad es que tenemos que estar bastante al tanto de todos los cambios y llevar al día el modelo relacional con el modelo conceptual. Nada mas, sólo que espero poco a poco ir publicando posts relacionados con Entity Framework porque eso será señal inequívoca de que avanzo en su estudio y uso en mis desarrollos.

Un saludo!

miércoles, 27 de julio de 2011

Equivalencia de tipos SQL Server 2008 R2 en Entity Framework


Si hoy en día no utilizas ningún ORM es que no vives en este mundo. Por supuesto, yo NO vivo en este mundo, así que para declararme oficialmente ciudadano, hemos optado por utilizar ADO.NET Entity Framework. Auguro que el camino será largo y lleno de escollos que salvar, pero seguro que cuando lleguemos al final, todas nuestras aventuras no habrán sido en balde.

Para comenzar con EF, y puesto que aún estamos en periodo de aprendizaje, simplemente veremos la equivalencia de tipos que realiza EF entre los tipos disponibles en SQL Server Express 2008 R2 y los tipos .NET.

Recuerda que en este otro post, también tenemos las equivalencias entre los tipos de SQL y el proveedor de ADO.NET SqlClient (System.Data.SqlClient.SqlDbType) y también para la factoría de proveedores (System.Data.DbType).

Lo primero que necesitamos es una tabla con columnas para todos los tipos de datos disponibles en SQL.

CREATE TABLE [dbo].[development_DataTypes](

            [Cbigint] [bigint] NOT NULL,

            [Cbinary] [binary](50) NULL,

            [Cbit] [bit] NULL,

            [Cchar] [char](10) NULL,

            [Cdate] [date] NULL,

            [Cdatetime] [datetime] NULL,

            [Cdatetime2] [datetime2](7) NULL,

            [Cdatetimeoffset] [datetimeoffset](7) NULL,

            [Cdecimal] [decimal](18, 0) NULL,

            [Cfloat] [float] NULL,

            [Cgeography] [geography] NULL,

            [Cgeometry] [geometry] NULL,

            [Chierarchyid] [hierarchyid] NULL,

            [Cimage] [image] NULL,

            [Cint] [int] NULL,

            [Cmoney] [money] NULL,

            [Cnchar] [nchar](10) NULL,

            [Cntext] [ntext] NULL,

            [Cnumeric] [numeric](18, 0) NULL,

            [Cnvarchar] [nvarchar](50) NULL,

            [Cnvarcharmax] [nvarchar](max) NULL,

            [Creal] [real] NULL,

            [Csmalldatetime] [smalldatetime] NULL,

            [Csmallint] [smallint] NULL,

            [Csmallmoney] [smallmoney] NULL,

            [Csql_variant] [sql_variant] NULL,

            [Ctext] [text] NULL,

            [Ctime] [time](7) NULL,

            [Ctimestamp] [timestamp] NULL,

            [Ctinyint] [tinyint] NULL,

            [Cuniqueidentifier] [uniqueidentifier] NULL,

            [Cvarbinary] [varbinary](50) NULL,

            [Cvarbinarymax] [varbinary](max) NULL,

            [Cvarchar] [varchar](50) NULL,

            [Cvarcharmax] [varchar](max) NULL,

            [Cxml] [xml] NULL,

 CONSTRAINT [PK_development_DataTypes] PRIMARY KEY CLUSTERED

(

            [Cbigint] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

 

A continuación, lo primero que observamos es que hay algunos tipos de SQL que no están admitidos por EF. Estos tipos son:

·         geograhpy

·         geometry

·         hierarchyid

·         sql_variant

clip_image002

Para el resto de tipos, aquí está la equivalencia:

SQL Server Express 2008 R2

.NET

bigint

Int64

binary

Binary

bit

Boolean

char

String

date

DateTime

datetime

DateTime

datetime2

DateTime

datetimeoffset

DateTimeOffSet

decimal

Decimal

float

Double

geography

N/A

geometry

N/A

hierarchyid

N/A

image

Binary

int

Int32

money

Decimal

nchar

String

ntext

String

numeric

Decimal

nvarchar

String

nvarchar(max)

String

real

Single

smalldatetime

DateTime

smallint

Int16

smallmoney

Decimal

sql_variant

N/A

text

String

time

Time

timestamp

Binary

tinyint

Byte

uniqueidentifer

Guid

varbinary

Binary

varbinary(max)

Binary

varchar

String

varchar(max)

String

xml

String

 

Un saludo!.

martes, 26 de julio de 2011

Genéricos en VB.NET

Hace poco escribí un post sobre colecciones genéricas, por ejemplo Dictionary(Of TKey, TValue), también utilizo asiduamente tipos anulables con el formato Nullable (Of T)… y sin embargo, tengo la sensación de que no tengo excesivamente claro que son los genéricos en .NET. Es decir, utilizo intuitivamente las colecciones genéricas (es algo que una vez lo descubres ya no puedes volver a las no genéricas), los tipos anulables, pero el mundo “genérico” de .NET es mucho más que sólo colecciones y tipos anulables.

Los genéricos (Generics en inglés) permiten declarar clases, interfaces y métodos cuyo tipo de dato es genérico (el famoso Of T o <T>, dependiendo de VB.NET o C#).

De este modo, será el programador cuando utilice la clase quien especifique que tipo de dato en concreto va a utilizar para la clase, interface o método.  Siendo así, tenemos un único código que puede ser utilizado por distintas tipos de clases según decida el programador cuando haga uso del mismo.

Algunas de las ventajas de usar genéricos es que en tiempo de compilación se validan los tipos, es decir, si declaro un Nullable (Of Int32) sólo podré almacenar enteros y además evitaremos castings innecesarios (porque conocemos el tipo y sólo pudimos guardar instancias de ese tipo). Por otro lado, las instancias se guardan en su tipo original, no hay boxing y unboxing porque no se utiliza el tipo Object sino el tipo concreto que hemos especificado.

Por ejemplo, podemos definir nuestra propia clase genérica

Public Class Saludador(Of T)


En esta clase podemos definir métodos que reciben parámetros del tipo genérico

Public Sub Saludar(ByVal saludo As T)


Después, y cuando ya estemos usando la clase, crearemos una instancia con un tipo concreto.

Dim s As New Saludador(Of Integer)

s.Saludar(5)

 

clip_image002

clip_image004

clip_image005

También tenemos métodos genéricos. En estos métodos no sólo se puede recibir un parámetro del tipo de la clase genérica (T), sino otros muchos parámetros genéricos (T1, T2, etc.). El método de declararlos es el siguiente:

Sub|Funcion <Nombre>(Of T, T1, T2, etc.) (ByVal <Nombre> AS T, ByVal <Nombre> AS T1, ByVal <Nombre> AS T2, etc.)

Public Class Saludador(Of T)

 

    ' Propiedad genérica que será tipada al tipo de datos de la clase durante su instanciación

    Property Propiedad1 As T

 

    ' Método genérico que recibe 3 parámetros de tipo genérico y un tipo no genérico (Boolean)

    Public Sub Saludar(Of T1, T2, T3)(ByVal a As T1, ByVal b As T2, ByVal c As T3, ByVal d As Boolean)

 

    End Sub

End Class

 

clip_image006

clip_image007

Las ventajas de los genéricos ya las hemos comentado antes pero, en mi opinión, creo que lo más reseñable es que permiten crear código reutilizable fuertemente tipado, y es justamente este “fuertemente tipado” quien nos da la seguridad de no estar guardando tipos no admitidos, nos da intellisense, nos evita el boxing y unboxing, etc.

Cabe mencionar que a nivel personal sólo he utilizado los genéricos como parte de las colecciones genéricas, pero odio encontrar código de terceros que no puedo leer, así que este post lo que pretendía era arrojar un poco de luz sobre los genéricos y por supuesto no sentar cátedra ;-)

Un ejemplo muy clarificador de cómo utilizar genéricos en el mundo real se puede encontrar en http://geeks.ms/blogs/jmaguilar/archive/2008/12/08/m-233-todos-gen-233-ricos-en-vb-net.aspx

Un saludo!