C# - 从 MongoDB 加载数据(存储为字符串的枚举)如果不能映射回枚举则抛出异常,例如旧的枚举值已被删除

C# - Loading data (enum stored as string) from MongoDB throw exception if cannot be mapped back to enum, for example old enum value has been deleted

我需要将 enums 作为字符串存储在 MongoDB 中,这很好,但我需要能够处理 enum.

中的更改

例如,如果您有以下枚举

enum CarType {
  SportCar,
  Suv,
  Hatchback
}

如果我删除 SUV 并尝试从 MongoDB 加载 CarType SUV 的记录,它将引发异常,因为它无法将其映射回 enum 来自 string。我需要能够优雅地处理这个问题,比如有一个 enum 值未知并将其用作后备值。

有没有办法为 MongoDB 中的指定列指定自定义映射器?例如,汽车实体有多个 string 值,但只有 CarType 在我们的后端是一个 enum 并在我们的 MongoDB 中存储为 string。因此,当从 DB 加载数据时,仅对不会抛出异常的 CarType 属性 使用客户映射器,而不是将其映射到 CarType.Unkown

首先您需要将未知枚举定义为第一个枚举。我自己更喜欢将它们存储为显式整数,以防止重命名枚举破坏数据库,但在您的情况下这不是必需的。

enum CarType {
  Unknown
  SportCar,
  Suv,
  Hatchback
}

然后您需要为您的枚举编写并注册一个自定义反序列化器,以便像这样实现对“未知”的回退。它会尝试将您的数据库值解析为枚举,如果无法解析则回退到默认值(未知)。

 public class CustomEnumSerializer<TEnum> : MongoDB.Bson.Serialization.Serializers.EnumSerializer<TEnum>
            where TEnum : struct
        {
            public override TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
            {
                var type = context.Reader.GetCurrentBsonType();
                string val;

                switch (type)
                {
                    case BsonType.String:
                        val = context.Reader.ReadString() ?? "";
                        break;
                    case BsonType.Int32:
                        val = context.Reader.ReadInt32().ToString();
                        break;
                    case BsonType.Int64:
                        val = context.Reader.ReadInt64().ToString();
                        break;
                    case BsonType.Null:
                        return default(TEnum);
                    default:
                        return base.Deserialize(context, args);
                }

                if (Enum.TryParse(val, true, out TEnum result) && Enum.IsDefined(typeof(TEnum), result))
                {
                    return result;
                }
                return default(TEnum);
            }
        }

将自定义序列化程序注册为:

BsonClassMap.RegisterClassMap<ClassThatHoldsTheProperty>(ms =>
            {
                ms.AutoMap();
                ms.GetMemberMap(i => i.CarTypeProperty)
                    .SetSerializer(new CustomEnumSerializer<CarType>());
            });