英文:
Serialization inside Mongodb driver filter
问题
我试图在`Type`字段上做一个选择,`CloudFileTypes`是一个枚举类型。如果你尝试使用linq方法或mongodb驱动程序筛选来进行选择,它会将`Type`视为一个数字,无法找到所需的文件,在数据库中`Type`字段的值是字符串。如果我选择了某个特定的文件并访问类型字段,它会返回枚举成员,就像需要的那样。
我提前道歉,如果我误解了。
代码中的问题部分:
public CloudFile GetRoot(User user)
{
var builder = Builders
var query = builder.Eq(e => e.Type, CloudFileTypes.Root);
var root = _fileCollection.Find(query).First();
return root.First();
}
`CloudFile` 模型:
public class CloudFile
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("name")] public string Name { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter))]
[BsonElement("type")] public CloudFileTypes Type { get; set; }
[BsonElement("size")] public int Size { get; set; }
[BsonElement("path")] public string Path { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
[BsonElement("user")] public string User { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
[BsonElement("parent")] public string Parent { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
[BsonElement("childs")] public string[] Childs { get; set; }
}
public enum CloudFileTypes
{
[EnumMember(Value = "root")] Root,
[EnumMember(Value = "folder")] Folder,
[EnumMember(Value = "file")] File,
}
[根文件的示例。](https://i.stack.imgur.com/3rOcl.png)
我该如何解决这个问题。尝试过谷歌搜索,但没有成功。
**编辑:** *我创建了一个Json转换器,用于在将对象发送到客户端时序列化`enum`,使用了Yong Shun的扩展:*
public class EnumStringConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(CloudFileTypes);
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value is not TEnum @enum)
return;
if (!Attribute.IsDefined(@enum.GetType().GetMember(@enum.ToString()).FirstOrDefault(), typeof(EnumMemberAttribute)))
writer.WriteValue(@enum.ToString());
writer.WriteValue(EnumExtensions.GetEnumMemberValue(@enum));
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
var str = jo.ToString();
var obj = EnumExtensions.EnumMemberValueToEnum<CloudFileTypes>(str);
return obj;
}
}
<details>
<summary>英文:</summary>
I'm trying to make a selection on the `Type` field, `CloudFileTypes` is an `enum`. If you try to make a selection using linq methods or mongodb driver filter, then it perceives `Type` as a number and cannot find the desired file, and in the database in the `Type` field I have `string` values. And if I select some specific file and access the type field, then it will return the enumeration member, as needed.
I apologize in advance if I misunderstood.
Problem part of code:
public CloudFile GetRoot(User user)
{
var builder = Builders<CloudFile>.Filter;
var query = builder.Eq(e => e.Type, CloudFileTypes.Root);
var root = _fileCollection.Find(query).First();
return root.First();
}
`CloudFile` model:
public class CloudFile
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("name")] public string Name { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter))]
[BsonElement("type")] public CloudFileTypes Type { get; set; }
[BsonElement("size")] public int Size { get; set; }
[BsonElement("path")] public string Path { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
[BsonElement("user")] public string User { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
[BsonElement("parent")] public string Parent { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
[BsonElement("childs")] public string[] Childs { get; set; }
}
public enum CloudFileTypes
{
[EnumMember(Value = "root")] Root,
[EnumMember(Value = "folder")] Folder,
[EnumMember(Value = "file")] File,
}
[Example of root file.](https://i.stack.imgur.com/3rOcl.png)
How do i solve this problem. Tried googling, didn't work.
**EDIT:** *JsonConverter I created to serialize the `enum` when the object is sent to the client, using the extension from Yong Shun:*
public class EnumStringConverter<TEnum> : JsonConverter where TEnum: struct, Enum
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(CloudFileTypes);
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value is not TEnum @enum)
return;
if (!Attribute.IsDefined(@enum.GetType().GetMember(@enum.ToString()).FirstOrDefault(), typeof(EnumMemberAttribute)))
writer.WriteValue(@enum.ToString());
writer.WriteValue(EnumExtensions.GetEnumMemberValue(@enum));
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
var str = jo.ToString();
var obj = EnumExtensions.EnumMemberValueToEnum<CloudFileTypes>(str);
return obj;
}
}
</details>
# 答案1
**得分**: 0
以下是要翻译的部分:
1. A helper/extension method that allows you to convert from `string` to `Enum` (which implements the `EnumMemberAttribute` and vice versa.
```csharp
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
public static class EnumExtensions
{
public static string GetEnumMemberValue<TEnum>(this TEnum @enum)
where TEnum : struct, Enum
{
string enumMemberValue = @enum.GetType()
.GetMember(@enum.ToString())
.FirstOrDefault()?
.GetCustomAttributes<EnumMemberAttribute>(false)
.FirstOrDefault()?
.Value;
if (enumMemberValue == null)
return @enum.ToString();
//throw new ArgumentException($"Enum {@enum.GetType().Name} with member {@enum} not apply {nameof(EnumMemberAttribute)} attribute.");
return enumMemberValue;
}
public static TEnum EnumMemberValueToEnum<TEnum>(this string value)
where TEnum : struct, Enum
{
foreach (var field in typeof(TEnum).GetFields())
{
if (Attribute.GetCustomAttribute(field,
typeof(EnumMemberAttribute)) is EnumMemberAttribute attribute)
{
if (attribute.Value == value)
return (TEnum)field.GetValue(null);
}
if (field.Name == value)
return (TEnum)field.GetValue(null);
}
throw an ArgumentException($"{value} is not found.");
}
}
- Implement and register a custom serializer for converting the
string
value (fromEnumMemberAttribute
value) toEnum
and vice versa. Reference: @wilver's answer on Storing Enums as strings in MongoDB
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Serializers;
public class EnumMemberStringSerializer<TEnum> : ObjectSerializer, IBsonSerializer<TEnum>
where TEnum : struct, Enum
{
public new Type ValueType => typeof(TEnum);
public TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
return EnumExtensions.EnumMemberValueToEnum<TEnum>(base.Deserialize(context, args)?.ToString());
}
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TEnum value)
{
base.Serialize(context, args, EnumExtensions.GetEnumMemberValue((TEnum)value));
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
{
base.Serialize(context, args, EnumExtensions.GetEnumMemberValue((TEnum)value));
}
object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
return EnumExtensions.EnumMemberValueToEnum<TEnum>(base.Deserialize(context, args)?.ToString());
}
}
- Register the
EnumMemberStringSerializer
serializer (beforeIMongoDatabase
&IMongoCollection
instances are created).
BsonSerializer.RegisterSerializer(typeof(CloudFileTypes), new EnumMemberStringSerializer<CloudFileTypes>());
英文:
Tricky yet interesting question.
As mentioned that you are storing the enum value as string in the collection, based on your current query, MongoDB Fluent API will pass the enum value as an integer but not the string value from the EnumMemberAttribute
.
Warning: This will be a long answer.
These are the steps in order to allow the MongoDB Fluent API to pass the value from the EnumMemberAttribute
in the query.
- A helper/extension method that allows you to convert from
string
toEnum
(which implements theEnumMemberAttribute
and vice versa.
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
public static class EnumExtensions
{
public static string GetEnumMemberValue<TEnum>(this TEnum @enum)
where TEnum : struct, Enum
{
string enumMemberValue = @enum.GetType()
.GetMember(@enum.ToString())
.FirstOrDefault()?
.GetCustomAttributes<EnumMemberAttribute>(false)
.FirstOrDefault()?
.Value;
if (enumMemberValue == null)
return @enum.ToString();
//throw new ArgumentException($"Enum {@enum.GetType().Name} with member {@enum} not apply {nameof(EnumMemberAttribute)} attribute.");
return enumMemberValue;
}
public static TEnum EnumMemberValueToEnum<TEnum>(this string value)
where TEnum : struct, Enum
{
foreach (var field in typeof(TEnum).GetFields())
{
if (Attribute.GetCustomAttribute(field,
typeof(EnumMemberAttribute)) is EnumMemberAttribute attribute)
{
if (attribute.Value == value)
return (TEnum)field.GetValue(null);
}
if (field.Name == value)
return (TEnum)field.GetValue(null);
}
throw new ArgumentException($"{value} is not found.");
}
}
- Implement and register a custom serializer for converting the
string
value (fromEnumMemberAttribute
value) toEnum
and vice versa. Reference: @wilver's answer on Storing Enums as strings in MongoDB
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Serializers;
public class EnumMemberStringSerializer<TEnum> : ObjectSerializer, IBsonSerializer<TEnum>
where TEnum : struct, Enum
{
public new Type ValueType => typeof(TEnum);
public TEnum Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
return EnumExtensions.EnumMemberValueToEnum<TEnum>(base.Deserialize(context, args)?.ToString());
}
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TEnum value)
{
base.Serialize(context, args, EnumExtensions.GetEnumMemberValue((TEnum)value));
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
{
base.Serialize(context, args, EnumExtensions.GetEnumMemberValue((TEnum)value));
}
object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
return EnumExtensions.EnumMemberValueToEnum<TEnum>(base.Deserialize(context, args)?.ToString());
}
}
- Register the
EnumMemberStringSerializer
serializer (beforeIMongoDatabase
&IMongoCollection
instances are created).
BsonSerializer.RegisterSerializer(typeof(CloudFileTypes), new EnumMemberStringSerializer<CloudFileTypes>());
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论