Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在MongoDB中将枚举存储为字符串_C#_Mongodb_Mongodb .net Driver - Fatal编程技术网

C# 在MongoDB中将枚举存储为字符串

C# 在MongoDB中将枚举存储为字符串,c#,mongodb,mongodb-.net-driver,C#,Mongodb,Mongodb .net Driver,有没有办法将枚举存储为字符串名而不是序数值 例如: 假设我有这个枚举: public enum Gender { Female, Male } 现在,如果某个虚构的用户与 ... Gender gender = Gender.Male; ... 它将作为{…“性别”:1…}存储在MongoDb数据库中 但我更喜欢这样的{…“性别”:“男性”} 这可能吗?自定义映射、反射技巧等等 我的上下文:我在POCO上使用强类型集合(我偶尔标记ARs和使用多态性)。我有一个工作单元形式的瘦

有没有办法将枚举存储为字符串名而不是序数值

例如:

假设我有这个枚举:

public enum Gender
{
    Female,
    Male
}
现在,如果某个虚构的用户与

...
Gender gender = Gender.Male;
...
它将作为{…“性别”:1…}存储在MongoDb数据库中

但我更喜欢这样的{…“性别”:“男性”}

这可能吗?自定义映射、反射技巧等等


我的上下文:我在POCO上使用强类型集合(我偶尔标记ARs和使用多态性)。我有一个工作单元形式的瘦数据访问抽象层。因此,我没有序列化/反序列化每个对象,但我可以(并且确实)定义一些类映射。我使用官方的MongoDb驱动程序+流畅的MongoDb。

我最终为枚举项赋值,正如Chris Smith在评论中所建议的:

我会避免的。字符串值比整数占用更多的空间。但是,如果涉及到持久性,我会为枚举中的每个项提供确定性值,因此
Female=1
memale=2
,这样,如果稍后添加枚举,或者更改了项的顺序,则不会出现问题


不完全是我想要的,但似乎没有其他方法。

您可以为包含枚举的类自定义类映射,并指定成员由字符串表示。这将处理枚举的序列化和反序列化

if (!MongoDB.Bson.Serialization.BsonClassMap.IsClassMapRegistered(typeof(Person)))
      {
        MongoDB.Bson.Serialization.BsonClassMap.RegisterClassMap<Person>(cm =>
         {
           cm.AutoMap();
           cm.GetMemberMap(c => c.Gender).SetRepresentation(BsonType.String);

         });
      }
if(!MongoDB.Bson.Serialization.BsonClassMap.IsClassMapRegistered(typeof(Person)))
{
MongoDB.Bson.Serialization.BsonClassMap.RegisterClassMap(cm=>
{
cm.AutoMap();
GetMemberMap(c=>c.Gender).SetRepresentation(BsonType.String);
});
}

我仍在寻找一种方法来指定枚举全局表示为字符串,但这是我当前使用的方法。

使用成员序列化选项约定定义有关如何保存枚举的约定

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public class Person
{
    [JsonConverter(typeof(StringEnumConverter))]  // JSON.Net
    [BsonRepresentation(BsonType.String)]         // Mongo
    public Gender Gender { get; set; }
}
new MemberSerializationOptionsConvention(typeof(Gender), new RepresentationSerializationOptions(BsonType.String))
NET驱动程序,以确定如何处理CLR类型和数据库元素之间的某些映射

如果希望将此应用于所有枚举,则只需为每个AppDomain设置一次约定(通常在启动应用程序时),而不是向所有类型添加属性或手动映射每个类型:

// Set up MongoDB conventions
var pack = new ConventionPack
{
    new EnumRepresentationConvention(BsonType.String)
};

ConventionRegistry.Register("EnumStringConvention", pack, t => true);

对于驱动程序2.x,我使用以下方法解决:

BsonClassMap.RegisterClassMap(cm=>
{
cm.AutoMap();
cm.MapMember(c=>c.Gender).SetSerializer(新的EnumSerializer(BsonType.String));
});

我发现,在某些情况下,仅应用枚举值不足以将字符串正确序列化到MongoDb中:

// Set up MongoDB conventions
var pack = new ConventionPack
{
    new EnumRepresentationConvention(BsonType.String)
};

ConventionRegistry.Register("EnumStringConvention", pack, t => true);
如果数据结构涉及将枚举值装箱到对象中,MongoDb序列化将不会使用set
EnumRepresentationConvention
对其进行序列化

事实上,如果您查看MongoDb驱动程序的实现,它将解析装箱值的
TypeCode
Int32
用于枚举值),并使用该类型在数据库中存储枚举值。因此,装箱的枚举值最终被序列化为
int
值。反序列化时,它们也将保持为
int

