/// <remarks>Upsert is making several storage calls to emulate the
        /// missing semantic from the Table Storage.</remarks>
        void UpsertInternal <T>(string tableName, IEnumerable <CloudEntity <T> > entities)
        {
            var context = _tableStorage.GetTableServiceContext();

            context.MergeOption = MergeOption.AppendOnly;
            context.ResolveType = ResolveFatEntityType;

            var stopwatch = new Stopwatch();

            var fatEntities = entities.Select(e => Tuple.Create(FatEntity.Convert(e, _serializer), e));

            var noBatchMode = false;

            foreach (var slice in SliceEntities(fatEntities, e => e.Item1.GetPayload()))
            {
                stopwatch.Restart();

                var cloudEntityOfFatEntity = new Dictionary <object, CloudEntity <T> >();
                foreach (var fatEntity in slice)
                {
                    // entities should be updated in a single round-trip
                    context.AttachTo(tableName, fatEntity.Item1);
                    context.UpdateObject(fatEntity.Item1);
                    cloudEntityOfFatEntity.Add(fatEntity.Item1, fatEntity.Item2);
                }

                Retry.Do(_policies.TransientTableErrorBackOff(), CancellationToken.None, () =>
                {
                    try
                    {
                        context.SaveChanges(noBatchMode ? SaveChangesOptions.ReplaceOnUpdate : SaveChangesOptions.ReplaceOnUpdate | SaveChangesOptions.Batch);
                        ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                    }
                    catch (DataServiceRequestException ex)
                    {
                        var errorCode = RetryPolicies.GetErrorCode(ex);

                        if (errorCode == StorageErrorCodeStrings.OperationTimedOut)
                        {
                            // if batch does not work, then split into elementary requests
                            // PERF: it would be better to split the request in two and retry
                            context.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
                            ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                            noBatchMode = true;
                        }
                        else if (errorCode == TableErrorCodeStrings.TableNotFound)
                        {
                            Retry.Do(_policies.SlowInstantiation(), CancellationToken.None, () =>
                            {
                                try
                                {
                                    var table = _tableStorage.GetTableReference(tableName);
                                    table.CreateIfNotExists();
                                }
                                // HACK: incorrect behavior of the StorageClient (2010-09)
                                // Fails to behave properly in multi-threaded situations
                                catch (StorageException cex)
                                {
                                    var extended = cex.RequestInformation != null ? cex.RequestInformation.ExtendedErrorInformation : null;
                                    if (extended == null || extended.ErrorCode != TableErrorCodeStrings.TableAlreadyExists)
                                    {
                                        throw;
                                    }
                                }
                                context.SaveChanges(noBatchMode ? SaveChangesOptions.ReplaceOnUpdate : SaveChangesOptions.ReplaceOnUpdate | SaveChangesOptions.Batch);
                                ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                            });
                        }
                        else if (errorCode == StorageErrorCodeStrings.ResourceNotFound)
                        {
                            throw new InvalidOperationException("Cannot call update on a resource that does not exist", ex);
                        }
                        else
                        {
                            throw;
                        }
                    }
                    catch (DataServiceQueryException ex)
                    {
                        // HACK: code duplicated

                        var errorCode = RetryPolicies.GetErrorCode(ex);

                        if (errorCode == StorageErrorCodeStrings.OperationTimedOut)
                        {
                            // if batch does not work, then split into elementary requests
                            // PERF: it would be better to split the request in two and retry
                            context.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
                            ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                            noBatchMode = true;
                        }
                        else
                        {
                            throw;
                        }
                    }
                });

                NotifySucceeded(StorageOperationType.TableUpsert, stopwatch);
            }
        }
        /// <remarks></remarks>
        private IEnumerable <CloudEntity <T> > GetInternal <T>(TableServiceContext context, string tableName, Maybe <string> filter)
        {
            string continuationRowKey       = null;
            string continuationPartitionKey = null;

            var stopwatch = Stopwatch.StartNew();

            context.MergeOption = MergeOption.AppendOnly;
            context.ResolveType = ResolveFatEntityType;

            do
            {
                var query = context.CreateQuery <FatEntity>(tableName);

                if (filter.HasValue)
                {
                    query = query.AddQueryOption("$filter", filter.Value);
                }

                if (null != continuationRowKey)
                {
                    query = query.AddQueryOption(NextRowKeyToken, continuationRowKey)
                            .AddQueryOption(NextPartitionKeyToken, continuationPartitionKey);
                }

                QueryOperationResponse response    = null;
                FatEntity[]            fatEntities = null;

                Retry.Do(_policies.TransientTableErrorBackOff(), CancellationToken.None, () =>
                {
                    try
                    {
                        response    = query.Execute() as QueryOperationResponse;
                        fatEntities = ((IEnumerable <FatEntity>)response).ToArray();
                    }
                    catch (DataServiceQueryException ex)
                    {
                        // if the table does not exist, there is nothing to return
                        var errorCode = RetryPolicies.GetErrorCode(ex);
                        if (TableErrorCodeStrings.TableNotFound == errorCode ||
                            StorageErrorCodeStrings.ResourceNotFound == errorCode)
                        {
                            fatEntities = new FatEntity[0];
                            return;
                        }

                        throw;
                    }
                });

                NotifySucceeded(StorageOperationType.TableQuery, stopwatch);

                foreach (var fatEntity in fatEntities)
                {
                    var etag = context.Entities.First(e => e.Entity == fatEntity).ETag;
                    context.Detach(fatEntity);
                    yield return(FatEntity.Convert <T>(fatEntity, _serializer, etag));
                }

                Debug.Assert(context.Entities.Count == 0);

                if (null != response && response.Headers.ContainsKey(ContinuationNextRowKeyToken))
                {
                    continuationRowKey       = response.Headers[ContinuationNextRowKeyToken];
                    continuationPartitionKey = response.Headers[ContinuationNextPartitionKeyToken];

                    stopwatch.Restart();
                }
                else
                {
                    continuationRowKey       = null;
                    continuationPartitionKey = null;
                }
            } while (null != continuationRowKey);
        }