转换
值转换器允许在从数据库读取或写入数据库时转换属性值。这种转换可以从一个值转换到另一个相同类型的值(例如,加密字符串),也可以从一种类型的值转换到另一种类型的值(例如,将枚举值转换为数据库中的字符串,或者将枚举值转换为数据库中的字符串)。
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.utc
DateTimeToStringConverter
-DateTime
到string
DateTimeToTicksConverter
-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 的能力。 这种情况下会记录警告。 将来的版本将考虑删除这些限制。