public ITransaction Setup(IEnumerable <object> items)
        {
            var txnEntries = items.Select(x =>
            {
                var type = x.GetType().Name;
                var id   = _idAccessor.GetId(x)?.ToString();
                var rev  = _revisionAccessor.GetRevision(x)?.ToString();
                var txn  = new TransactionEntry()
                {
                    DateTime  = DateTime.UtcNow,
                    Id        = $"{id}-{type}",
                    ActualId  = id,
                    ActualRev = rev,
                    Type      = type
                };
                return(txn);
            });

            var transaction = new Transaction(_couchDb, _idAccessor, _revisionAccessor);

            try
            {
                transaction.Init(txnEntries);
            }
            catch (Exception e)
            {
                transaction.Rollback(e);
                throw;
            }

            return(transaction);
        }
Exemplo n.º 2
0
        public Task Execute(IEnumerable <CommitContext> items, Next <IEnumerable <CommitContext> > next)
        {
            items = items.ToList(); //ensure 1 iteration over list. (tasks to run once)

            //do not call the db if we have no items to transact on.
            if (!items.Any())
            {
                return(Task.CompletedTask);
            }

            //setup the items for the database commit.
            var entityUpdates = new Dictionary <string, object>();
            var docRequests   = new List <BulkDocRequest>();

            foreach (var bulkContext in items)
            {
                var entry = new BulkDocRequest
                {
                    Content = bulkContext.Entity,
                    Id      = bulkContext.Key.CouchDbId
                };

                switch (bulkContext.ActionType)
                {
                case ActionType.Add:
                    break;

                case ActionType.Update:
                    break;

                case ActionType.Delete:
                    entry.Rev    = (string)_revisionAccessor.GetRevision(bulkContext.Entity);
                    entry.Delete = true;
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

                docRequests.Add(entry);
                entityUpdates.Add(entry.Id, bulkContext.Entity);
            }

            //apply the commit
            var request = new BulkDocsRequest {
                Docs = docRequests
            };
            var updates = _couchDb.BulkApplyChanges(request);

            //update any revisions!
            foreach (var update in updates)
            {
                var entity = entityUpdates[update.Id];
                _revisionAccessor.SetRevision(entity, update.Rev);
            }

            return(Task.CompletedTask);
        }
Exemplo n.º 3
0
        public void Init(IEnumerable <TransactionEntry> txnEntries)
        {
            _txnEntries = new Dictionary <string, TransactionEntry>();
            foreach (var txnEntry in txnEntries)
            {
                txnEntry.TransactionId = _id;
                _txnEntries.Add(txnEntry.ActualId, txnEntry);
            }

            var now = DateTime.UtcNow;

            bool HasExpired(DateTime time) => new TimeSpan(now.Ticks - time.Ticks).TotalSeconds > 5;

            var existingTransactionEntries = _couchDb
                                             .LoadAllEntities(
                new AllDocsRequest()
            {
                Keys = _txnEntries.Values.Select(x => x.Id)        //checking against transaction key for documents
            })
                                             .Rows
                                             .Select(x => x.Doc)
                                             .Cast <TransactionEntry>()
                                             .ToList();

            var hasTransactionLockConflict = existingTransactionEntries.Any(x => HasExpired(x.DateTime));

            if (hasTransactionLockConflict)
            {
                throw new TransactionException(existingTransactionEntries.Select(x => x.Id).ToList());
            }

            var payload = new BulkDocsRequest()
            {
                Docs = _txnEntries.Values.Select(
                    txn => new BulkDocRequest()
                {
                    Id      = txn.Id,
                    Content = txn
                })
            };

            _results = _couchDb.BulkApplyChanges(payload).ToList();

            //now we have a txn (lock) we confirm the target docs are still on the same rev
            var conflictingDocs = _couchDb.LoadAllEntities(
                new AllDocsRequest()
            {
                Keys = _txnEntries.Values.Select(x => x.ActualId)
            })
                                  .Rows
                                  .Select(x => x.Doc)
                                  .Select(x => new
            {
                Id  = _idAccessor.GetId(x).ToString(),
                Rev = _revisionAccessor.GetRevision(x)?.ToString()
            })
                                  .Where(x =>
            {
                if (!_txnEntries.TryGetValue(x.Id, out var entry))
                {
                    return(false);
                }
                return(entry.ActualRev != x.Rev);
            })
                                  .ToList();

            if (conflictingDocs.Any())
            {
                throw new AggregateException(conflictingDocs.Select(x => new ConflictException(x.Id, x.Rev, "conflict", "stale reference")));
            }
        }