【问题标题】:How to save enum in database as string如何将数据库中的枚举保存为字符串
【发布时间】:2015-12-09 03:07:03
【问题描述】:

这是我的模型类,我们有一个类型,可以是僵尸或人类。

public class User
{
    public int ID { get; set; }
    public string Name { get; set; }
    public Type Type { get; set; }
    public List<Weapon> WeaponsInList { get; set; }
}  

public enum Type
{   
    [Description("Zombie")]
    Zombie,
    
    [Description("Human")]
    Human
}

目前,它正在将数据保存在 Int 中。

我想将数据保存为 Human 和 Zombie,而不是 int。

【问题讨论】:

  • 枚举是一个静态对象,将它保存到数据库中没有任何意义...实际上您应该在数据库中定义项目,然后使用 T4 模板在代码中生成您的枚举。跨度>
  • 一个枚举值一个int。你真的应该这样保存它。除非您有非常好的理由将其作为字符串存储在数据库中。我想您没有。
  • @Pierre-LucPineault :将其存储为 int 不危险吗?不是所有的人都需要做的就是重新排序枚举,然后你的数据库中的所有值都指向错误的枚举而没有任何警告
  • @Diskdrive 您可以为您的枚举分配一个特定的整数,这样即使重新排序它也不会改变(通常是 2 的幂,因此您可以将其声明为“标志”)。但通常你只是不会为了好玩而重新排序枚举。
  • @Pierre-LucPineault,Diskdrive 指的是一个真正的问题。枚举可以出于有趣以外的原因进行安排,无论是有效的还是偶然的。这是存储字符串的更强大的解决方案;那些不会改变。但如果他们这样做了,不会丢失任何数据,只需要更新数据库即可。另外,以这种方式查看表格更有效率,并且无需对值进行编号,这对非标志枚举很好。事实上,这就是 xml 序列化的工作方式。所以,至少有一些 MS 开发者同意 Diskdrive。

标签: c# .net entity-framework c#-4.0


【解决方案1】:

您可以将枚举作为字符串保存到数据库中,我同意 dotctor 的观点,这不是最好的主意,但如果需要,您需要进行一些更改。

public class User
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<Wepon> WeposInList { get; set; }

    [Column("Type")]
    public string TypeString
    {
       get { return Type.ToString(); }
       private set { Type= value.ParseEnum<Type>(); }
    }

    [NotMapped]
    public Type Type { get; set; }
}  

将此扩展类添加到您的项目中。

public static class StringExtensions
{
    public static T ParseEnum<T>(this string value)
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
}

详细信息在这里 - http://NoDogmaBlog.bryanhogan.net/2014/11/saving-enums-as-strings-with-entity-framework/

【讨论】:

  • 我将如何使用它来跟踪数据库中的标签和 id?获取记录时出现空参数异常。
  • 尝试删除 [NotMapped] 并将其设置为数据库中的其他列
  • OP 问题的唯一实际答案。 :)
  • 这不会违反 SOLID 吗?通过这样做,您的实体不仅可以描述您的数据,还可以描述业务。我要做的是有一个枚举,我会将其播种到数据库中,然后从那里引用它。
【解决方案2】:

在 Entity Framework Core 中,您可以指定内置转换:

modelBuilder
    .Entity<DataSet>()
    .Property(d => d.SemanticType)
    .HasConversion(new EnumToStringConverter<DataSetSemanticType>());

更多详情here.

【讨论】:

  • 这仅在 EF 内核中可用。
  • 这个解决方案非常优雅并且完美运行。谢谢!
  • 我有一个 .NET Core 3.1,使用 HasConversion 时出现此构建错误 错误 CS1061 'PrimitivePropertyConfiguration' 不包含 'HasConversion' 的定义并且没有可访问的扩展方法 'HasConversion' 接受第一个参数可以找到“PrimitivePropertyConfiguration”类型的(您是否缺少 using 指令或程序集引用?)
  • 使用.HasConversion&lt;string&gt;();
【解决方案3】:

据我记忆,我遇到过这个问题,老实说,我不知道为什么 MS 不添加这个功能(NH 可以像往常一样做......)。

无论如何,我通常做的是使用 const 字符串类,例如:

public static class MyEnum
{
    public const string Foo = "Foo";
    public const string Bar = "Bar";
}

public class Client
{

    public string MyVal { get; set; }

    public Client()
    {
        MyVal = MyEnum.Bar;
    }

}

缺点 - 尽可能简单。

缺点 - 你会松散类型检查(尽管它可以通过编程方式强制执行)。


