public async Task <T> Save(T instance, bool saveChangedOnly = true) { var serialKey = _storageKeyGenerator.GetSerialKey(_typeDefinition); var clusterIndexKey = _storageKeyGenerator.GetClusteredIndexKey(_typeDefinition); var _val = _valueConverter.ResolveId(_typeDefinition, instance); RedisValue id = _val.Item1; bool wasIdSet = _val.Item2; RedisKey key; var existingIndexes = new RedisKey[0]; var existingRecord = new HashEntry[0]; long serial = -1; if (!wasIdSet) { // this is a new object so we need to add it to the clustered index... serial = await _database.StringIncrementAsync(serialKey).ConfigureAwait(false); // guid ids are auto generated and not null if (id.IsNull) { id = serial; } // and set the id on the object _typeDefinition.Accessor[instance, _typeDefinition.PrimaryKey.Name] = _valueConverter.ToObject(id, _typeDefinition.PrimaryKey.Type); key = _storageKeyGenerator.GetKey(_typeDefinition, id); } else { string rawIndexes = null; key = _storageKeyGenerator.GetKey(_typeDefinition, id); if (saveChangedOnly) { existingRecord = await _database.HashGetAllAsync(key).ConfigureAwait(false); rawIndexes = existingRecord.FirstOrDefault(h => h.Name == Constants.RoidHashFieldIndexes).Value; } else { rawIndexes = await _database.HashGetAsync(key, Constants.RoidHashFieldIndexes).ConfigureAwait(false); } // if rawIndexes is null, that means we have not yet saved the object // id is user provided in this case hence we need to add it to the clustered index if (string.IsNullOrWhiteSpace(rawIndexes)) { serial = await _database.StringIncrementAsync(serialKey).ConfigureAwait(false); } else { existingIndexes = rawIndexes.Split(new[] { Constants.Separator }, StringSplitOptions.RemoveEmptyEntries) .Select(i => (RedisKey)i) .ToArray(); } } bool isNew = serial != -1; var hash = _mapper.HashFor(_typeDefinition, instance, isNew, existingRecord); var indexes = _indexer.GetIndexes(_typeDefinition, hash); var indexesToRemove = existingIndexes.Where(i => !indexes.Contains(i)).ToArray(); var indexesToAdd = indexes.Where(i => !existingIndexes.Contains(i)).ToArray(); #region save to redis var keyString = (string)key; var txn = _database.CreateTransaction(); #pragma warning disable CS4014 txn.HashSetAsync(key, hash.ToArray()); if (isNew) { txn.SortedSetAddAsync(clusterIndexKey, new[] { new SortedSetEntry(keyString, serial) }); } foreach (var index in indexesToAdd) { txn.SetAddAsync(index, keyString); } foreach (var index in indexesToRemove) { txn.SetRemoveAsync(index, keyString); } #pragma warning restore CS4014 if (!await txn.ExecuteAsync().ConfigureAwait(false)) { throw new RoidException("Failed to commit the transaction"); } #endregion save to redis return(instance); }