如何将数据库中的枚举保存为字符串
How to save enum in database as string
这是我的模型 Class,我们有一个类型,可以是僵尸或人类。
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。
将它们存储为字符串不是一个好主意,但您可以使用 ef enum to lookup 为您的枚举创建查找表并且它非常易于使用。
我认为,将它们存储为 int
更有用,因为您可以非常轻松地将 int
从 DB 转换为 enum
.
但如果它是你想要的,有两种方法。您可以将 Type.Zombie.ToString()
(或 Type.Human.ToString()
分别)保存到数据库(将是 "Zombie"),或者您可以获得您正在使用的 DescriptionAttribute
的值并保存该值到数据库。 here 描述了如何获取描述。 - 在这种情况下,它也将是 "Zombie",但它可能是您在 Description()
.
中写入的任何其他内容
如果使用 ToString
,则可以使用 Enum.Parse
取回 enum
的实例。如果使用说明,就没那么容易了。
您可以将枚举作为字符串保存到数据库中,我同意 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; }
}
将此扩展 class 添加到您的项目中。
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/
据我所知,我遇到过这个问题,老实说,我不知道为什么 MS 没有添加此功能(NH 可以像往常一样这样做..)。
无论如何,我通常做的是使用 const 字符串 classes like:
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 描述的概念(例如,当给定的枚举在整个域中广泛使用时,它有一些缺点)。好吧..我得到了以下工作:
用于存储值的基本组件class:
[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();
}
但是由于您进行了隐式转换,因此 class 声明是唯一需要注意辅助类型的声明。
在Entity Framework Core中可以指定内置转换:
modelBuilder
.Entity<DataSet>()
.Property(d => d.SemanticType)
.HasConversion(new EnumToStringConverter<DataSetSemanticType>());
更多详情here。
显式枚举到字符串的转换会使您的代码变得混乱,并且您必须继续解析值。查找表也是如此。只需将 [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
进一步 , Entity Framework Core has a 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; }
}
这是我的模型 Class,我们有一个类型,可以是僵尸或人类。
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。
将它们存储为字符串不是一个好主意,但您可以使用 ef enum to lookup 为您的枚举创建查找表并且它非常易于使用。
我认为,将它们存储为 int
更有用,因为您可以非常轻松地将 int
从 DB 转换为 enum
.
但如果它是你想要的,有两种方法。您可以将 Type.Zombie.ToString()
(或 Type.Human.ToString()
分别)保存到数据库(将是 "Zombie"),或者您可以获得您正在使用的 DescriptionAttribute
的值并保存该值到数据库。 here 描述了如何获取描述。 - 在这种情况下,它也将是 "Zombie",但它可能是您在 Description()
.
如果使用 ToString
,则可以使用 Enum.Parse
取回 enum
的实例。如果使用说明,就没那么容易了。
您可以将枚举作为字符串保存到数据库中,我同意 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; }
}
将此扩展 class 添加到您的项目中。
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/
据我所知,我遇到过这个问题,老实说,我不知道为什么 MS 没有添加此功能(NH 可以像往常一样这样做..)。
无论如何,我通常做的是使用 const 字符串 classes like:
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 描述的概念(例如,当给定的枚举在整个域中广泛使用时,它有一些缺点)。好吧..我得到了以下工作:
用于存储值的基本组件class:
[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();
}
但是由于您进行了隐式转换,因此 class 声明是唯一需要注意辅助类型的声明。
在Entity Framework Core中可以指定内置转换:
modelBuilder
.Entity<DataSet>()
.Property(d => d.SemanticType)
.HasConversion(new EnumToStringConverter<DataSetSemanticType>());
更多详情here。
显式枚举到字符串的转换会使您的代码变得混乱,并且您必须继续解析值。查找表也是如此。只需将 [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
进一步
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; }
}