所以这一次我试着想一些更雄心勃勃的东西。所以我采用了 Brian 描述的概念(当给定的枚举在整个域中广泛使用时,它有一些缺点)。好吧..我得到了以下工作:

用于存储值的基本组件类:

[ComplexType]
public class DbEnum<TEnum>
{
    public string _ { get; set; }

    public DbEnum()
    {
        _ = default(TEnum).ToString();
    }

    protected DbEnum(TEnum value)
    {
        _ = value.ToString();
    }

    public TEnum ToEnum()
    {
        return _.ToEnum<TEnum>();
    }

    public static implicit operator DbEnum<TEnum>(TEnum value)
    {
        return new DbEnum<TEnum>(value);
    }

    public static implicit operator TEnum(DbEnum<TEnum> value)
    {
        return value.ToEnum();
    }
}

...这基本上就足够了...除了 EF 不支持泛型类型...

这意味着对于每个枚举,您必须拥有类似...

public enum PrivacyLevel
{
    Public,
    Friends,
    Private
}

public class PrivacyLevelEnum : DbEnum<PrivacyLevel>
{
    public PrivacyLevelEnum() : this(default (PrivacyLevel))
    {      
    }

    public PrivacyLevelEnum(PrivacyLevel value) : base(value)
    {
    }

    public static implicit operator PrivacyLevelEnum(PrivacyLevel value)
    {
        return new PrivacyLevelEnum(value);
    }

    public static implicit operator PrivacyLevel(PrivacyLevelEnum value)
    {
        return value.ToEnum();
    }
}

这为您提供了一些可以轻松生成的样板,例如使用 T4 模板。

最终你会使用:

public class CalendarEntry : Entity
{

    public virtual PrivacyLevelEnum PrivacyLevel { get; set; } = new PrivacyLevelEnum();

}

但由于您已经进行了隐式转换,因此类声明是唯一需要了解帮助器类型的声明。

【讨论】:

  • 这个方法在哪里找到 string.ToEnum(); - 是不是扩展方法
  • @Ken 是的 - public static T ToEnum&lt;T&gt;(this string value) { return (T)Enum.Parse(typeof(T), value, true); }
【解决方案4】:

显式枚举到字符串的转换会使您的代码变得混乱,并且您必须继续解析值。查找表也是如此。只需将[Column] 属性添加到您的枚举字段并将TypeName 指定为nvarchar(对于SQL)或varchar(对于postgres)。像魅力一样为我工作。 在你的情况下,例如:

public class User
{
    public int ID { get; set; }

    public string Name { get; set; }

    [Column(TypeName = "nvarchar(20)")]
    public Type Type { get; set; }

    public List<Wepon> WeposInList { get; set; }

    }  

您可以在官方文档here中阅读更多相关信息

【讨论】:

  • 在 EF Core 中为我服务。好的。特别注意它是向下兼容的,即在将 db 从 int 转换为 string 列类型之后,“整数字符串”仍然会被正确解析。
  • 如果没有在我的上下文的 OnModelCreating 方法中指定 int字符串转换器,这将不起作用。
【解决方案5】:

将它们存储为字符串不是一个好主意,但您可以使用 ef enum to lookup 为您的枚举创建查找表,并且非常易于使用。

【讨论】:

  • 您能否解释一下为什么将枚举存储为nvarchar() 不是一个好主意?文本不是比数据库中的整数更具可读性吗?
【解决方案6】:

除了Martin's answer,Entity Framework Core 有一个pre-defined converter 用于枚举到字符串。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<User>()
        .Property(e => e.Type)
        .HasConversion<string>();
}

同样可以通过以下方式实现:

public class User
{
    public int ID { get; set; }

    public string Name { get; set; }

    [Column(TypeName = "nvarchar(24)")]
    public Type Type { get; set; }

    public List<Weapon> WeaponsInList { get; set; }
}

【讨论】:

    【解决方案7】:

    我认为,将它们存储为 int 会更有用,因为您可以轻松地将 DB 中的 int 转换为 enum

    但如果它是你想要的,有两种方法。您可以将Type.Zombie.ToString()(或分别为Type.Human.ToString())保存到数据库(将是“僵尸”),或者您可以获取您正在使用的DescriptionAttribute 的值并将其保存到数据库中。描述如何获取描述here。 - 在这种情况下,它也将是“僵尸”,但它可能是您在Description() 中写的任何其他内容。

    如果您使用ToString,则可以使用Enum.Parse 来获取enum 的实例。如果你使用描述,那就没那么容易了。

    【讨论】:

    • Entity Framework 现在支持枚举,不需要将 int 从 DB 转换为它的枚举值。
    猜你喜欢
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-11
    • 2014-08-07
    相关资源
    最近更新 更多