Skip to content

Commit 0da8811

Browse files
authored
CSHARP-5697: Fix Client.BulkWrite failure in case complex type is being used as Document's Id (#1749)
1 parent 3b1dc42 commit 0da8811

File tree

6 files changed

+128
-8
lines changed

6 files changed

+128
-8
lines changed

src/MongoDB.Driver/BulkWriteInsertOneResult.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
1617
using MongoDB.Bson;
1718

1819
namespace MongoDB.Driver
@@ -25,6 +26,19 @@ public class BulkWriteInsertOneResult
2526
/// <summary>
2627
/// The id of the inserted document.
2728
/// </summary>
28-
public BsonValue InsertedId { get; init; }
29+
[Obsolete("InsertedId is deprecated and will be removed in future versions. Use DocumentId instead.")]
30+
public BsonValue InsertedId
31+
{
32+
get => BsonValue.Create(DocumentId);
33+
init
34+
{
35+
DocumentId = value;
36+
}
37+
}
38+
39+
/// <summary>
40+
/// The id of the inserted document.
41+
/// </summary>
42+
public object DocumentId { get; init; }
2943
}
3044
}

src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal sealed class ClientBulkWriteOperation : RetryableWriteCommandOperationB
3333
{
3434
private readonly bool? _bypassDocumentValidation;
3535
private readonly bool _errorsOnly;
36-
private readonly Dictionary<int, BsonValue> _idsMap = new();
36+
private readonly Dictionary<int, object> _idsMap = new();
3737
private readonly BsonDocument _let;
3838
private readonly RenderArgs<BsonDocument> _renderArgs;
3939
private readonly IBatchableSource<BulkWriteModel> _writeModels;
@@ -331,7 +331,7 @@ private void PopulateIndividualResponses(IEnumerable<BsonDocument> individualRes
331331
_idsMap.TryGetValue(operationIndex, out var insertedId);
332332
bulkWriteResult.InsertResults.Add(operationIndex, new()
333333
{
334-
InsertedId = insertedId
334+
DocumentId = insertedId
335335
});
336336
}
337337
else if (writeModelType == typeof(BulkWriteUpdateOneModel<>) || writeModelType == typeof(BulkWriteUpdateManyModel<>) || writeModelType == typeof(BulkWriteReplaceOneModel<>))

src/MongoDB.Driver/Core/WireProtocol/Messages/CommandMessageSection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ internal sealed class ClientBulkWriteOpsCommandMessageSection : BatchableCommand
133133
{
134134
public ClientBulkWriteOpsCommandMessageSection(
135135
IBatchableSource<BulkWriteModel> operations,
136-
Dictionary<int, BsonValue> idsMap,
136+
Dictionary<int, object> idsMap,
137137
int? maxBatchCount,
138138
int? maxDocumentSize,
139139
RenderArgs<BsonDocument> renderArgs)
@@ -144,7 +144,7 @@ public ClientBulkWriteOpsCommandMessageSection(
144144
RenderArgs = renderArgs;
145145
}
146146

147-
public Dictionary<int, BsonValue> IdsMap { get; }
147+
public Dictionary<int, object> IdsMap { get; }
148148
public new IBatchableSource<BulkWriteModel> Documents { get; }
149149
public RenderArgs<BsonDocument> RenderArgs { get; }
150150
}

src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ClientBulkWriteOpsSectionFormatter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ internal sealed class ClientBulkWriteOpsSectionFormatter : ICommandMessageSectio
3232
private MemoryStream _nsInfoMemoryStream;
3333
private BsonBinaryWriter _nsInfoWriter;
3434
private IBsonSerializerRegistry _serializerRegistry;
35-
private Dictionary<int, BsonValue> _idsMap;
35+
private Dictionary<int, object> _idsMap;
3636
private int _currentIndex;
3737

3838
public ClientBulkWriteOpsSectionFormatter(long? maxSize)
@@ -150,7 +150,7 @@ public void RenderInsertOne<TDocument>(RenderArgs<BsonDocument> renderArgs, Bson
150150
WriteStartModel(serializationContext, "insert", model);
151151
var documentSerializer = _serializerRegistry.GetSerializer<TDocument>();
152152
var documentId = documentSerializer.SetDocumentIdIfMissing(null, model.Document);
153-
_idsMap[_currentIndex] = BsonValue.Create(documentId);
153+
_idsMap[_currentIndex] = documentId;
154154
serializationContext.Writer.WriteName("document");
155155
documentSerializer.Serialize(serializationContext, model.Document);
156156
WriteEndModel(serializationContext);
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using System.Threading.Tasks;
19+
using FluentAssertions;
20+
using MongoDB.Bson.Serialization;
21+
using MongoDB.Driver.Core.Misc;
22+
using MongoDB.Driver.TestHelpers;
23+
using MongoDB.TestHelpers.XunitExtensions;
24+
using Xunit;
25+
26+
namespace MongoDB.Driver.Tests.Jira;
27+
28+
public class CSharp5697Tests : IntegrationTest<CSharp5697Tests.ClassFixture>
29+
{
30+
public CSharp5697Tests(ClassFixture fixture)
31+
: base(fixture, server => server.Supports(Feature.ClientBulkWrite))
32+
{
33+
}
34+
35+
[Theory]
36+
[ParameterAttributeData]
37+
public async Task ClientBulkWrite_supports_complex_id([Values(true, false)] bool async)
38+
{
39+
var id = async ? "1" : "2";
40+
var options = new ClientBulkWriteOptions { VerboseResult = true };
41+
BulkWriteModel[] models =
42+
[
43+
new BulkWriteInsertOneModel<Document>(
44+
Fixture.Collection.CollectionNamespace,
45+
new Document(new DocumentId(id)))
46+
];
47+
48+
var result = async ?
49+
await Fixture.Client.BulkWriteAsync(models, options) :
50+
Fixture.Client.BulkWrite(models, options);
51+
52+
result.InsertResults[0].DocumentId.Should().BeOfType<DocumentId>()
53+
.Subject.Key.Should().Be(id);
54+
}
55+
56+
public class Document
57+
{
58+
public Document(DocumentId id)
59+
{
60+
Id = id;
61+
}
62+
63+
public DocumentId Id { get; }
64+
}
65+
66+
public class DocumentId
67+
{
68+
public DocumentId(string key)
69+
{
70+
Key = key;
71+
}
72+
73+
public string Key { get; }
74+
}
75+
76+
public sealed class DocumentIdSerializer : IBsonSerializer<DocumentId>
77+
{
78+
public Type ValueType => typeof(DocumentId);
79+
80+
public DocumentId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
81+
=> new DocumentId(context.Reader.ReadString());
82+
83+
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DocumentId value)
84+
=> context.Writer.WriteString(value.Key);
85+
86+
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
87+
{
88+
var id = (DocumentId)value;
89+
Serialize(context, args, id);
90+
}
91+
92+
object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
93+
=> Deserialize(context, args);
94+
}
95+
96+
public class ClassFixture : MongoCollectionFixture<Document>
97+
{
98+
public ClassFixture()
99+
{
100+
BsonSerializer.RegisterSerializer( new DocumentIdSerializer());
101+
}
102+
103+
protected override IEnumerable<Document> InitialData => null;
104+
}
105+
}
106+

tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedClientBulkWriteOperation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public static BsonDocument ConvertClientBulkWriteResult(ClientBulkWriteResult re
102102
{ "deletedCount", (int)result.DeletedCount },
103103
{
104104
"insertResults", ConvertResults(result.InsertResults,
105-
item => new() { { "insertedId", item.InsertedId } })
105+
item => new() { { "insertedId", BsonValue.Create(item.DocumentId) } })
106106
},
107107
{
108108
"updateResults", ConvertResults(result.UpdateResults,

0 commit comments

Comments
 (0)