/// <summary> /// Given a <paramref name="key"/> either adds a new <typeparamref name="TEntity"/> OR updates an existing one. /// </summary> /// <typeparam name="TKey">The type of the key.</typeparam> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="self">The self.</param> /// <param name="key">The key.</param> /// <param name="newView">The new view that will be saved, if entity does not already exist</param> /// <param name="updateViewFactory">The update method (called to update an existing entity, if it exists).</param> /// <param name="hint">The hint.</param> /// <returns></returns> public static TEntity AddOrUpdate <TKey, TEntity>(this IDocumentWriter <TKey, TEntity> self, TKey key, TEntity newView, Action <TEntity> updateViewFactory, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { return(self.AddOrUpdate(key, () => newView, view => { updateViewFactory(view); return view; }, hint)); }
public static TView UpdateEnforcingNew <TView>(this IDocumentWriter <unit, TView> self, Action <TView> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) where TView : new() { return(self.UpdateEnforcingNew(unit.it, update, hint)); }
/// <summary> /// Given a <paramref name="key"/> either adds a new <typeparamref name="TEntity"/> OR updates an existing one. /// </summary> /// <typeparam name="TKey">The type of the key.</typeparam> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="self">The self.</param> /// <param name="key">The key.</param> /// <param name="addFactory">The add factory (used to create a new entity, if it is not found).</param> /// <param name="update">The update method (called to update an existing entity, if it exists).</param> /// <param name="hint">The hint.</param> /// <returns></returns> public static TEntity AddOrUpdate <TKey, TEntity>(this IDocumentWriter <TKey, TEntity> self, TKey key, Func <TEntity> addFactory, Action <TEntity> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { return(self.AddOrUpdate(key, addFactory, entity => { update(entity); return entity; }, hint)); }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint) { var id = _entityPrefix + _strategy.GetNameForEntity(typeof(TEntity), key); var result = default(TEntity); _store.AddOrUpdate(id, s => { result = addFactory(); using (var memory = new MemoryStream()) { _strategy.Serialize(result, memory); return(memory.ToArray()); } }, (s2, bytes) => { TEntity entity; using (var memory = new MemoryStream(bytes)) { entity = _strategy.Deserialize <TEntity>(memory); } result = update(entity); using (var memory = new MemoryStream()) { _strategy.Serialize(result, memory); return(memory.ToArray()); } }); return(result); }
public TView AddOrUpdate(TKey key, Func <TView> addFactory, Func <TView, TView> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { return(_inner.AddOrUpdate(key, addFactory, update, hint)); }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint) { var result = default(TEntity); this._store.AddOrUpdate(this.GetName(key), s => { result = addFactory(); return(result); }, (s2, savedEntity) => { var entity = ( TEntity )savedEntity; result = update(entity); return(result); }); return(result); }
public Task <TEntity> AddOrUpdateAsync(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { var result = this.AddOrUpdate(key, addFactory, update, hint); return(Task.FromResult(result)); }
public async Task <TEntity> AddOrUpdateAsync(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { var name = this.GetName(key); try { // This is fast and allows to have git-style subfolders in atomic strategy // to avoid NTFS performance degradation (when there are more than // 10000 files per folder). Kudos to Gabriel Schenker for pointing this out var subfolder = Path.GetDirectoryName(name); if (subfolder != null && !Directory.Exists(subfolder)) { Directory.CreateDirectory(subfolder); } // we are locking this file. using (var file = File.Open(name, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) { var initial = new byte[0]; TEntity result; if (file.Length == 0) { result = addFactory(); } else { using (var mem = new MemoryStream()) { StorageProfiler.CountReadRequest(name); await file.CopyToAsync(mem); mem.Seek(0, SeekOrigin.Begin); var entity = this._strategy.Deserialize <TEntity>(mem); initial = mem.ToArray(); result = update(entity); } } // some serializers have nasty habbit of closing the // underling stream using (var mem = new MemoryStream()) { this._strategy.Serialize(result, mem); var data = mem.ToArray(); if (!data.SequenceEqual(initial)) { StorageProfiler.CountWriteRequest(name); // upload only if we changed file.Seek(0, SeekOrigin.Begin); await file.WriteAsync(data, 0, data.Length); // truncate this file file.SetLength(data.Length); } } return(result); } } catch (DirectoryNotFoundException) { var s = string.Format( "Container '{0}' does not exist.", this._folder); throw new InvalidOperationException(s); } }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addViewFactory, Func <TEntity, TEntity> updateViewFactory, AddOrUpdateHint hint) { string etag = null; var blob = GetBlobReference(key); TEntity view; byte[] bytes = new byte[0]; try { // atomic entities should be small, so we can use the simple method bytes = blob.DownloadByteArray(); using (var stream = new MemoryStream(bytes)) { view = _strategy.Deserialize <TEntity>(stream); } view = updateViewFactory(view); etag = blob.Attributes.Properties.ETag; } catch (StorageClientException ex) { switch (ex.ErrorCode) { case StorageErrorCode.ContainerNotFound: var s = string.Format( "Container '{0}' does not exist. You need to initialize this atomic storage and ensure that '{1}' is known to '{2}'.", blob.Container.Name, typeof(TEntity).Name, _strategy.GetType().Name); throw new InvalidOperationException(s, ex); case StorageErrorCode.BlobNotFound: case StorageErrorCode.ResourceNotFound: view = addViewFactory(); break; default: throw; } } // atomic entities should be small, so we can use the simple method // http://toolheaven.net/post/Azure-and-blob-write-performance.aspx using (var memory = new MemoryStream()) { _strategy.Serialize(view, memory); // note that upload from stream does weird things var bro = etag != null ? new BlobRequestOptions { AccessCondition = AccessCondition.IfMatch(etag) } : new BlobRequestOptions { AccessCondition = AccessCondition.IfNoneMatch("*") }; // make sure that upload is not rejected due to cashed content MD5 // http://social.msdn.microsoft.com/Forums/hu-HU/windowsazuredata/thread/4764e38f-b200-4efe-ada2-7de442dc4452 blob.Properties.ContentMD5 = null; var content = memory.ToArray(); // upload only if content has changed if (!content.SequenceEqual(bytes)) { blob.UploadByteArray(content, bro); } } return(view); }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint) { return(this.AddOrUpdateAsync(key, addFactory, update, hint).GetAwaiter().GetResult()); }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint) { var name = GetName(key); try { var subfolder = Path.GetDirectoryName(name); if (subfolder != null && !Directory.Exists(subfolder)) { Directory.CreateDirectory(subfolder); } // we are locking the file. using (var file = File.Open(name, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) { byte[] initial = new byte[0]; TEntity result; if (file.Length == 0) { result = addFactory(); } else { using (var mem = new MemoryStream()) { file.CopyTo(mem); mem.Seek(0, SeekOrigin.Begin); var entity = _strategy.Deserialize <TEntity>(mem); initial = mem.ToArray(); result = update(entity); } } using (var mem = new MemoryStream()) { _strategy.Serialize(result, mem); var data = mem.ToArray(); if (!data.SequenceEqual(initial)) { // upload only if we changed file.Seek(0, SeekOrigin.Begin); file.Write(data, 0, data.Length); // truncate this file file.SetLength(data.Length); } } return(result); } } catch (FileNotFoundException) { var s = string.Format("File '{0}' does not exist.", name); throw new InvalidOperationException(s); } catch (DirectoryNotFoundException) { var s = string.Format("Container '{0}' does not exist.", _folder); throw new InvalidOperationException(s); } catch (IOException) // fix for fast read / slow write concurency { _log.WriteLine("File write failed for {0} - {1} with key {2}. Will retry.", _folder, name, key); Thread.Sleep(120); return(AddOrUpdate(key, addFactory, update, hint)); } }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addViewFactory, Func <TEntity, TEntity> updateViewFactory, AddOrUpdateHint hint) { // TODO: implement proper locking and order var blob = GetBlobReference(key); TEntity view; try { using (var stream = blob.OpenRead()) { view = _strategy.Deserialize <TEntity>(stream); } view = updateViewFactory(view); } catch (StorageClientException ex) { switch (ex.ErrorCode) { case StorageErrorCode.ContainerNotFound: var s = string.Format( "Container '{0}' does not exist. You need to initialize this atomic storage and ensure that '{1}' is known to '{2}'.", blob.Container.Name, typeof(TEntity).Name, _strategy.GetType().Name); throw new InvalidOperationException(s, ex); case StorageErrorCode.BlobNotFound: case StorageErrorCode.ResourceNotFound: view = addViewFactory(); break; default: throw; } } using (var mem = blob.OpenWrite()) { _strategy.Serialize(view, mem); } return(view); }
public async Task <TEntity> AddOrUpdateAsync(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { var keyString = this.GetKeyString(key); if (this._viewsCache.ContainsKey(keyString)) { return(this._viewsCache.AddOrUpdate(keyString, k => addFactory(), (k, e) => update(e))); } var loadedEntity = await this._reader.GetAsync(key); loadedEntity.IfValue(e => this._viewsCache.TryAdd(keyString, e)); this.RemoveFromDelete(keyString); return(this._viewsCache.AddOrUpdate(keyString, k => addFactory(), (k, e) => update(e))); }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { return(this.AddOrUpdateAsync(key, addFactory, update, hint).Result); }