public Guid Write(byte[] data) { var offset = 0; byte[] buffer = null; var id = Guid.Empty; while (offset < data.Length) { var nextBlockSize = Math.Min(data.Length - offset, BinaryDataStorageClass.MaxBufferSize); if (buffer == null || buffer.Length != nextBlockSize) { buffer = new byte[nextBlockSize]; } Array.Copy(data, offset, buffer, 0, nextBlockSize); var nextBlock = new BinaryDataStorageClass { Bytes = buffer, Id = Guid.NewGuid(), NextBlock = id, RemainingBytes = offset + nextBlockSize }; _file.Write(nextBlock); offset += nextBlockSize; id = nextBlock.Id; } return(id); }
/// <summary> /// Add a binary to the file and store the data in the data cache. /// /// It should be noted that there is a failure mode in which the commit of the binary details file could work, but the commit /// of the data could fail. This would result in the record of the binary being stored, but no actual data would be present /// in the files. This would manifest itself as an exception, which is caught here, and an attempt to remove the binary details /// is made. In the event that the data commit fails because of catastrophic failure or power loss, the recovery attempt may not /// take place or may itself fail. This would leave the data in an inconsistent state, but only for the most recently saved binary. /// /// On open, the most recently saved binary is checked, to see if the data is present. If it is not, it is removed at that stage. /// On the assumption that the failure to /// </summary> /// <param name="details"></param> /// <param name="data"></param> /// <returns></returns> public Guid Add(BinaryStorageClass details, byte[] data) { lock (_lock) { try { if (details.Id == Guid.Empty) { details.Id = Guid.NewGuid(); } details.FileIndex = _nextIndex++; details.FirstDataBlockId = _data.Write(data); _file.Write(details); _file.Commit(); _binaries.Add(details.Id, details); //Make sure that the file is removed if commit fails. Action reverseOutFile = () => { // ReSharper disable AccessToDisposedClosure //Note: Resharper cannot determine whether the lambda will outlive the try/catch block. It can't so we can ignore the warning. _file.Delete(details); _file.Commit(); // ReSharper restore AccessToDisposedClosure }; using (var reverse = new OnError(reverseOutFile)) { _data.Commit(); reverse.Commit(); //if we reach here, the commit worked and we don't need to reverse out the file. } return(details.Id); } catch { _file.Dispose(); _file = null; Open(_path); _data.ReOpen(); throw; } } }