Esempio n. 1
0
        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);
        }