/// <summary>
        /// Writes an input stream to an entry in the blob storage specified by the provided token.
        /// </summary>
        /// <param name="versionId">Content version id.</param>
        /// <param name="token">Blob token provided by a preliminary request.</param>
        /// <param name="input">The whole stream to write.</param>
        protected internal static async Task CopyFromStreamAsync(int versionId, string token, Stream input)
        {
            var tokenData = ChunkToken.Parse(token, versionId);

            try
            {
                using (var tran = SnTransaction.Begin())
                {
                    var context = await GetBlobStorageContextAsync(tokenData.FileId, true, versionId, tokenData.PropertyTypeId);

                    if (context.Provider == BuiltInProvider)
                    {
                        // Our built-in provider does not have a special stream for the case when
                        // the binary should be saved into a regular SQL varbinary column.
                        await CopyFromStreamByChunksAsync(context, input);
                    }
                    else
                    {
                        // This is the recommended way to write a stream to the binary storage.
                        using (var targetStream = context.Provider.GetStreamForWrite(context))
                            await input.CopyToAsync(targetStream);
                    }

                    tran.Commit();
                }
            }
            catch (Exception e)
            {
                throw new DataException("Error during saving binary chunk to stream.", e);
            }
        }
        /// <summary>
        /// Creates a new SnTransaction object and starts a db transaction if the
        /// TransactionScope is not yet active. Call this in a 'using' statement.
        /// </summary>
        /// <param name="isolationLevel">Transaction isolation level.</param>
        /// <param name="timeout">Timeout for the transaction.</param>
        /// <returns>A disposable SnTransaction object.</returns>
        public static SnTransaction Begin(IsolationLevel?isolationLevel = null, TimeSpan?timeout = null)
        {
            var tran = new SnTransaction
            {
                _isLocalTransaction = !TransactionScope.IsActive
            };

            if (tran._isLocalTransaction)
            {
                TransactionScope.Begin(
                    isolationLevel ?? IsolationLevel.ReadCommitted,
                    timeout ?? TimeSpan.FromSeconds(Configuration.Data.TransactionTimeout));
            }

            return(tran);
        }
        /// <summary>
        /// Writes a byte array to the blob entry specified by the provided token.
        /// </summary>
        /// <param name="versionId">Content version id.</param>
        /// <param name="token">Blob token provided by a preliminary request.</param>
        /// <param name="buffer">Byte array to write.</param>
        /// <param name="offset">Starting position.</param>
        /// <param name="fullSize">Full size of the whole stream.</param>
        protected internal static async Task WriteChunkAsync(int versionId, string token, byte[] buffer, long offset, long fullSize)
        {
            var tokenData = ChunkToken.Parse(token, versionId);

            using (var tran = SnTransaction.Begin())
            {
                try
                {
                    var ctx = await GetBlobStorageContextAsync(tokenData.FileId);

                    // must update properties because the Length contains the actual saved size but the featue needs the full size
                    UpdateContextProperties(ctx, versionId, tokenData.PropertyTypeId, fullSize);

                    await ctx.Provider.WriteAsync(ctx, offset, buffer);

                    tran.Commit();
                }
                catch (Exception ex)
                {
                    throw new DataException("Error during saving binary chunk to stream.", ex);
                }
            }
        }