如何将大型标志枚举存储到 SQL 数据库中的单个列?

How to store a large flags enum to a single column in a SQL database?

我有这个枚举:

[Flags]
public enum Actions 
{
    None = 0,
    MoveUp = 1,
    MoveDown = 2,
    MoveRight = 3,
    MoveLeft = 4
}

我希望 table 的列是这样的:

Id | UserId | Actions
.. | ..     | ..

我对如何存储枚举感到困惑,因为用户可以有多个操作,我不想使用另一个 table 来存储操作。我有 50 多个操作,这就是我使用 Flags 属性的原因。

actionssUser = Actions.MoveUp | Actions.MoveRight | ....... 

首先,重要的是要注意您的标志枚举未正确配置,并且几乎可以肯定不会以您期望的方式运行。如the documentation所述:

Define enumeration constants in powers of two, that is, 1, 2, 4, 8, and so on. This means the individual flags in combined enumeration constants do not overlap.

最后一句话在这里特别重要,您稍后就会看到。

例子

让我们使用您的示例将其付诸实践。您的枚举应该如下所示:

[Flags]
public enum Actions 
{
    None            = 0,
    MoveUp          = 1,
    MoveDown        = 2,
    MoveRight       = 4,
    MoveLeft        = 8
}

现在,假设您将枚举值设置为包含 MoveUpMoveRight 标志:

var actions = Actions.MoveUp | Actions.MoveRight;

您现在可以使用简单的转换将其转换为整数:

var actionsValue = (int)actions;

在本例中,将 return 5。此时,您可以将该值作为标准存储在 SQL 服务器中,例如TINYINT 列(假设您有八个或更少的选项),正如@Charlieface 在评论中指出的那样。

或者,更好的是,您可以将其转换为 byte,并将其存储为 BINARY(4) 列——或带有完整枚举的 BINARY(50)——如@meysam -asadi 建议:

var actionsValue = (byte)actions;

说明

如果您查看以上值,当您使用 2 的幂时,唯一可能的组合 return 5 是 1 (MoveUp) 和 4 (MoveRight).

如果您了解二进制,这会更加直观,因为您的位数组将如下所示:

0101

或者,从右到左:

Bit    Int    Label
1      1      MoveUp
0      2      MoveDown
1      4      MoveRight
0      8      MoveLeft

基本上,每个后续的 2 次方都会将下一个数字从 0 翻转到 1,将该选项标记为已标记。

复活你的旗帜

在return的旅程中,过程看起来大同小异,只是相反。因此,当您从数据库中检索值时,您可以简单地将其转换回枚举:

var actions = (Actions)actionsValue;

(其中 actionsValue 是您从数据库中检索到的任何值。)

限制

这里有限制!如果您使用这种方法,您需要确定您的值是稳定的。如果您尝试向现有枚举中注入一个值,则需要相应地重新计算所有以前的记录。为避免这种情况,您需要将任何新值添加到枚举的末尾。如果这不是可接受的限制,您最好将这些值存储在单独的列中。

Shorthand

这是题外话,但由于您有超过 50 个枚举值,所以 shorthand 用于分配更容易计算的 2 的幂:

[Flags]
public enum Actions 
{
    None            = 0,          // 0
    MoveUp          = 1,          // 1
    MoveDown        = 1 << 1,     // 2
    MoveRight       = 1 << 2,     // 4
    MoveLeft        = 1 << 3,     // 8
    … 
}

这比尝试将 2^50 写成 1125899906842624 更容易