要改变这一点,可以编写一个自定义的
ObjectSerializer
,如果装箱的值是枚举,它将强制执行set
EnumRepresentationConvention
。大概是这样的:

public class ObjectSerializer : MongoDB.Bson.Serialization.Serializers.ObjectSerializer
{
     public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
    {
        var bsonWriter = context.Writer;
        if (value != null && value.GetType().IsEnum)
        {
            var conventions = ConventionRegistry.Lookup(value.GetType());
            var enumRepresentationConvention = (EnumRepresentationConvention) conventions.Conventions.FirstOrDefault(convention => convention is EnumRepresentationConvention);
            if (enumRepresentationConvention != null)
            {
                switch (enumRepresentationConvention.Representation)
                {
                    case BsonType.String:
                        value = value.ToString();
                        bsonWriter.WriteString(value.ToString());
                        return;
                }
            }
        }

        base.Serialize(context, args, value);
    }
}
然后将自定义序列化程序设置为用于序列化对象的序列化程序:

BsonSerializer.RegisterSerializer(typeof(object), new ObjectSerializer());
这样做将确保装箱的枚举值与未装箱的枚举值一样存储为字符串

但是请记住,当反序列化文档时,装箱的值将保持为字符串。它将不会转换回原始枚举值。如果需要将字符串转换回原始枚举值,则可能必须在文档中添加一个判别字段,以便序列化程序可以知道要反序列化的枚举类型


一种方法是存储一个bson文档,而不仅仅是一个字符串,其中使用判别字段(
\u t
)和值字段(
\u v
)来存储枚举类型及其字符串值。

此处发布的答案适用于
TEnum
TEnum[]
,但是,无法使用
词典
。您可以在使用代码初始化序列化程序时实现这一点,但我希望通过属性来实现这一点。我创建了一个灵活的
字典序列化程序
,可以为键和值配置序列化程序

public class DictionarySerializer<TDictionary, KeySerializer, ValueSerializer> : DictionarySerializerBase<TDictionary>
    where TDictionary : class, IDictionary, new()
    where KeySerializer : IBsonSerializer, new()
    where ValueSerializer : IBsonSerializer, new()
{
    public DictionarySerializer() : base(DictionaryRepresentation.Document, new KeySerializer(), new ValueSerializer())
    {
    }

    protected override TDictionary CreateInstance()
    {
        return new TDictionary();
    }
}

public class EnumStringSerializer<TEnum> : EnumSerializer<TEnum>
    where TEnum : struct
{
    public EnumStringSerializer() : base(BsonType.String) { }
}
公共类DictionarySerializer:DictionarySerializerBase
其中TDictionary:class,IDictionary,new()
其中KeySerializer:IBsonSerializer,new()
其中ValueSerializer:IBsonSerializer,new()
{
public DictionarySerializer():base(DictionaryRepresentation.Document,new KeySerializer(),new ValueSerializer())
{
}
受保护的重写TDictionary CreateInstance()
{
返回新的TDictionary();
}
}
公共类EnumStringSerializer:EnumSerializer
其中TEnum:struct
{
公共EnumStringSerializer():基(BsonType.String){}
}
类似这样的用法,其中键和值都是枚举类型,但可以是序列化程序的任意组合:

    [BsonSerializer(typeof(DictionarySerializer<
        Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum>, 
        EnumStringSerializer<FeatureToggleTypeEnum>,
        EnumStringSerializer<LicenseFeatureStateEnum>>))]
    public Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum> FeatureSettings { get; set; }
[b序列化器(类型化)(字典序列化器<
字典,
EnumStringSerializer,
EnumStringSerializer>)]
公共词典功能设置{get;set;}

如果您使用的是.NET Core 3.1及以上版本,请使用Microsoft最新的ultra-fast Json序列化程序/反序列化程序System.Text.Json()

请参阅


我会避免的。字符串值比整数占用更多的空间。但是,如果涉及持久性,我会为枚举中的每个项提供确定性的值,因此女性=1,男性=2,因此如果稍后将枚举添加到
    [BsonSerializer(typeof(DictionarySerializer<
        Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum>, 
        EnumStringSerializer<FeatureToggleTypeEnum>,
        EnumStringSerializer<LicenseFeatureStateEnum>>))]
    public Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum> FeatureSettings { get; set; }
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.Text.Json.Serialization;;

public class Person
{
    [JsonConverter(typeof(JsonStringEnumConverter))]  // System.Text.Json.Serialization
    [BsonRepresentation(BsonType.String)]         // MongoDB.Bson.Serialization.Attributes
    public Gender Gender { get; set; }
}