public virtual void Add <T>(T instance) where T : class { if (instance == null) { throw new ArgumentNullException(nameof(instance)); } var type = typeof(T); var value = _idAccessor.GetId(instance); var key = _idManager.GetFromId(typeof(T), value); if (key == null) { key = _idManager.GenerateId(type); _idAccessor.SetId(instance, key.Id); } var entry = new SessionEntry() { Action = ActionType.Add, Instance = instance, Key = key }; _sessionCache.Attach(entry); }
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); }
public Task <IEnumerable <LoadContext> > Execute(QueryContext context, Next <QueryContext, IEnumerable <LoadContext> > next) { var ctx = context; var q = ctx.Query; var query = new MongoQueryRequest { Selector = q.Selector, Limit = q.Limit, Skip = q.Skip, Sort = q.Sort }; var allDocs = _couchDb.MongoQuery(query); //using the loaded docs, update the context entries. var results = (from entity in allDocs.Docs let id = _idAccessor.GetId(entity) let key = _idManager.GetFromId(entity.GetType(), id) select new LoadContext() { Entity = entity, Key = key, Type = entity.GetType() }).ToList(); return(Task.FromResult((IEnumerable <LoadContext>)results)); }
public Task Execute(IEnumerable <LoadContext> context, Next <IEnumerable <LoadContext> > next) { var items = context.ToList(); //ensure 1 iteration over list. (tasks to run once) //we do not want to load items which have been loaded from the cache var toLoad = items.Where(x => !x.LoadedFromCache).ToDictionary(loadContext => loadContext.Key.CouchDbId); var allDocs = _couchDb.LoadAllEntities(new AllDocsRequest() { Keys = toLoad.Keys }); //using the loaded docs, update the context entries. foreach (var entity in allDocs.Rows.Select(x => x.Doc)) { var id = _idAccessor.GetId(entity); var key = _idManager.GetFromId(entity.GetType(), id); toLoad[key.CouchDbId].Entity = entity; } return(Task.CompletedTask); }
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"))); } }