值转换
值转换器允许在从数据库读取或写入数据库时转换属性值。这种转换可以从一个值转换到另一个相同类型的值(例如,加密字符串),也可以从一种类型的值转换到另一种类型的值(例如,将枚举值转换为数据库中的字符串,或者将枚举值转换为数据库中的字符串)。
1、基础知识
值转换器是根据ModelClrType(模型类型)和ProviderClrType(提供程序类型)指定的。模型类型是实体类型中属性的. net类型。提供程序类型是数据库提供程序所能理解的.net类型。例如,要将枚举保存为数据库中的字符串,模型类型是枚举的类型,提供者类型是String。这两种类型可以是相同的。
转换是使用两个Func表达式树定义的:一个从ModelClrType到ProviderClrType,另一个从ProviderClrType到ModelClrType。表达式树可以被编译到数据库访问代码中,以实现有效的转换。对于复杂的转换,表达式树可以是对执行转换的方法的简单调用。
2、配置一个值转换器
值转换是在DbContext的OnModelCreating属性上定义的。例如,考虑定义如下的枚举和实体类型:
public class Rider { public int Id { get; set; } public EquineBeast Mount { get; set; } } public enum EquineBeast { Donkey, Mule, Horse, Unicorn }
然后可以在OnModelCreate中定义转换(Conversion),将枚举值存储为字符串(例如,“Donkey”,“Mule”,…)在数据库中:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder .Entity<Rider>() .Property(e => e.Mount) .HasConversion( v => v.ToString(), v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v)); }
空值永远不会传递给值转换器。这使得转换的实现更容易,并允许它们在可为空和不可为空的属性之间共享。
3、值转换类 ValueConverter class
如上所示调用HasConversion将创建一个ValueConverter实例,并在属性上设置它。相反,可以显式地创建ValueConverter。例如:
var converter = new ValueConverter<EquineBeast, string>( v => v.ToString(), v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v)); modelBuilder .Entity<Rider>() .Property(e => e.Mount) .HasConversion(converter);
当多个属性使用相同的转换时,这很有用。目前还没有办法在一个地方指定给定类型的每个属性必须使用相同的值转换器。
4、内置转换器
EF Core 附带一组预定义的 ValueConverter
类,这些类位于 Microsoft.EntityFrameworkCore.Storage.ValueConversion
命名空间中。 这些是:
BoolToZeroOneConverter
-布尔值到一个0BoolToStringConverter
-Bool 到字符串(如 "Y" 和 "N")BoolToTwoValuesConverter
-布尔值到任意两个值BytesToStringConverter
-字节数组到 Base64 编码的字符串CastingConverter
-只需要类型强制转换的转换CharToStringConverter
-Char 到单字符字符串DateTimeOffsetToBinaryConverter
-DateTimeOffset 到二进制编码的64位值DateTimeOffsetToBytesConverter
-DateTimeOffset 到字节数组DateTimeOffsetToStringConverter
-DateTimeOffset 到字符串DateTimeToBinaryConverter
-DateTime 到64位值,包括 Datetimekind.utcDateTimeToStringConverter
-DateTime 到 stringDateTimeToTicksConverter
-DateTime 到计时周期EnumToNumberConverter
-枚举到基础数字EnumToStringConverter
-枚举到字符串GuidToBytesConverter
-Guid 到字节数组GuidToStringConverter
-Guid 到字符串NumberToBytesConverter
-任何数值到字节数组NumberToStringConverter
-任何数值到字符串StringToBytesConverter
-字符串到 UTF8 字节TimeSpanToStringConverter
-TimeSpan 到字符串TimeSpanToTicksConverter
-TimeSpan 到计时周期
请注意,EnumToStringConverter包含在这个列表中。这意味着不需要显式地指定转换,如上所示。相反,只需使用内置转换器:
var converter = new EnumToStringConverter<EquineBeast>(); modelBuilder .Entity<Rider>() .Property(e => e.Mount) .HasConversion(converter);
请注意,所有内置转换器都是无状态的,因此多个属性可以安全地共享单个实例。
5、预定义的转换
对于内置转换器存在的常见转换,不需要显式指定转换器。相反,只要配置应该使用哪种提供者类型,EF就会自动使用适当的内置转换器。上面的例子使用枚举到字符串的转换,但是如果提供者类型被配置,EF实际上会自动这样做:
modelBuilder .Entity<Rider>() .Property(e => e.Mount) .HasConversion<string>();
通过显式地指定列类型,也可以实现同样的目的。例如,实体类型是这样定义的:
public class Rider { public int Id { get; set; } [Column(TypeName = "nvarchar(24)")] public EquineBeast Mount { get; set; } }
然后enum值将作为字符串保存在数据库中,无需在onmodelcreate中进行任何进一步的配置。
6、限制
值转换系统存在一些已知的当前限制:
- 如上所述,
null
无法转换。 - 目前没有办法将一个属性转换为多个列,反之亦然。
- 使用值转换可能会影响 EF Core 将表达式转换为 SQL 的能力。 这种情况下会记录警告。 将来的版本将考虑删除这些限制。