【问题标题】:How can I make EF Core database first use Enums?如何让 EF Core 数据库首先使用 Enums?
【发布时间】:2017-05-30 12:28:18
【问题描述】:

我正在使用 EF Core 和数据库优先方法,使用“Scaffold-DbContext”命令生成我的 DbContext / Entities。

我如何指示 Scaffold-DbContext 某个 table 中的某个 field 应该生成使用 Enum 而不是 int 的代码?

这就是你过去在普通 EF 中的做法: https://www.devu.com/cs-asp/lesson-69-mapping-enum-types-entity-properties-framework-designer/

示例

此枚举已在代码中定义:

public enum StateEnum {
  Ok = 1,
  Fail = 2
}

这是 Scaffold-DbContext 给我的

public partial class Foo
{
    public int Id { get; set; }
    public int State { get; set; }
}

这就是我想要它创建的:

public partial class Foo
{
    public int Id { get; set; }
    public StateEnum State { get; set; }
}

【问题讨论】:

  • 目前没有办法在运行scaffold-dbcontext时创建枚举属性。这仅仅是因为,枚举存储为 int(或枚举在数据库中的基础类型),并且在构建模型时,EF 会查看元数据,因此没有关于列是 int 与 enum 的信息。在搭建脚手架后,您始终可以将属性类型从 int 更改为 enum,这样就可以正常工作了。
  • Does EF7 support enums?的可能重复

标签: c# entity-framework-core


【解决方案1】:

EF Core 2.1 中的值转换不能满足您现在的需要吗?

https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions

快速示例:

  entity.Property(e => e.MyEnumField)
            .HasMaxLength(50)
            .HasConversion(
                v => v.ToString(),
                v => (MyEnum)Enum.Parse(typeof(MyEnum),v))
                .IsUnicode(false);

【讨论】:

  • 这确实有效,但不考虑 OP 使用 Scaffold 命令的原始要求,该命令将在每次运行时覆盖 OnModelCreating 方法的内容。
【解决方案2】:

从 Entity Framework Core 2.1 开始,EF 支持 Value Conversions 专门解决需要将属性映射到不同类型进行存储的场景。

专门针对 Enums,您可以使用提供的 EnumToStringConverterEnumToNumberConverter

【讨论】:

  • 终于!谢谢!
  • 所有这些答案的问题在于它们将枚举存储为枚举父表的字符串或数字列。从数据库的角度来看,这是错误的。从数据库的角度来看,这个枚举应该作为 ID 字段存储在父表中,它通过外键引用查找表。
  • 我不同意。枚举是 CODE ,因此没有数据库表示,否则您可以简单地向数据库添加另一个值,这会使代码无效,因为该枚举值是未知的。
  • 但是,这里有一个很大的警告:“使用值转换可能会影响 EF Core 将表达式转换为 SQL 的能力。对于这种情况,将记录一个警告。正在消除这些限制考虑在未来发布”docs.microsoft.com/en-us/ef/core/modeling/value-conversions
  • Scaffold-DbContext 将创建/覆盖我的 DbContext,因此它会覆盖 OnModelCreating,您如何处理?
【解决方案3】:

我是因为问题标题才来到这里的。不确定这是否适用于“Scaffold-DbContext”,但它通过显式设置枚举的基本类型在 DbContext(Microsoft.EntityFrameworkCore 2.0.1.0)中有效,即使枚举元素的默认基础类型是 int。您也可以使用 Fluent API 设置默认值(尤其是枚举以 1 开头的情况)。

public enum StateEnum : int
{
    Ok = 1,
    Fail = 2
}

允许的枚举类型为 byte、sbyte、short、ushort、int、uint、long 或 ulong。

