简体   繁体   English

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

[英]How to store a List of enum using EF 5

I am currently working on a small project with code-first: I have to create a movie database and as usual each movie can have more than one genre (m:n).我目前正在做一个代码优先的小项目:我必须创建一个电影数据库,并且像往常一样,每部电影可以有多个类型 (m:n)。 Since genres are constant, I decided to create an enum genre with all genres in it.由于流派是不变的,我决定创建一个包含所有流派的枚举类型。

And in the Movie table I have a list of genres (enum).Movie表中,我有一个流派列表(枚举)。 Obviously this went wrong, because you can't store a list of enums in a database.显然这是错误的,因为您不能在数据库中存储枚举列表。

So I started looking for answers.于是我开始寻找答案。 I came across many solutions, unfortunately none of them really helped me.我遇到了很多解决方案,不幸的是它们都没有真正帮助我。 So I decided to ask this question.所以我决定问这个问题。 I know this may be a duplicate but the other solutions weren't really helpful.我知道这可能是重复的,但其他解决方案并没有真正的帮助。

Some of the solutions I've found are Flags and SmartEnum .我发现的一些解决方案是FlagsSmartEnum

I tried both but it didn't really work.我都试过了,但它并没有真正起作用。 Could you please take a look at my code and tell me what I did wrong or if there is another way to convert a list of enums.您能否看看我的代码并告诉我我做错了什么,或者是否有另一种方法来转换枚举列表。

Movie:电影:

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();
}

Genres:流派:

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: I know I could do it with an extra class but I want to do it with enum. PS:我知道我可以用额外的 class 来做到这一点,但我想用枚举来做到这一点。

This is not really an enum problem, but rather the fact that you want to model a many-to-many relationship.这并不是真正的enum问题,而是您想要 model 多对多关系的事实。 If you would need to store a list of int or string , it would be the same.如果您需要存储intstring的列表,那将是相同的。

Either you break good practice, and store in your movie table several rows for the same movie (one row for each genre of the movie).要么你打破良好的做法,并在你的电影表中存储同一部电影的几行(电影的每种类型都有一行)。 In that case you would store directly an enum in the database.在这种情况下,您将直接在数据库中存储一个enum That would be really ugly.那真的很难看。

Or you model correctly, meaning you need a table storing your genres.或者你的 model 是正确的,这意味着你需要一个存储你的流派的表。 This table could be constructed in a classic way, with a primary key (Id) and value (genre enum value).该表可以以经典方式构建,具有主键 (Id) 和值(流派枚举值)。 You could even use the int representation of the enum as the primary key, but I don't see any good doing this.您甚至可以使用enumint表示形式作为主键,但我认为这样做没有任何好处。

To store enum s in a database as a property using EF Core, use pre-defined or build-in converters.要使用 EF Core 将enum作为属性存储在数据库中,请使用预定义或内置转换器。

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

You need a genre enum, and a genre class for the mapping:您需要一个流派枚举和一个流派 class 用于映射:

public enum GenreEnum
{
    Drama,
    Western,
    //...
}

public class Genre
{
    public int Id { get; set; }
    public GenreEnum Name { get; set; }

    // And other properties needed.
}

And then接着

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

This can be even done more simply with an attribute:这甚至可以通过一个属性更简单地完成:

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.
}

And of course you'll need a DbSet<Genre> and a DbSet<Movie> , and a many to many relationship between Movie and Genre .当然,您需要一个DbSet<Genre>和一个DbSet<Movie> ,以及MovieGenre之间的多对多关系。

But I'm not sure I'd use this as a solution.但我不确定我是否会将其用作解决方案。 New genres might need to be added in the future, and a simple string instead of an enum might be better.将来可能需要添加新的流派,一个简单的string而不是enum可能会更好。

EDIT编辑

You could store a string that contains some form of a serialization of your list of enum :您可以存储一个string ,其中包含某种形式的enum列表的序列化:

For instance "Western; Drama", or a JSON representation例如“西方;戏剧”,或 JSON 表示

An be able de deserialize it, but again that would be ugly.可以反序列化它,但这又是丑陋的。

EF can work with Enums just fine, though I wouldn't consider your example using movie Genres to be a good candidate for an enumeration given new Genres could be added. EF 可以很好地与 Enums 一起工作,但我不认为您使用电影流派的示例是一个很好的枚举候选者,因为可以添加新的流派。 The use of SmartEntity is just a class wrapper. SmartEntity 的使用只是一个 class 包装器。

Your example of Genres is a Many-to-Many relationship, so looking at the DB side you would have something like:您的流派示例是多对多关系,因此查看数据库方面,您将有如下内容:

Genres
 - GenreId (PK)
 - Name

Series
 - SeriesId (PK)

SeriesGenres
 - SeriesId (PK, FK)
 - GenreId (PK, FK)
 

Genre is a simple class, so there is honestly no real benefit to wrapping it with a structure class like SmartEnum.流派是一个简单的 class,所以说实话,用像 SmartEnum 这样的结构 class 包装它并没有真正的好处。 At the end of the day you will want EF to treat it like any other entity so that you can query against it effectively.归根结底,您会希望 EF 像对待任何其他实体一样对待它,以便您可以有效地查询它。 EF Core 5 can accommodate the many-to-many relationship without having to define the SeriesGenre entity, where a Series simply has a collection of Genre, then configured with a HasMany(x => x.Genres).WithMany() relationship and the configuration of the SeriesGenre table & FKs. EF Core 5 可以适应多对多关系,而无需定义 SeriesGenre 实体,其中 Series 仅具有 Genre 的集合,然后配置有HasMany(x => x.Genres).WithMany()关系和SeriesGenre 表和 FK 的配置。 EF can take care of the rest behind the scenes. EF可以在幕后照顾rest。

A better example for an enumeration would be something like a Status where you would want a fixed set of statuses that business rule logic will act upon and would not change unless the system was updated to account for a new status.枚举的一个更好的例子是状态,您需要一组固定的状态,业务规则逻辑将根据这些状态进行操作,并且除非系统更新以考虑新状态,否则它们不会更改。 For example:例如:

///<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
}

In this case an Order would have a status recorded for any point in time.在这种情况下,订单将记录任何时间点的状态。 Business logic would hinge off the current status state.业务逻辑将取决于当前状态 state。 We'd want to store the order record a Status, but still ensure our database has referential integrity so we would have a Statuses table with corresponding OrderStatusIds matching what is in the Enum.我们希望将订单记录存储为状态,但仍要确保我们的数据库具有引用完整性,因此我们将有一个状态表,其中对应的 OrderStatusIds 与枚举中的内容匹配。 The OrderStatusId column in the Orders table can then have a FK constraint on the OrderStatuses table to keep referential integrity in the database.然后,Orders 表中的 OrderStatusId 列可以对 OrderStatuses 表具有 FK 约束,以保持数据库中的引用完整性。 OrderStatus never gets an entity declaration. OrderStatus 永远不会获得实体声明。

The main recommendations I have when doing this are that:我这样做的主要建议是:

  • The enumeration holding table PK column should not use auto-increment, but explicit IDs to match the Enum.枚举持有表 PK 列不应该使用自增,而是显式的 ID 来匹配 Enum。
  • Similarly the enumeration should be explicit with each value rather than relying on auto-increment.类似地,枚举应该对每个值都是明确的,而不是依赖于自动增量。
  • The table and the enumeration should be documented to refer their mutual dependency.表和枚举应记录在案以提及它们的相互依赖关系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM