ProgramingTip

MongoDB에서 Enum을 많이로 저장

bestdevel 2020. 11. 16. 21:58
반응형

MongoDB에서 Enum을 많이로 저장


열거 된 이름으로 저장하는 방법이 있습니까?

예 :

이 열거 형이 존재하는 상상해.

public enum Gender
{
    Female,
    Male
}

이제 가상의 사용자가

...
Gender gender = Gender.Male;
...

MongoDb 데이터베이스에 {... "Gender": 1 ...}로 저장됩니다.

하지만 나는 {... "Gender": "Male"...} 같은 것을 선호합니다.

이것이 가능한가? 커스텀 매핑, 리플렉션 트릭 등.

내 맥락 : POCO보다 강력한 형식의 컬렉션을 사용합니다 (음, AR을 표시하고 다형성을 사용합니다). 작업 단위의 형태로 다양 데이터 액세스 추상화 계층이 있습니다. 그래서 어떤 클래스 맵을 정의 할 수 있습니다. 나는 공식 MongoDb 드라이버 + 유창한 mongodb를 사용합니다.


MongoDB .NET 드라이버를 사용하면 규칙적용 하여 CLR 유형과 데이터베이스 요소의 특정 매핑이 처리 되는 방식 을 사용할 수 있습니다 .

모든 열거 형에 적용되는 모든 유형에 속성을 추가하거나 모든 유형을 수동으로 매핑하는 것과 동일하게 AppDomain 당 한 번에 매핑하는 규칙을 설정합니다 (일반적으로 애플리케이션을 시작할 때).

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

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

using MongoDB.Bson;

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

public class Person
{
    [JsonConverter(typeof(StringEnumConverter))]  // JSON.Net
    [BsonRepresentation(BsonType.String)]         // Mongo
    public Gender Gender { get; set; }
}

열거를 포함하는 클래스에 대한 클래스 맵을 사용자 정의하고 멤버가 맵시있게 표시 할 수 있습니다. 여기 열거 형의 생성 화 및 역 생성 화를 모두 처리합니다.

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

         });
      }

열거 된 표현이 전역 언어 표현 표현 지정하는 방법입니다.


MemberSerializationOptionsConvention사용 하여 열거 형 저장 방법에 대한 규칙을 정의합니다.

new MemberSerializationOptionsConvention(typeof(Gender), new RepresentationSerializationOptions(BsonType.String))

드라이버 2.x에서는 여기서 변환기를 사용하여 해결했습니다 .

BsonClassMap.RegisterClassMap<Person>(cm =>
            {
                cm.AutoMap();
                cm.MapMember(c => c.Gender).SetSerializer(new EnumSerializer<Gender>(BsonType.String));
            });

Ricardo Rodriguez의 답변을 적용하는 것만으로 는 일부 경우에 열거 형 값을 MongoDb에 어디에나 거기에서 발견 한 데 충분하지 않습니다.

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

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

데이터 구조에 열거 형 값이 개체 에 포함되는 경우 MongoDB의는 집합 직렬화 EnumRepresentationConvention사용하여 직렬화하지 않습니다 .

당신이 MongoDB를 드라이버의 구현을 보면, ObjectSerializer , 그것은 해결됩니다 TypeCode(박스형 값의 Int32열거 값을), 데이터베이스에 열거 된 값을 저장 유형을 사용합니다. 따라서 박스형 열거 형 값은 int값으로 변환 됩니다. int역내 화 될 때도 값으로 유지 됩니다.

이를 변경하려면 boxed 값이 열거 형인 경우 ObjectSerializer집합을 적용 하는 사용자 지정을 작성할 수 있습니다 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);
    }
}

그런 다음 사용자 지정 serializer를 개체 직렬화에 사용할 것으로 설정합니다.

BsonSerializer.RegisterSerializer(typeof(object), new ObjectSerializer());

이렇게하면 boxed enum 값이 unboxed 값과 마찬가지로 문자열로 저장됩니다.

그러나 문서를 deserialize 할 때 boxed 값은 문자열로 유지됩니다. 원래 열거 형 값으로 다시 변환되지 않습니다. 문자열을 원래 열거 형 값으로 다시 변환해야하는 경우 serializer가 desrialize 할 열거 형 유형을 알 수 있도록 식별 필드를 문서에 추가해야 할 수 있습니다.

이를 수행하는 한 가지 방법은 문자열 대신 bson 문서를 저장하는 것입니다. 여기에는 식별 필드 ( _t)와 값 필드 ( _v)가 열거 형 유형과 문자열 값을 저장하는 데 사용됩니다.


Chris Smith가 주석에서 제안한대로 열거 형 항목에 값을 할당했습니다.

나는 그것을 피할 것입니다. 문자열 값은 정수보다 더 많은 공간을 차지합니다. 지속성이 열거의 각 항목에 관여주고 결정 값을 경우 나는 그러나, 그래서 것 Female = 1, Male = 2그렇다면 열거는 문제와 끝까지하지 않도록 변경 이후 또는 항목의 순서에 추가됩니다.

정확히 내가 찾던 것은 아니지만 다른 방법이없는 것 같습니다.


여기에 게시 된 답변은 TEnumTEnum[]에서는 잘 작동하지만에서는 작동하지 않습니다 Dictionary<TEnum, object>. 코드를 사용하여 serializer를 초기화 할 때이 작업을 수행 할 수 있지만 속성을 통해이 작업을 수행하고 싶었습니다. DictionarySerializer키와 값에 대한 직렬 변환기로 구성 할 수 있는 유연성 만들었습니다 .

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) { }
}

키와 값이 모두 열거 형이지만 serializer의 모든 조합이 될 수있는 이와 같은 사용법 :

    [BsonSerializer(typeof(DictionarySerializer<
        Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum>, 
        EnumStringSerializer<FeatureToggleTypeEnum>,
        EnumStringSerializer<LicenseFeatureStateEnum>>))]
    public Dictionary<FeatureToggleTypeEnum, LicenseFeatureStateEnum> FeatureSettings { get; set; }

참고 URL : https://stackoverflow.com/questions/6996399/storing-enums-as-strings-in-mongodb

반응형