public TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint = AddOrUpdateHint.ProbablyExists) { var result = default(TEntity); // Adds a key/value pair to the ConcurrentDictionary<TKey, TEnitity> if the key does not exist, // or updates a key/value pair in the ConcurrentDictionary<TKey, TEntity> if the key already exists. _store.AddOrUpdate(GetName(key), s => { result = addFactory(); using (var memory = new MemoryStream()) { _strategy.Serialize(result, memory); return(memory.ToArray()); } }, (s, 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 TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint) { var result = default(TEntity); _store.AddOrUpdate(GetName(key), 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 TEntity AddOrUpdate(TKey key, Func <TEntity> addViewFactory, Func <TEntity, TEntity> updateViewFactory, AddOrUpdateHint hint) { string etag = null; var blob = GetBlobReference(key); TEntity view; try { // atomic entities should be small, so we can use the simple method var 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; blob.UploadByteArray(memory.ToArray(), bro); } return(view); }
public TEntity AddOrUpdate(TKey key, Func <TEntity> addFactory, Func <TEntity, TEntity> update, AddOrUpdateHint hint) { var name = 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)) { 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); result = update(entity); } } // some serializers have nasty habbit of closing the // underling stream using (var mem = new MemoryStream()) { _strategy.Serialize(result, mem); var data = mem.ToArray(); file.Seek(0, SeekOrigin.Begin); file.Write(data, 0, data.Length); // truncate this file file.SetLength(data.Length); } return(result); } } catch (DirectoryNotFoundException) { var s = string.Format( "Container '{0}' does not exist.", _folder); throw new InvalidOperationException(s); } }
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)); } }