所以我认为这适用于其中任何一个。 enum (C# Reference)

public class MyDbContext : DbContext
{      
    protected override void OnModelCreating(ModelBuilder builder) 
    {
        builder.Entity<Foo>().Property(x => x.State).HasDefaultValue(StateEnum.Ok);
    }
}

【讨论】:

  • 我在我们的数据库中有一个遗留表,它使用一个小的 int 来表示一个枚举,所以我需要将枚举更改为从 short 继承:( MyType:short)以便 ef 核心转换它正确。谢谢!!
  • 或者,.HasConversion&lt;int&gt;(); 将工作,而不是 .HasDefaultValue(..)
【解决方案4】:

目前,EF core does not support enums。像这样的代码:

public class MyDbContext : DbContext
{      
    protected override void OnModelCreating(ModelBuilder builder) 
    {
        builder.Entity<StateEnum>(e => {...});
    }
}

不与消息一起编译:

CS0452 C# 类型必须是引用类型才能在泛型类型或方法中用作参数“TEntity”

解决方案:你可以使用enumeration classes instead

【讨论】:

    【解决方案5】:

    试试这个解决方案:

    public enum StateEnum {
          Ok = 1,
          Fail = 2
    }
    
    public partial class Foo
    {
        public int Id { get; set; }
        public int StateId { get; set; }
        public StateEnum State
        {
            get => (StateEnum)StateId;
            set => StateId = (int)value;
        }
    }
    

    【讨论】:

    • 你能补充一下吗?
    • 想法是保留 StateId 用于数据库代码映射(默认行为),但以用户友好的方式添加 State 枚举以使用 Foo 类。
    • 警告:这会导致内存过滤,因为 EF 无法将表达式转换为查询 WHERE 子句
    【解决方案6】:

    接受的答案并不能解决问题。问题指出 “我如何指示 Scaffold-DbContext 某个表中的某个字段应该生成代码以使用 Enum 而不是仅仅一个 int?” 很多答案都表明使用 Entity Framework Core 2.1,现在支持价值转换。如果您使用代码优先,而不是数据库优先,这将很有用。 Scaffold-DbContext 每次都会覆盖 DBContext。 就我个人而言,我对枚举是数据库中的整数没有任何问题。但我不想在代码中使用整数。

    你也可以

    1. 更改 DBContext 以使用 ValueConversion。每次搭建脚手架时,您都会丢失该代码。
    2. 将实体中的 Ints 更改为特定的 Enum。它工作得很好,但是当你脚手架时你也会失去它。
    3. 创建一个局部类,在其中创建一个名为 GetStateEnum() 和 SetStateEnum(StateEnum stateEnum) 等的方法。它非常冗长,但它会停留在脚手架之间。
    4. 有一种叫做 Source Generators 的东西可能会在未来解决这个问题。我还没有找到一个简单的解决方案。

    我选择了备选方案 2。它是迄今为止我发现的最简单的一个,并且使用 git-compare 恢复类非常容易。我可以在一分钟内更改我所有的枚举。

    如果有人有更好的解决方案。请告诉我。

    【讨论】:

      【解决方案7】:

      你可以让你枚举 (U) 和一个实体 (T) 代表数据库中的枚举值

      public static T[] BuildEntityObjectsFromEnum<T, U>() where U: Enum where T : new()
          {
              var listObjectsToReturn = new List<T>();
              Dictionary<string, int> dictionary = Enum.GetValues(typeof(U)).Cast<U>().ToDictionary(t => t.ToString(), t =>  Convert.ToInt32(t));
      
              foreach (var item in dictionary)
              {
                  var newObject = new T();
                  Type classType = typeof(T);
                  classType.GetProperties()[0].SetValue(newObject, item.Value); // Enum int id
                  classType.GetProperties()[1].SetValue(newObject, item.Key); // Enum string value
                  listObjectsToReturn.Add(newObject);
              }
              return listObjectsToReturn.ToArray();
          }
      

      然后你可以从枚举中为表播种

      modelBuilder.Entity<T>().HasData(BuildEntityObjectsFromEnum<T,U>());
      

      【讨论】:

        【解决方案8】:

        按照the accepted answer 中提供的link,我使用了代码优先方法的预定义转换,并且它使用Entity Framework Core 5:

        modelBuilder.Entity<Model>(model => {
            model.Property(m => m.EnumType)
                .HasConversion<int>();
        });
        

        【讨论】:

          猜你喜欢
          • 2023-03-27
          • 1970-01-01
          • 2013-10-11
          • 1970-01-01
          • 2018-09-05
          • 2020-05-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多