11

I am testing MongoDB (server v 2.6.7) with the C# driver 2.0.

When I am using the insert function InsertOneAsync for a document with an _id which exists I am expecting an error like the one you get from the Mongo shell:

WriteResult({
    "nInserted" : 0,
    "writeError" : {
            "code" : 11000,
            "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: mydb.Commands.$_id_  dup key: { : 0.0 }"
    }})

But the problem is that the insert with the C# driver does not throw an exception and I can not find the WriteResult for the insert. When I look in the database it seems nothing have happened.

So my question is what to expect from InsertOneAsync when inserting an existing _id?

The code in Visual Studio:

IMongoCollection<BsonDocument> commandsCollection = db.GetCollection<BsonDocument>("Commands");
var bson = new BsonDocument
        {
            {"_id", i.Value},
            {"label", i.Key}
        };
commandsCollection.InsertOneAsync(bson);
shA.t
  • 16,580
  • 5
  • 54
  • 111
Fredrik
  • 127
  • 1
  • 1
  • 10

3 Answers3

14

If you're doing this within an async method, then Brduca's answer will work (and is preferrable), otherwise you can call Wait() on the Task returned from the InsertOneAsync call to ensure your application stays around long enough to see the duplicate key exception:

commandsCollection.InsertOneAsync(doc).Wait();

If the insert fails because of a duplicate key, the Wait() will throw an AggregateException that contains a MongoWriteException that contains the duplicate key details.

try
{
    commandsCollection.InsertOneAsync(doc).Wait();
}
catch(AggregateException aggEx)
{
    aggEx.Handle(x => 
    { 
        var mwx = x as MongoWriteException;
        if (mwx != null && mwx.WriteError.Category == ServerErrorCategory.DuplicateKey) 
        {
            // mwx.WriteError.Message contains the duplicate key error message
            return true; 
        }
        return false;
    });
}

Similarly, if you're using await, that will throw an AggregateException as well.

To avoid the added complexity of the AggregateException wrapping the mongo exception, you can call GetAwaiter().GetResult() instead of Wait():

try
{
    commandsCollection.InsertOneAsync(doc).GetAwaiter().GetResult();
}
catch(MongoWriteException mwx)
{
    if (mwx.WriteError.Category == ServerErrorCategory.DuplicateKey) 
    {
        // mwx.WriteError.Message contains the duplicate key error message
    }
}
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • 1
    But how do I detect the duplicate key? InsertOneAsync will return a Task with no result part. And Wait will return a void. – Fredrik May 20 '15 at 09:35
  • 1
    Thank you. Just one more thing. How do I find the duplicate key information in a MongoWriteException? `catch (AggregateException e) { e.Handle((x) => { if (x is MongoWriteException) { } return true; });` – Fredrik May 21 '15 at 06:47
  • 1
    If a MongoWriteException is thrown, and the exception argument is named mwx, is it even possible mwx to be null? In other words - is the mwx != null check really necessary? – CedricB Dec 11 '15 at 23:52
  • @DevilDog74 Good catch -- edited. That was left over from the first implementation where it was needed. – JohnnyHK Dec 12 '15 at 04:24
  • `await` will not throw an `AggregateException`. It will throw the actual exception. – i3arnon Feb 17 '16 at 08:25
  • it's odd, when I use `await` the thread exits with status 0 without exceptions and doesn't insert anything. but with Wait() I lose the async call purpose and it becomes a sync call but it works with the same code. the `InsertManyAsync` seem to be broken and not working asynchronously. – CME64 Oct 31 '18 at 13:07
2

This is an async Task, you're missing the await

await commandsCollection.InsertOneAsync(bson);

https://github.com/mongodb/mongo-csharp-driver/blob/master/README.md

brduca
  • 3,573
  • 2
  • 22
  • 30
  • 1
    Okej, I added the await but now the C# program just terminates quiet where the insert is. I want a exception thrown in my face ^^ – Fredrik May 07 '15 at 14:12
  • have you tried to explicitly save the collection? commandsCollection.Save() – brduca May 07 '15 at 14:50
  • 1
    Unfortunately such a command does not exists in the new version of the C# driver. [link](http://stackoverflow.com/questions/29327856/how-to-insert-data-into-a-mongodb-collection-using-c) The second response. – Fredrik May 08 '15 at 05:56
  • Does it work when it's synchronous? Your code seems to be OK. Maybe it's a problem connecting to the server. – brduca May 08 '15 at 08:49
  • Yes, or I don't know what's happens. Because I insert the exact same values I can't determine of the values are the "new" ones or the "old" ones. And I can't seem to find any good documentation with the async methods. – Fredrik May 08 '15 at 09:08
1

Further to @JonnyHK reply you can do the same when inserting many.

collection.InsertManyAsync(doc, new InsertManyOptions { IsOrdered = false }).Wait();

would be wrapped in try/catch;

try
{
    collection.InsertManyAsync(doc, new InsertManyOptions { IsOrdered = false }).Wait();
}
catch (AggregateException aggEx)
{
    aggEx.Handle(x =>
    {
        var mwx = x as MongoBulkWriteException;
        return mwx != null && mwx.WriteErrors.All(e => e.Category == ServerErrorCategory.DuplicateKey);
    });
}
Aaron Bamford
  • 532
  • 1
  • 4
  • 11