EF Core 枚举列表

8

我正在使用Entity Framework Core编写一个小型应用程序来组织酒店预订。我需要添加有关每个房间床位数量和类型的信息。我在思考后决定,将枚举列表存储,而不是将其存储在单独的表中会更好。但我不知道如何实现。

public enum Bed
{
    Single = 1,
    Double = 2,
}

public class Room : IEntity
{
   [Key]
   public int Id { get; set; }
        
   public string RoomIdentifier { get; set; }
   public ICollection<Bed> Beds { get; set; }
        
   public int Floor { get; set; }
   public double Price { get; set; }

   public bool IsFree { get; set; }
   public bool IsOutOfService { get; set; }
   public bool ShouldBeCleaned { get; set; }
        
   public ICollection<RoomReservation> RoomReservations { get; set; }
}

能否实现这个功能呢?我还在考虑将其保存为字符串,例如 (1,2,1,2),然后解析它以获取房间中床的数量和类型。非常感谢您的帮助!


默认情况下,枚举列表将存储在单独的表中。因此,将ID字符串解析回列表将是一种自定义序列化器。 - Richard Hubley
2
枚举类型在数据库中以整数形式存储。值转换器用于在枚举类型和其数字等效项之间进行转换。请参见https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions。 - Robert Harvey
我不认为枚举列表与任何其他对象的列表有什么区别。 - gunr2171
也许如果您展示更多的代码? - Robert Harvey
1
不使用单独的表,你只会得到问题 - 非标准更改检测,无法在此类“列”上执行服务器端查询条件等。将值存储在逗号分隔的字符串中并不比将日期/数字存储在字符串中而不是本机数据类型更好。现在的数据库表(特别是由ORM代码迁移管理时)是免费的(基本上没有成本),只需使用它们,不要浪费时间。我非常确定你在几天后发送的下一个问题将是“如何使用LINQ和EF Core查找具有特定床位数量和类型的房间”,而没有好的答案。 - Ivan Stoev
显示剩余3条评论
2个回答

9

这里有一个使用虚构对象名称的示例,希望能够帮助。

在 OnModelCreating 方法中:

modelBuilder.Entity<Room>()
        .Property(e => e.BedList)
        .HasConversion(
            v => string.Join(',', v),
            v => v.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<Bed>())
        .Metadata.SetValueComparer(valueComparer);

您将需要为列表创建自己的值比较器示例:

var valueComparer = new ValueComparer<ICollection<string>>(
                    (c1, c2) => c1.SequenceEqual(c2),
                    c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
                    c => (ICollection<string>)c.ToHashSet());

我为什么需要一个ValueComparer?没有它也能正常工作。 - Rogerio Schmitt
1
@RogerioSchmitt 更改跟踪可能会识别列更改并在不需要值比较器的情况下进行写入,但说实话我没有进行过测试。 - Richard Hubley
很遗憾,无法检索包含另一个列表或集合中任何值的值的数据(例如:_context.Room.Where(r => r.Beds.Any(b => listOfBeds.Contains(b))))。这将导致“'b => __listOfBeds_3.Contains(b)' 无法翻译”的错误。 - einord

3

如果你想要在单个字段中存储逗号分隔的列表,可以按照Richard's的示例操作,或者如果您有映射逻辑,则可以手动处理。

以下答案仅适用于当您想使用枚举而不是表格时,而不是尝试将多个枚举值放入单个列中。


有一个名为 Aardalis Steve Smith 的 C# 包叫做SmartEnumGitHub 链接

它允许你拥有一个强类型的枚举,但你可以将它以字符串或整数等任何形式保存在数据库中。

这里是一个例子:

public sealed class Bed : SmartEnum<TestEnum>
{
    // static instances of the SmartEnum Bed represents the enumerations
    public static readonly Bed Single = new Bed(name: "Single", value: 1, isPremium: false);
    public static readonly Bed Double = new Bed(name: "Double", value: 2, isPremium: true);


    // example additional property of each enumeration
    public bool IsPremium = false;


    private Bed(string name, int value, bool isPremium) : base(name, value)
    {
        IsPremium = isPremium;
    }
}

在上面的例子中,枚举是由一个 int 值支持的。通过使用 : SmartEnum<TestEnum, string>,您可以使其由字符串支持。它支持隐式转换等功能,在 EF 中工作良好,并且您将能够根据需要“列出”枚举。

1
嗯。Dapper不需要这些步骤;你只需在DTO中声明属性为枚举类型,Dapper会自动处理正确的事情(数据库字段是int)。 - Robert Harvey
@RobertHarvey 当然,这是一种替代方案--在领域层面上解决了问题。 - galdin
是的,我认为将这些值存储为字符串值最好,但其他建议也非常受欢迎。 - Hesh
EF Core 也可以自动完成这项任务,并支持将枚举值存储(和查询)为 int 或 string 类型在数据库中,因此无需额外的包。 - Ivan Stoev
@IvanStoev 我知道,就像我之前说的那样...这解决了域层的问题,并且它有其优点。 - galdin
如何在 HTML UI 的选择选项中使用它? - Joseph Wambura

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接