【问题标题】:Why is Entity Framework resetting my enum to default value when saved为什么实体框架在保存时将我的枚举重置为默认值
【发布时间】:2022-02-23 15:20:04
【问题描述】:

我正在调试以下令人费解的行为。我有一个实体ExampleEntity,其中包含一个[Required] 枚举,出于迁移原因,它有一个默认值。插入新的ExampleEntity 时,我指定了该字段应采用的值。在调试器中检查显示该值已在 DbSet 的本地版本中正确设置。但是,每当我在上下文中调用 SaveChanges() 时,枚举值都会重置为配置的默认值。

模型:

public enum ExampleEnum
{
    Mecury,
    Venus,
    Earth,
    Mars
}

internal class ExampleEntity
{
    public int id { get; set; }

    [Required]
    public ExampleEnum Val { get; set; }
}

internal class EFContext : DbContext
{
    public EFContext(DbContextOptions options) : base(options)
    {
    }

    public virtual DbSet<ExampleEntity> Ents { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
       modelBuilder.Entity<ExampleEntity>(entity =>
       {
            entity.Property(e => e.Val).HasDefaultValue(ExampleEnum.Earth);
       });
    }
}

测试代码:

class Program
{
    private static DbConnection CreateInMemoryDatabase()
    {
        var connection = new SqliteConnection("Filename=:memory:");
        connection.Open();
        return connection;
    }

    static void Main(string[] args)
    {
        var ContextOptions = new DbContextOptionsBuilder<EFContext>()
                .UseSqlite(CreateInMemoryDatabase())
                .Options;

        using (var context = new EFContext(ContextOptions))
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
        }

        using (var db = new EFContext(ContextOptions))
        {
            var newEntity = new ExampleEntity
            {
               Val = ExampleEnum.Mecury,
            };
            db.Ents.Add(newEntity);
            db.SaveChanges();
        }

        using (var db = new EFContext(ContextOptions))
        {
            var savedEnt = db.Ents.First();
            Console.WriteLine(savedEnt.Val.ToString());
        }
    }
}

我希望打印的值是Mercury,但它会打印出配置的默认值Earth。我可以在保存更改后解决此问题,将 Val 设置为所需的值:

var newEntity = new ExampleEntity
{
   Val = ExampleEnum.Mecury,
};
db.Ents.Add(newEntity);
db.SaveChanges();
newEnttity.Val = ExampleEnum.Mecury;
db.SaveChanges()

【问题讨论】:

标签: c# entity-framework entity-framework-core


【解决方案1】:

问题是由使用默认值 Mecury (0) 引起的。 Entity Framework 大概无法区分未初始化的枚举和故意设置为 0 的枚举之间的区别,因此它假定前者并设置默认值。

可能最简单的解决方法是使用零(第一个枚举值)作为默认值。在这种情况下,将枚举重新排序为:

Earth
Mecury
Venus
Mars

【讨论】:

    【解决方案2】:

    重新排序枚举,这会在您决定更改代码中的默认值的那一刻产生一些问题,然后最终导致现有数据无效。相反,我建议始终对声明的枚举使用 NotSet/None 默认值:

    public enum ExampleEnum
    {
        NotSet = 0,
        Mecury,
        Venus,
        Earth,
        Mars
    }
    

    对于实体,我建议在您的实体属性上设置默认值。这样,新实体在被持久化之前和之后都会被初始化并处于有效状态:

    internal class ExampleEntity
    {
        public int id { get; set; }
    
        [Required]
        public ExampleEnum Val { get; set; } = ExampleEnum.Earth;
    }
    

    HasDefaultValue 只是告诉 EF 在未提供值时期望在列上设置 DefaultValue。在应用 CodeFirst 的地方,您指定的值是默认设置的值。考虑到删除HasDefaultValue 配置将允许您使用新行保存原始 Mercury (0) 值,它似乎确实引入了一个阻止程序来将属性更新为默认值 (0)。我在使用和不使用配置的情况下对此进行了测试:

    var test = new ExampleEntity { Val = ExampleEnum.Mars};
    test.Val = ExampleEnum.Mercury;
    

    这消除了忽略“设置”的 EF,因为枚举的类默认值仍然是 (0 [Mercury])。使用 Mercury = 0 的配置和枚举声明,上述测试仍然由于相同的原因而失败。删除默认配置,上述设置按预期工作。

    总的来说,我建议将枚举默认值设置为“NotSet”之类的值,然后在您的属性上使用初始化程序以获得所需的默认值,而 EF 只是担心底层架构。

    【讨论】:

      【解决方案3】:

      我们今天遇到了同样的问题。我们有一个不可为空的枚举,因此它是必需的,并且默认值是在实体配置中定义的。在某些情况下,传递给 DbContext 的值不是持久的,默认为 Enum.Default。我和我的同事们对这种行为感到困惑。它也变成了一个枚举列正在影响另一个枚举列的值,但我不会详细介绍它。

      builder.Property(_ =&gt; _.MyEnum).HasDefaultValue(Enum.Default).HasConversion&lt;string&gt;();

      对我们有用的是我们在删除默认值后添加了一个新的迁移。这解决了我们所有的枚举问题。

      builder.Property(_ =&gt; _.MyEnum).HasConversion&lt;string&gt;();

      我们最初需要将默认值应用于新列的现有记录,但由于 Code First 迁移连续运行,当第一次迁移运行时,它会将默认值添加到现有记录,然后我们删除默认值,因为向前移动它是一个必需的属性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多