martes, 28 de mayo de 2019
Otra característica bastante interesante de Entity Framework Core es que podemos introducir lógica personalizada a la hora de cargar el valor de las propiedades a partir de los datos obtenidos desde el almacén, y viceversa, usando los conversores de valores, o value converters.
Esta capacidad abre posibilidades bastante interesantes,que no eran tan inmediatas (o en algunos casos, ni siquiera posibles de forma directa) en versiones "clásicas" de Entity Framework, o EF Core anterior a 2.1. Gracias a ella podremos, por ejemplo, tener en nuestra entidad una propiedad de tipo enum mapeada a una cadena de caracteres en el almacén de datos, o introducir cualquier lógica de transformación, como podría ser la desencriptación y encriptación de valores, a la hora de leer y persistir información.
Por ejemplo, supongamos que tenemos una entidad con una propiedad booleana que, por el motivo que sea, está almacenada en la base de datos en forma de cadena, con valores "SI" y "NO". Este sería un caso en el que los value converters nos vendrán de perlas :)
Para configurar esta transformación, podemos añadir a
Asimismo, estas transformaciones serán tenidas en cuenta al realizar consultas. Fijaos en el siguiente ejemplo, donde se puede observar cómo desde LINQ usamos la propiedad de la entidad para establecer un filtro que muy hábilmente es transformado por EF para adaptarlo a los datos que realmente están siendo almacenados:
Dado que existen necesidades de conversión que son muy frecuentes, el propio EF Core trae de serie un buen conjunto de conversores predefinidos. Por ejemplo, uno de ellos,
Aquí, implícitamente estaremos utilizando
¡Esto es todo de momento! Espero que lo visto aquí haya sido útil para daros una idea de lo que podemos hacer con esta característica de EF Core. Y si queréis leer más, os recomiendo echar un vistazo a la documentación oficial.
Publicado en Variable not found.
Esta capacidad abre posibilidades bastante interesantes,que no eran tan inmediatas (o en algunos casos, ni siquiera posibles de forma directa) en versiones "clásicas" de Entity Framework, o EF Core anterior a 2.1. Gracias a ella podremos, por ejemplo, tener en nuestra entidad una propiedad de tipo enum mapeada a una cadena de caracteres en el almacén de datos, o introducir cualquier lógica de transformación, como podría ser la desencriptación y encriptación de valores, a la hora de leer y persistir información.
¡Bienvenidos, value converters!
Los value converters son clases donde se especifica la lógica de conversión de valores entre la propiedad de la entidad y el valor que procede del almacén de datos y viceversa, en forma de árboles de expresión.Por ejemplo, supongamos que tenemos una entidad con una propiedad booleana que, por el motivo que sea, está almacenada en la base de datos en forma de cadena, con valores "SI" y "NO". Este sería un caso en el que los value converters nos vendrán de perlas :)
Para configurar esta transformación, podemos añadir a
OnModelCreating()
un código como el siguiente, donde usamos el método HasConversion()
sobre la propiedad de la entidad para suministrarle dos árboles de expresión, el primero para transformar desde el valor de la entidad al esperado por el almacén, y luego para hacerlo en sentido contrario:public class MyContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Friend>()
.Property(f => f.IsGoodFriend)
.HasConversion(
value => value ? "SI" : "NO", // (1) Entity-->Provider conversion
value => value == "SI" // (2) Provider-->Entity conversion
);
...
}
}
De esta forma, cuando el valor de la propiedad IsGoodFriend
vaya a ser enviado al almacén de datos, se transformará usando la primera expresión, mientras que para establecer dicha propiedad partiendo del valor almacenado, se utilizará la segunda.Asimismo, estas transformaciones serán tenidas en cuenta al realizar consultas. Fijaos en el siguiente ejemplo, donde se puede observar cómo desde LINQ usamos la propiedad de la entidad para establecer un filtro que muy hábilmente es transformado por EF para adaptarlo a los datos que realmente están siendo almacenados:
Consulta LINQ:
==============
var goodFriends = ctx.Friends.Where(f=>f.IsGoodFriend).ToList();
SQL Generado:
=============
SELECT [f].[Id], [f].[IsGoodFriend], [f].[Name]
FROM [Friends] AS [f]
WHERE [f].[IsGoodFriend] = N'SI'
Ojo, hay que tener cuidado con esto: el uso de expresiones no reconocidas por el proveedor a la hora de especificar las conversiones podría dar lugar a la evaluación en cliente, y ya sabemos que esto puede causar problemas.
Value converters reutilizables
Otra forma más clara y reutilizable de especificar la conversión de valores es utilizando la claseValueConverter<TModel, TProvider>
, donde TModel
es el tipo de dato usado en la entidad .NET, mientras que TProvider
es el tipo usado por el proveedor del almacén de datos. Así, nuestro ejemplo anterior podría reescribirse de esta forma:var boolToSiNo = new ValueConverter<bool, string>(
value => value ? "SI" : "NO", // Entity-->Provider conversion
value => value == "SI" // Provider-->Entity conversion
);
builder.Entity<Friend>()
.Property(f => f.IsGoodFriend)
.HasConversion(boolToSiNo);
builder.Entity<Friend>()
.Property(f => f.IsFamily)
.HasConversion(boolToSiNo); // Reuse the same converter
Un aspecto interesante es que los valores nulos nunca serán convertidos a través de este mecanismo, por lo que no tendremos que tenerlos en cuenta en su implementación. Esto es así porque se utilizan los mismos conversores tanto para propiedades tipo valor como sus correspondientes anulables (por ejemplo, una propiedad int
o int?
).Dado que existen necesidades de conversión que son muy frecuentes, el propio EF Core trae de serie un buen conjunto de conversores predefinidos. Por ejemplo, uno de ellos,
BoolToStringConverter<TProvider>
, podría habernos sido útil en el ejemplo anterior:var converter = new BoolToStringConverter("NO", "SI");
builder.Entity<Friend>()
.Property(f => f.IsGoodFriend)
.HasConversion(converter);
En el espacio de nombres Microsoft.EntityFrameworkCore.Storage.ValueConversion
podemos ver muchos conversores más:BoolToStringConverter
BoolToTwoValuesConverter
BoolToZeroOneConverter
BytesToStringConverter
CastingConverter
CharToStringConverter
DateTimeOffsetToBinaryConverter
DateTimeOffsetToBytesConverter
DateTimeOffsetToStringConverter
DateTimeToBinaryConverter
DateTimeToStringConverter
DateTimeToTicksConverter
EnumToNumberConverter
EnumToStringConverter
GuidToBytesConverter
GuidToStringConverter
NumberToBytesConverter
NumberToStringConverter
StringToBoolConverter
StringToBytesConverter
StringToCharConverter
StringToDateTimeConverter
StringToDateTimeOffsetConverter
StringToEnumConverter
StringToGuidConverter
StringToNumberConverter
StringToTimeSpanConverter
TimeSpanToStringConverter
TimeSpanToTicksConverter
Aquí, implícitamente estaremos utilizando
EnumToStringConverter
para que FriendType
pueda ser guardado como cadena en el almacén:public class Friend
{
...
[Column(TypeName = "nvarchar(10)")]
public FriendType Type { get; set; }
}
En este ejemplo, en cambio, indicamos en la definición del modelo que la propiedad Type
la queremos almacenar en un int
, lo que provocará que sea utilizado el conversor EnumToNumberConverter
:modelBuilder
.Entity<Friend>()
.Property(f => f.Type)
.HasConversion<int>();
Si tenéis curiosidad, la lógica de selección de conversores por defecto podéis verla en GitHub.¡Esto es todo de momento! Espero que lo visto aquí haya sido útil para daros una idea de lo que podemos hacer con esta característica de EF Core. Y si queréis leer más, os recomiendo echar un vistazo a la documentación oficial.
Publicado en Variable not found.
Patrocinador
Oferta de empleo: Desarrollador web .NET (Barcelona / posibilidad en remoto)
Si tienes más de dos años de experiencia desarrollando aplicaciones web ASP.NET/.NET Core y te gustan los retos tecnológicos, ven a trabajar con
nosotros a un proyecto de escala global que está revolucionando la atención al cliente en plataformas e-commerce.
Aún no hay comentarios, ¡sé el primero!
Enviar un nuevo comentario