Skip to content

Commit 48aae83

Browse files
author
rstam
committed
CSHARP-810: Save should work with a polymorphic _id (or any custom serialization of an _id).
1 parent de66bed commit 48aae83

File tree

1 file changed

+39
-44
lines changed

1 file changed

+39
-44
lines changed

MongoDB.Driver/MongoCollection.cs

+39-44
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using MongoDB.Bson;
2222
using MongoDB.Bson.IO;
2323
using MongoDB.Bson.Serialization;
24+
using MongoDB.Bson.Serialization.Options;
2425
using MongoDB.Bson.Serialization.Serializers;
2526
using MongoDB.Driver.Builders;
2627
using MongoDB.Driver.Internal;
@@ -1393,71 +1394,65 @@ public virtual WriteConcernResult Save(Type nominalType, object document)
13931394
/// <returns>A WriteConcernResult (or null if WriteConcern is disabled).</returns>
13941395
public virtual WriteConcernResult Save(Type nominalType, object document, MongoInsertOptions options)
13951396
{
1397+
if (nominalType == null)
1398+
{
1399+
throw new ArgumentNullException("nominalType");
1400+
}
13961401
if (document == null)
13971402
{
13981403
throw new ArgumentNullException("document");
13991404
}
1405+
14001406
var serializer = BsonSerializer.LookupSerializer(document.GetType());
1407+
1408+
// if we can determine for sure that it is a new document and we can generate an Id for it then insert it
14011409
var idProvider = serializer as IBsonIdProvider;
1402-
object id;
1403-
Type idNominalType;
1404-
IIdGenerator idGenerator;
1405-
if (idProvider != null && idProvider.GetDocumentId(document, out id, out idNominalType, out idGenerator))
1410+
if (idProvider != null)
14061411
{
1407-
if (id == null && idGenerator == null)
1412+
object id;
1413+
Type idNominalType;
1414+
IIdGenerator idGenerator;
1415+
var hasId = idProvider.GetDocumentId(document, out id, out idNominalType, out idGenerator);
1416+
1417+
if (idGenerator == null && (!hasId || id == null))
14081418
{
14091419
throw new InvalidOperationException("No IdGenerator found.");
14101420
}
14111421

1412-
if (idGenerator != null && idGenerator.IsEmpty(id))
1422+
if (idGenerator != null && (!hasId || idGenerator.IsEmpty(id)))
14131423
{
14141424
id = idGenerator.GenerateId(this, document);
14151425
idProvider.SetDocumentId(document, id);
14161426
return Insert(nominalType, document, options);
14171427
}
1418-
else
1419-
{
1420-
BsonValue idBsonValue;
1421-
var documentType = document.GetType();
1422-
if (BsonClassMap.IsClassMapRegistered(documentType))
1423-
{
1424-
var classMap = BsonClassMap.LookupClassMap(documentType);
1425-
var idMemberMap = classMap.IdMemberMap;
1426-
var idSerializer = idMemberMap.GetSerializer(id.GetType());
1427-
// we only care about the serialized _id value but we need a dummy document to serialize it into
1428-
var bsonDocument = new BsonDocument();
1429-
var bsonDocumentWriterSettings = new BsonDocumentWriterSettings
1430-
{
1431-
GuidRepresentation = _settings.GuidRepresentation
1432-
};
1433-
var bsonWriter = new BsonDocumentWriter(bsonDocument, bsonDocumentWriterSettings);
1434-
bsonWriter.WriteStartDocument();
1435-
bsonWriter.WriteName("_id");
1436-
idSerializer.Serialize(bsonWriter, id.GetType(), id, idMemberMap.SerializationOptions);
1437-
bsonWriter.WriteEndDocument();
1438-
idBsonValue = bsonDocument[0]; // extract the _id value from the dummy document
1439-
} else {
1440-
if (!BsonTypeMapper.TryMapToBsonValue(id, out idBsonValue))
1441-
{
1442-
idBsonValue = BsonDocumentWrapper.Create(idNominalType, id);
1443-
}
1444-
}
1428+
}
14451429

1446-
var query = Query.EQ("_id", idBsonValue);
1447-
var update = Builders.Update.Replace(nominalType, document);
1448-
var updateOptions = new MongoUpdateOptions
1449-
{
1450-
CheckElementNames = options.CheckElementNames,
1451-
Flags = UpdateFlags.Upsert,
1452-
WriteConcern = options.WriteConcern
1453-
};
1454-
return Update(query, update, updateOptions);
1455-
}
1430+
// since we can't determine for sure whether it's a new document or not upsert it
1431+
// the only safe way to get the serialized _id value needed for the query is to serialize the entire document
1432+
1433+
var bsonDocument = new BsonDocument();
1434+
var writerSettings = new BsonDocumentWriterSettings { GuidRepresentation = _settings.GuidRepresentation };
1435+
using (var bsonWriter = new BsonDocumentWriter(bsonDocument, writerSettings))
1436+
{
1437+
serializer.Serialize(bsonWriter, nominalType, document, null);
14561438
}
1457-
else
1439+
1440+
BsonValue idBsonValue;
1441+
if (!bsonDocument.TryGetValue("_id", out idBsonValue))
14581442
{
14591443
throw new InvalidOperationException("Save can only be used with documents that have an Id.");
14601444
}
1445+
1446+
var query = Query.EQ("_id", idBsonValue);
1447+
var update = Builders.Update.Replace(bsonDocument);
1448+
var updateOptions = new MongoUpdateOptions
1449+
{
1450+
CheckElementNames = options.CheckElementNames,
1451+
Flags = UpdateFlags.Upsert,
1452+
WriteConcern = options.WriteConcern
1453+
};
1454+
1455+
return Update(query, update, updateOptions);
14611456
}
14621457

14631458
/// <summary>

0 commit comments

Comments
 (0)