/// <summary> /// Attempt to insert an entity, leaving any exceptions go /// </summary> private void AttemptInsert(string type, Entity entity) { JsonObject document = SerializeEntity(entity); // make sure _key and _rev are not even present // (they will be set automatically to null, but arango // requires them to not be present at all - null is not enough) document.Remove("_key"); document.Remove("_rev"); // set timestamps // (serializer instead of direct setting, because the serializer // does not store "Z" (timezone), so deserialization does not // correct for timezones and loads it as it is) DateTime now = DateTime.UtcNow; document["CreatedAt"] = Serializer.ToJson(now); document["UpdatedAt"] = Serializer.ToJson(now); var newAttributes = arango.ExecuteAqlQuery(new AqlQuery() .Insert(document).Into(EntityUtils.CollectionFromType(type)) .Return("NEW") ).First().AsJsonObject; // update entity attributes EntitySerializer.SetAttributes( entity, newAttributes, DeserializationContext.ServerStorageToServer ); }
/// <summary> /// Updates the entity in the database and returns the updated entity /// Careful insert checks revisions to detect write-write conflicts /// </summary> public virtual void Update( Entity entity, bool carefully = false ) { string type = EntityUtils.GetEntityStringType(entity.GetType()); if (string.IsNullOrEmpty(entity.EntityKey)) { throw new ArgumentException( "Provided entity has not been inserted yet" ); } try { JsonObject oldDocument = FindDocument(entity.EntityId); if (oldDocument == null) { throw new ArangoException(404, 1202, "document not found"); } JsonObject newDocument = SerializeEntity(entity); // set timestamps // (serializer instead of direct setting, because the serializer // does not store "Z" (timezone), so deserialization does not // correct for timezones and loads it as it is) newDocument["CreatedAt"] = oldDocument["CreatedAt"]; newDocument["UpdatedAt"] = Serializer.ToJson(DateTime.UtcNow); // if we aren't careful, then don't even send the _rev key // (just to make sure the database won't throw a conflict) // (this probably doesn't help with anything, but why not do it) if (!carefully) { newDocument.Remove("_rev"); } var newAttributes = arango.ExecuteAqlQuery(new AqlQuery() .Replace(() => newDocument) .CheckRevs(carefully) .In(EntityUtils.CollectionFromType(type)) .Return("NEW") ).First().AsJsonObject; EntitySerializer.SetAttributes( entity, newAttributes, DeserializationContext.ServerStorageToServer ); } catch (ArangoException e) when(e.ErrorNumber == 1200) { // write-write conflict /* * Write-write conflict (1200) can be thrown even when we * don't explicitly check for revisions. This comes down * to the way ArangoDB works. If it tries to acquire a write * lock on a document and fails, it doesn't retry and throws * the conflict error (it fails because someone else is * currently writing to that document). Giving up on the write * is ok in this situation as it could be though of as the * other process immediately overwriting our update. * * https://github.com/arangodb/arangodb/issues/9702 */ // When we are careful, we need to notify the user by // throwing a conflict exception if (carefully) { throw new EntityRevConflictException( "Entity has been modified since the last refresh" ); } // When we aren't careful, we just warn the user and // ignore the fact the write failed. (see the note above) log.Warning( "Entity wasn't saved due to a write-write conflict " + "(other process was saving the same entity at the " + "same time).\nCheck out documentation for the method " + "entity.SaveCarefully() to learn more.", e ); } catch (ArangoException e) when(e.ErrorNumber == 1202) { // document not found throw new EntityPersistenceException( "Entity has not yet been inserted, or already deleted" ); } catch (ArangoException e) when(e.ErrorNumber == 1203) { // collection not found throw new EntityPersistenceException( "Entity has not yet been inserted, or already deleted" ); } }