Как читать данные из Mongodb, которые имеют повторяющееся имя элемента в C#
Я использую MongoDB.Drivers
в моем приложении C# MVC для связи с базой данных Mongodb.
Код C#
var client = new MongoClient("mongodb://localhost:27012");
var db = client.GetDatabase("Test_DB");
var collection = db.GetCollection<BsonDocument>("TestTable");
var tData = await collection.FindAsync(new BsonDocument(true)); // I used - new BsonDocument(true) to specify allow duplicate element name while read data.
На изображении выше вы можете видеть, что у меня есть несколько столбцов под названием DuplicateCol
с разными значениями. Когда я попытался прочитать эти данные в c#
с помощью MongoDB.Driver
Я получил следующую ошибку: InvalidOperationException: Duplicate element name 'DuplicateCol'.
При вставке дубликата имени элемента я использовал AllowDuplicateNames=true
из BsonDocument
Объект, как показано ниже. (Он вставляет повторяющееся имя элемента без ошибок.)
BsonDocument obj = new BsonDocument();
obj.AllowDuplicateNames = true;
obj.Add("DuplicateCol", "Value_One");
obj.Add("propone", "newVal");
obj.Add("DuplicateCol", "Value_Two");
.... // other properties with value
await collection.InsertOneAsync(obj);
Примечание: эта схема должна быть. Я не могу это изменить.
Пожалуйста, дайте мне предложения, чтобы исправить эту проблему. Любая помощь будет высоко оценен..
Благодарю.
2 ответа
Если ничто иное не поможет, вы просмотрели другие ответы и комментарии и по-прежнему считаете, что вы обязательно должны придерживаться схемы, описанной в вашем вопросе, вы можете воспользоваться следующим способом. Создайте класс так:
class AlwaysAllowDuplicateNamesBsonDocumentSerializer : BsonDocumentSerializer {
protected override BsonDocument DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args) {
if (!context.AllowDuplicateElementNames)
context = context.With(c => c.AllowDuplicateElementNames = true);
return base.DeserializeValue(context, args);
}
public override BsonDocument Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) {
if (!context.AllowDuplicateElementNames)
context = context.With(c => c.AllowDuplicateElementNames = true);
return base.Deserialize(context, args);
}
}
Это пользовательский сериализатор для BsonDocument
который всегда устанавливает AllowDuplicateElementNames
при десериализации. Тогда вам нужно немного подумать, чтобы перезаписать по умолчанию BsonDocument
сериализатор (потому что BsonDocumentSerializer.Instance
не имеет сеттера):
// get __instance field, which is backing field for Instance property
var instanceField = typeof(BsonDocumentSerializer).GetField("__instance", BindingFlags.Static | BindingFlags.NonPublic);
// overwrite with our custom serializer
instanceField.SetValue(null, new AlwaysAllowDuplicateNamesBsonDocumentSerializer());
Сделав это где-нибудь при запуске, вы сможете читать ваши документы с дублированными атрибутами.
Полный код для теста:
static void Main(string[] args) {
var instanceField = typeof(BsonDocumentSerializer).GetField("__instance", BindingFlags.Static | BindingFlags.NonPublic);
instanceField.SetValue(null, new AlwaysAllowDuplicateNamesBsonDocumentSerializer());
TestMongoQuery();
Console.ReadKey();
}
static async void TestMongoQuery() {
var client = new MongoClient();
var db = client.GetDatabase("Test_DB");
var collection = db.GetCollection<BsonDocument>("TestTable");
using (var allDocs = await collection.FindAsync(FilterDefinition<BsonDocument>.Empty)) {
while (allDocs.MoveNext()) {
foreach (var doc in allDocs.Current) {
var duplicateElements = doc.Elements.Where(c => c.Name == "DuplicateCol");
foreach (var el in duplicateElements) {
Console.WriteLine(el.Name + ":" + el.Value);
}
}
}
}
}
Ошибка, которую вы получаете, является дизайном
InvalidOperationException: Duplicate element name 'DuplicateCol'
Как сказал CodeFuller, ключи документа MongoDB должны быть уникальными. Если вам нужен документ, содержащий повторяющиеся имена полей, и вы не можете изменить эту схему, MongoDB может оказаться не лучшим решением для базы данных... Я не знаю, какое из них будет честным. Хотя представляется возможным сохранить дубликаты ключей, используя:
AllowDuplicateNames=true
Я полагаю, что у вас будут проблемы с запросами и индексацией, среди прочего.
Можно привести аргумент, что эта схема является очень странным требованием. Более подходящей схемой может быть:
{
"_id" : ObjectId("xxx"),
"propTest" : 0,
...
"duplicateCol": [ "Value_Two", "Value_One" ]
}
Здесь у вас есть одно свойство (duplicateCol), которое представляет собой массив, который принимает несколько строк.