Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read data from Mongodb which have duplicate element name in c#

I am using MongoDB.Drivers in my C# MVC application to communicate with Mongodb database.

C# Code

            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.

MongoDB Data Pic enter image description here

In above image you can see I have multiple Columns called DuplicateCol with different values. When I tried to read these data in c# using MongoDB.Driver I got following error : InvalidOperationException: Duplicate element name 'DuplicateCol'.

While insert duplicate element name I used AllowDuplicateNames=true of BsonDocument object as below. (It insert duplicate element name without error.)

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

Note: This Schema is Must. I can not altered it.

Please provide me suggestions to fix this Issue. Any help would be highly appreciated..

Thanks.

like image 283
prog1011 Avatar asked Feb 05 '18 11:02

prog1011


2 Answers

If nothing else helps, you reviewed other answers and comments, and still think you absolutely must keep design described in your question, you can use the following hack. Create class like this:

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

This is custom serializer for BsonDocument which always sets AllowDuplicateElementNames while deserializing. Then you need a bit of reflection to overwrite default BsonDocument serializer (because BsonDocumentSerializer.Instance has no setter):

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

By doing that somewhere at startup you will be able to read back your documents with duplicated attributes.

Full code for test:

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);
                }
            }
        }
    }
}
like image 113
Evk Avatar answered Sep 19 '22 23:09

Evk


The error you receive is by design

InvalidOperationException: Duplicate element name 'DuplicateCol'

As CodeFuller said, MongoDB document keys should be unique. If you need a document to contain duplicate field names and you cannot alter this schema, MongoDB might not be the right database solution for you....I don't know which one will to be honest. Although it does seem possible to save duplicate keys using:

AllowDuplicateNames=true

I imagine you will experience challenges with querying and indexing, among others.

An argument could be made that this schema is a very strange requirement. A more appropriate schema might be:

{
    "_id" : ObjectId("xxx"),
    "propTest" : 0,
    ...
    "duplicateCol": [ "Value_Two", "Value_One" ]
}

Here you have a single property (duplicateCol) which is an array which accepts multiple strings.

like image 28
pieperu Avatar answered Sep 21 '22 23:09

pieperu