如何使用 EF 5 存储枚举列表

How to store a List of enum using EF 5

我目前正在做一个代码优先的小项目:我必须创建一个电影数据库,并且像往常一样,每部电影可以有不止一种类型 (m:n)。由于类型是不变的,我决定创建一个包含所有类型的枚举类型。

并且在 Movie table 中我有一个流派列表(枚举)。显然这是错误的,因为你不能在数据库中存储枚举列表。

所以我开始寻找答案。我遇到了很多解决方案,不幸的是 none 其中确实对我有帮助。所以我决定问这个问题。我知道这可能是重复的,但其他解决方案并不是很有帮助。

我找到的一些解决方案是 FlagsSmartEnum

我都试过了,但都没有用。你能不能看看我的代码并告诉我我做错了什么,或者是否有另一种方法来转换枚举列表。

电影:

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 问题,而是您想要建立多对多关系模型这一事实。 如果您需要存储 intstring 的列表,那将是相同的。

要么你打破了良好的做法,并在你的电影中存储了同一部电影的几行table(电影的每种类型一行)。在那种情况下,您将直接在数据库中存储一个 enum。那真的很难看。

或者您的模型正确,这意味着您需要 table 来存储您的流派。这个 table 可以用 classic 方式构造,具有主键 (Id) 和值(类型枚举值)。您甚至可以使用 enumint 表示作为主键,但我认为这样做没有任何好处。

要使用 EF Core 将 enum 作为 属性 存储在数据库中,请使用预定义或内置转换器。

https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=fluent-api#pre-defined-conversions

您需要一个类型枚举,以及一​​个类型 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>,以及 MovieGenre 之间的多对多关系。

但我不确定我是否会使用它作为解决方案。将来可能需要添加新的类型,简单的 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 和枚举以表明它们的相互依赖性。