|
21 | 21 | using MongoDB.Bson; |
22 | 22 | using MongoDB.Bson.IO; |
23 | 23 | using MongoDB.Bson.Serialization; |
| 24 | +using MongoDB.Bson.Serialization.Options; |
24 | 25 | using MongoDB.Bson.Serialization.Serializers; |
25 | 26 | using MongoDB.Driver.Builders; |
26 | 27 | using MongoDB.Driver.Internal; |
@@ -1393,71 +1394,65 @@ public virtual WriteConcernResult Save(Type nominalType, object document) |
1393 | 1394 | /// <returns>A WriteConcernResult (or null if WriteConcern is disabled).</returns> |
1394 | 1395 | public virtual WriteConcernResult Save(Type nominalType, object document, MongoInsertOptions options) |
1395 | 1396 | { |
| 1397 | + if (nominalType == null) |
| 1398 | + { |
| 1399 | + throw new ArgumentNullException("nominalType"); |
| 1400 | + } |
1396 | 1401 | if (document == null) |
1397 | 1402 | { |
1398 | 1403 | throw new ArgumentNullException("document"); |
1399 | 1404 | } |
| 1405 | + |
1400 | 1406 | 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 |
1401 | 1409 | 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) |
1406 | 1411 | { |
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)) |
1408 | 1418 | { |
1409 | 1419 | throw new InvalidOperationException("No IdGenerator found."); |
1410 | 1420 | } |
1411 | 1421 |
|
1412 | | - if (idGenerator != null && idGenerator.IsEmpty(id)) |
| 1422 | + if (idGenerator != null && (!hasId || idGenerator.IsEmpty(id))) |
1413 | 1423 | { |
1414 | 1424 | id = idGenerator.GenerateId(this, document); |
1415 | 1425 | idProvider.SetDocumentId(document, id); |
1416 | 1426 | return Insert(nominalType, document, options); |
1417 | 1427 | } |
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 | + } |
1445 | 1429 |
|
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); |
1456 | 1438 | } |
1457 | | - else |
| 1439 | + |
| 1440 | + BsonValue idBsonValue; |
| 1441 | + if (!bsonDocument.TryGetValue("_id", out idBsonValue)) |
1458 | 1442 | { |
1459 | 1443 | throw new InvalidOperationException("Save can only be used with documents that have an Id."); |
1460 | 1444 | } |
| 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); |
1461 | 1456 | } |
1462 | 1457 |
|
1463 | 1458 | /// <summary> |
|
0 commit comments