EF核心支持字段-将属性公开为另一种类型?

bit*_*001 6 c# entity-framework-core

假设我有一个EF实体类Person,上面有一个PhoneNumber。PhoneNumber以字符串类型存储,但是我希望对Person的所有访问都通过Phone进行,Phone具有一些不错的访问器功能,例如验证或GetAreaCode()。我想将它作为字符串备份在数据库中,但是当查询它时,我想将其作为PhoneNumber返回:

public class Person {
    public PhoneNumber Phone { /* Some clever get/set logic here */ }

    private string _phoneNumber; // Backing field
}
Run Code Online (Sandbox Code Playgroud)

还是可以让PhoneNumber将自身存储为字符串?如果我只是通过删除上面的后备字段将其包括在模型中,则EF会被构造函数(一个受保护的ctor,具有比一个字符串多的args)和一个副本ctor混淆PhoneNumber(PhoneNumber other)。我可以让EF以某种方式忽略这些吗?

我愿意接受...

nbr*_*osz 5

我发现可行的唯一方法是使用名称与您的支持字段不匹配的 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;
Run Code Online (Sandbox Code Playgroud)

然后,在DbContext的OnModelCreating方法中,告诉它在数据库表上创建一个充当后备属性的列:

        // backing properties
        modelBuilder.Entity<Fire>()
            .Property<string>("FireType")
            .HasField("_fireType")
            .UsePropertyAccessMode(PropertyAccessMode.Field);
Run Code Online (Sandbox Code Playgroud)

这样,我终于能够创建一个成功的迁移,使我可以在模型上拥有一个私有字段,在模型上拥有一个公共转换属性,并在数据库表中拥有一个正确命名的列。唯一的问题是,公共财产和私有字段不能共享相同的名称(不共享相同的类型),但是无论如何,这对您而言并非如此。


Cod*_*und 5

您可以使用@nbrosz的答案来解决问题,但是如果您使用的是EF Core 2.1,则不再需要执行这种解决方法。您可以使用EF Core 2.1(自2018年5月7日起在候选版本1中)摆脱支持字段,可以使用Microsoft 此处解释的值转换功能:

值转换器允许在读取或写入数据库时​​转换属性值。此转换可以是从一个值到相同类型的另一个值(例如,加密字符串),也可以是从一种类型的值到另一种类型的值(例如,将枚举值与数据库中的字符串进行来回转换)。

因此,对于您的情况,您只需删除后备字段即可。您不再需要它。您的课程应如下所示:

public class Person 
{
    public PhoneNumber Phone { /* Some clever get/set logic here */ }
}
Run Code Online (Sandbox Code Playgroud)

在您的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
        }
    );
Run Code Online (Sandbox Code Playgroud)

而已。实际上它是候选版本,但微软表示:

EF Core 2.1 RC1是一个“上线”版本,这意味着一旦测试您的应用程序可以与RC1一起正常使用,就可以在生产中使用它并获得Microsoft的支持,但是一旦可用,您仍然应该更新到最终的稳定版本。

我剩下的答案用于@nbrosz,因为您正在处理枚举类型。您可以删除背景字段,也可以是EF Core 2.1提供的许多内置值转换器之一。对于枚举到字符串值的转换,我们有类型EnumToStringConverter。对于您在回答中所做的逻辑,您可以将其简化为以下实体:

[Display(Name = "Fire Type")]
public Enums.FireType Type { get; set; }
Run Code Online (Sandbox Code Playgroud)

我们删除了该NotMapped属性的属性,并且没有逻辑y进行转换。

在您的OnModelCreating方法中,您可以执行以下操作:

var converter = new EnumToStringConverter<FireType>();

modelBuilder
    .Entity<Fire>()
    .Property(e => e.FireType)
    .HasConversion(converter);
Run Code Online (Sandbox Code Playgroud)

您还可以使用以下通用版本,让EF Core为您检测正确的转换器HasConversion<T>

modelBuilder
    .Entity<Fire>()
    .Property(e => e.FireType)
    .HasConversion<string>();
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢使用流利的配置,则可以使用以下Column数据注释样式,EF Core会为您进行转换:

[Column(TypeName = "nvarchar(20)")]
[Display(Name = "Fire Type")]
public Enums.FireType Type { get; set; }
Run Code Online (Sandbox Code Playgroud)

  • @MattJenkins,您可以使用“ValueConverterSelector”来解决这个问题。检查[这篇文章](https://andrewlock.net/strongly-typed-ids-in-ef-core-using-strongly-typed-entity-ids-to-避免-primitive-obsession-part-4/)了解更多细节。 (2认同)