如何使用 EF 5 存储枚举列表
How to store a List of enum using EF 5
我目前正在做一个代码优先的小项目:我必须创建一个电影数据库,并且像往常一样,每部电影可以有不止一种类型 (m:n)。由于类型是不变的,我决定创建一个包含所有类型的枚举类型。
并且在 Movie
table 中我有一个流派列表(枚举)。显然这是错误的,因为你不能在数据库中存储枚举列表。
所以我开始寻找答案。我遇到了很多解决方案,不幸的是 none 其中确实对我有帮助。所以我决定问这个问题。我知道这可能是重复的,但其他解决方案并不是很有帮助。
我找到的一些解决方案是 Flags
和 SmartEnum
。
我都试过了,但都没有用。你能不能看看我的代码并告诉我我做错了什么,或者是否有另一种方法来转换枚举列表。
电影:
class Serie
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(255)]
public string Titel { get; set; } = "";
public virtual List<Genres> Genres { get; } = new();
}
流派:
public sealed class Genres : SmartEnum<Genres>
{
public static readonly Genres Drama = new(name: "Drama", value: 1);
public static readonly Genres Crime = new(name: "Crime", value: 2);
...
private Genres(string name, int value) : base(name, value)
{ }
}
PS:我知道我可以用额外的 class 来做,但我想用枚举来做。
这并不是真正的 enum
问题,而是您想要建立多对多关系模型这一事实。
如果您需要存储 int
或 string
的列表,那将是相同的。
要么你打破了良好的做法,并在你的电影中存储了同一部电影的几行table(电影的每种类型一行)。在那种情况下,您将直接在数据库中存储一个 enum
。那真的很难看。
或者您的模型正确,这意味着您需要 table 来存储您的流派。这个 table 可以用 classic 方式构造,具有主键 (Id) 和值(类型枚举值)。您甚至可以使用 enum
的 int
表示作为主键,但我认为这样做没有任何好处。
要使用 EF Core 将 enum
作为 属性 存储在数据库中,请使用预定义或内置转换器。
您需要一个类型枚举,以及一个类型 class 用于映射:
public enum GenreEnum
{
Drama,
Western,
//...
}
public class Genre
{
public int Id { get; set; }
public GenreEnum Name { get; set; }
// And other properties needed.
}
然后
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Genre>()
.Property(e => e.Name)
.HasConversion<string>();
}
使用属性甚至可以更简单地完成此操作:
public enum GenreEnum
{
Drama,
Western,
//...
}
public class Genre
{
public int Id { get; set; }
[Column(TypeName = "nvarchar(24)")]
public GenreEnum Name { get; set; }
// And other properties needed.
}
当然你需要 DbSet<Genre>
和 DbSet<Movie>
,以及 Movie
和 Genre
之间的多对多关系。
但我不确定我是否会使用它作为解决方案。将来可能需要添加新的类型,简单的 string
而不是 enum
可能会更好。
编辑
您可以存储一个 string
,其中包含您的 enum
列表的某种形式的序列化:
例如“西部;戏剧”,或 JSON 表示法
能够对其进行反序列化,但这又很丑陋。
EF 可以很好地使用枚举,但我不认为您使用电影流派的示例是枚举的良好候选者,因为可以添加新的流派。 SmartEntity 的使用只是一个 class 包装器。
您的流派示例是多对多关系,因此查看数据库端您会得到如下内容:
Genres
- GenreId (PK)
- Name
Series
- SeriesId (PK)
SeriesGenres
- SeriesId (PK, FK)
- GenreId (PK, FK)
类型很简单 class,所以老实说用像 SmartEnum 这样的结构 class 包装它并没有真正的好处。在一天结束时,您会希望 EF 像对待任何其他实体一样对待它,以便您可以有效地查询它。 EF Core 5 可以适应多对多关系,而无需定义 SeriesGenre 实体,其中一个 Series 仅具有 Genre 的集合,然后配置 HasMany(x => x.Genres).WithMany()
关系和 SeriesGenre [=36= 的配置] & FKs。 EF 可以在幕后处理剩下的事情。
枚举的一个更好的例子是像状态这样的东西,在这种情况下,您需要一组固定的状态,业务规则逻辑将根据这些状态进行操作,并且除非系统更新以说明新状态,否则不会更改。例如:
///<summary>
/// Enumeration for order statuses. Ensure this matches the OrderStatuses table.
///</summary>
public enum OrderStatus
{
None = 0,
Pending = 1,
Packing = 2,
Review = 3,
Shipped = 4,
Delivered = 100
}
在这种情况下,订单会记录任何时间点的状态。业务逻辑将取决于当前状态。我们想要存储订单记录状态,但仍然确保我们的数据库具有参照完整性,因此我们将有一个 Statuses table,其对应的 OrderStatusIds 与枚举中的内容相匹配。然后,Orders table 中的 OrderStatusId 列可以在 OrderStatuses table 上具有 FK 约束,以保持数据库中的引用完整性。 OrderStatus 永远不会获得实体声明。
我在这样做时的主要建议是:
- 包含 table PK 列的枚举不应使用自动递增,而应使用显式 ID 来匹配枚举。
- 同样,每个值的枚举都应该是明确的,而不是依赖于自动递增。
- 应该记录 table 和枚举以表明它们的相互依赖性。
我目前正在做一个代码优先的小项目:我必须创建一个电影数据库,并且像往常一样,每部电影可以有不止一种类型 (m:n)。由于类型是不变的,我决定创建一个包含所有类型的枚举类型。
并且在 Movie
table 中我有一个流派列表(枚举)。显然这是错误的,因为你不能在数据库中存储枚举列表。
所以我开始寻找答案。我遇到了很多解决方案,不幸的是 none 其中确实对我有帮助。所以我决定问这个问题。我知道这可能是重复的,但其他解决方案并不是很有帮助。
我找到的一些解决方案是 Flags
和 SmartEnum
。
我都试过了,但都没有用。你能不能看看我的代码并告诉我我做错了什么,或者是否有另一种方法来转换枚举列表。
电影:
class Serie
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(255)]
public string Titel { get; set; } = "";
public virtual List<Genres> Genres { get; } = new();
}
流派:
public sealed class Genres : SmartEnum<Genres>
{
public static readonly Genres Drama = new(name: "Drama", value: 1);
public static readonly Genres Crime = new(name: "Crime", value: 2);
...
private Genres(string name, int value) : base(name, value)
{ }
}
PS:我知道我可以用额外的 class 来做,但我想用枚举来做。
这并不是真正的 enum
问题,而是您想要建立多对多关系模型这一事实。
如果您需要存储 int
或 string
的列表,那将是相同的。
要么你打破了良好的做法,并在你的电影中存储了同一部电影的几行table(电影的每种类型一行)。在那种情况下,您将直接在数据库中存储一个 enum
。那真的很难看。
或者您的模型正确,这意味着您需要 table 来存储您的流派。这个 table 可以用 classic 方式构造,具有主键 (Id) 和值(类型枚举值)。您甚至可以使用 enum
的 int
表示作为主键,但我认为这样做没有任何好处。
要使用 EF Core 将 enum
作为 属性 存储在数据库中,请使用预定义或内置转换器。
您需要一个类型枚举,以及一个类型 class 用于映射:
public enum GenreEnum
{
Drama,
Western,
//...
}
public class Genre
{
public int Id { get; set; }
public GenreEnum Name { get; set; }
// And other properties needed.
}
然后
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Genre>()
.Property(e => e.Name)
.HasConversion<string>();
}
使用属性甚至可以更简单地完成此操作:
public enum GenreEnum
{
Drama,
Western,
//...
}
public class Genre
{
public int Id { get; set; }
[Column(TypeName = "nvarchar(24)")]
public GenreEnum Name { get; set; }
// And other properties needed.
}
当然你需要 DbSet<Genre>
和 DbSet<Movie>
,以及 Movie
和 Genre
之间的多对多关系。
但我不确定我是否会使用它作为解决方案。将来可能需要添加新的类型,简单的 string
而不是 enum
可能会更好。
编辑
您可以存储一个 string
,其中包含您的 enum
列表的某种形式的序列化:
例如“西部;戏剧”,或 JSON 表示法
能够对其进行反序列化,但这又很丑陋。
EF 可以很好地使用枚举,但我不认为您使用电影流派的示例是枚举的良好候选者,因为可以添加新的流派。 SmartEntity 的使用只是一个 class 包装器。
您的流派示例是多对多关系,因此查看数据库端您会得到如下内容:
Genres
- GenreId (PK)
- Name
Series
- SeriesId (PK)
SeriesGenres
- SeriesId (PK, FK)
- GenreId (PK, FK)
类型很简单 class,所以老实说用像 SmartEnum 这样的结构 class 包装它并没有真正的好处。在一天结束时,您会希望 EF 像对待任何其他实体一样对待它,以便您可以有效地查询它。 EF Core 5 可以适应多对多关系,而无需定义 SeriesGenre 实体,其中一个 Series 仅具有 Genre 的集合,然后配置 HasMany(x => x.Genres).WithMany()
关系和 SeriesGenre [=36= 的配置] & FKs。 EF 可以在幕后处理剩下的事情。
枚举的一个更好的例子是像状态这样的东西,在这种情况下,您需要一组固定的状态,业务规则逻辑将根据这些状态进行操作,并且除非系统更新以说明新状态,否则不会更改。例如:
///<summary>
/// Enumeration for order statuses. Ensure this matches the OrderStatuses table.
///</summary>
public enum OrderStatus
{
None = 0,
Pending = 1,
Packing = 2,
Review = 3,
Shipped = 4,
Delivered = 100
}
在这种情况下,订单会记录任何时间点的状态。业务逻辑将取决于当前状态。我们想要存储订单记录状态,但仍然确保我们的数据库具有参照完整性,因此我们将有一个 Statuses table,其对应的 OrderStatusIds 与枚举中的内容相匹配。然后,Orders table 中的 OrderStatusId 列可以在 OrderStatuses table 上具有 FK 约束,以保持数据库中的引用完整性。 OrderStatus 永远不会获得实体声明。
我在这样做时的主要建议是:
- 包含 table PK 列的枚举不应使用自动递增,而应使用显式 ID 来匹配枚举。
- 同样,每个值的枚举都应该是明确的,而不是依赖于自动递增。
- 应该记录 table 和枚举以表明它们的相互依赖性。