假设我有一个EF实体类Person,上面有PhoneNumber。 PhoneNumber存储为字符串类型,但我希望Person上的所有访问都通过Phone,它具有一些很好的访问器功能,例如验证或GetAreaCode()
。我希望将它作为字符串备份在db中,但是在查询它时我想将其作为PhoneNumber返回:
public class Person {
public PhoneNumber Phone { /* Some clever get/set logic here */ }
private string _phoneNumber; // Backing field
}
或者我可以将PhoneNumber存储为字符串吗?如果我只是通过删除上面的支持字段将它包含在模型中,那么EF会被构造函数(受保护的ctor与一个字符串多一些args)以及复制ctor PhoneNumber(PhoneNumber other)
混淆。我可以让EF以某种方式忽略这些吗?
我对创意持开放态度......
答案 0 :(得分:4)
您可以使用@nbrosz的answer来解决您的问题,但如果您使用的是EF Core 2.1,则不再需要执行此类解决方法。您可以使用EF Core 2.1(自2018年5月7日起在Release Candidate 1中)删除支持字段,您可以使用Microsoft解释的值转换功能here:
值转换器允许在读取时转换属性值 来自或写入数据库。此转换可以来自一个值 到另一个相同类型(例如,加密字符串)或从 一种类型的值到另一种类型的值(例如, 将枚举值转换为数据库中的字符串。)
因此,对于您的情况,您可以删除支持字段。你不再需要它。你的课应该是这样的:
public class Person
{
public PhoneNumber Phone { /* Some clever get/set logic here */ }
}
在您的OnModelCreating
方法中,您可以按如下方式配置转换:
modelBuilder.Entity<Person>()
.Property(p => p.Phone)
.HasConversion(
phone => {
// Here you code the logic of how to get the value to store in DB
return ...;
},
dbValue => {
// Here you code the logic of how to construct the PhoneNumber instance from the value to store in DB
}
);
就是这样。实际上它是候选版本,但微软说:
EF Core 2.1 RC1是一个“go live”版本,这意味着一旦你测试了它 您的应用程序与RC1正常工作,您可以使用它 制作并获得微软的支持,但你仍然应该 一旦可用,就更新到最终的稳定版本。
我的答案的其余部分是 @nbrosz ,因为您正在处理枚举类型。您可以删除支持字段,也可以使用EF Core 2.1提供的众多内置值转换器之一。对于枚举到字符串值转换,我们使用类型EnumToStringConverter
。对于您在答案中所做的逻辑,您可以将其简化为实体:
[Display(Name = "Fire Type")]
public Enums.FireType Type { get; set; }
我们删除了该属性上的NotMapped
属性,并且没有用于转换的逻辑y。
在OnModelCreating
方法中执行此操作:
var converter = new EnumToStringConverter<FireType>();
modelBuilder
.Entity<Fire>()
.Property(e => e.FireType)
.HasConversion(converter);
您也可以使用下面的HasConversion<T>
的通用版本让EF Core为您检测到正确的转换器:
modelBuilder
.Entity<Fire>()
.Property(e => e.FireType)
.HasConversion<string>();
如果您不想使用流畅的配置,可以使用Column
数据注释属性,如下所示,EF Core将为您进行转换:
[Column(TypeName = "nvarchar(20)")]
[Display(Name = "Fire Type")]
public Enums.FireType Type { get; set; }
答案 1 :(得分:3)
我发现可行的唯一方法是创建一个公共属性,其中getter / setter 的名称与您的支持字段不匹配,并将其标记为NotMapped,如下所示:
[NotMapped]
[Display(Name = "Fire Type")]
public Enums.FireType Type
{
get
{
Enums.FireType type;
if (!Enum.TryParse(_fireType, out type))
type = Enums.FireType.Fire; // default
return type;
}
set
{
_fireType = value.ToString();
}
}
private string _fireType;
然后在DbContext的OnModelCreating方法中,告诉它在数据库表上创建一个类似于后备属性的列:
// backing properties
modelBuilder.Entity<Fire>()
.Property<string>("FireType")
.HasField("_fireType")
.UsePropertyAccessMode(PropertyAccessMode.Field);
有了这个,我终于能够创建一个成功的迁移,允许我在我的模型上拥有一个私有字段,在模型上有一个公共变换属性,在我的数据库表中有一个正确命名的列。唯一的问题是公共财产和私人领域不能共享相同的名称(没有共享相同的类型),但无论如何,情况并非如此。