Search code examples
c#asp.net-mvcmongodbnosqlmongodb.driver

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.


Solution

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