Home > Blockchain >  How to store a List of enum using EF 5
How to store a List of enum using EF 5

Time:11-18

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

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.

CodePudding user response:

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. The use of SmartEntity is just a class wrapper.

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. 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 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 can take care of the rest behind the scenes.

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. 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. The OrderStatusId column in the Orders table can then have a FK constraint on the OrderStatuses table to keep referential integrity in the database. OrderStatus never gets an entity declaration.

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

CodePudding user response:

This is not really an enum problem, but rather the fact that you want to model a many-to-many relationship. If you would need to store a list of int or string, it would be the same.

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. That would be really ugly.

Or you model correctly, meaning you need a table storing your genres. This table could be constructed in a classic way, with a primary key (Id) and value (genre enum value). You could even use the int representation of the enum as the primary key, but I don't see any good doing this.

To store enums in a database as a property using EF Core, use pre-defined or build-in converters.

See 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:

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.

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.

EDIT

You could store a string that contains some form of a serialization of your list of enum:

For instance "Western; Drama", or a JSON representation

An be able de deserialize it, but again that would be ugly.

  • Related