|
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