/// <summary>
        /// Test point queries entities inside and outside the given range.
        /// </summary>
        /// <param name="testClient">The table client to test.</param>
        /// <param name="tableName">The name of the table to test.</param>
        /// <param name="accessPermissions">The access permissions of the table client.</param>
        /// <param name="startPk">The start partition key range.</param>
        /// <param name="startRk">The start row key range.</param>
        /// <param name="endPk">The end partition key range.</param>
        /// <param name="endRk">The end row key range.</param>
        private void TestPointQuery(
            CloudTableClient testClient,
            string tableName,
            SharedAccessTablePermissions accessPermissions,
            string startPk,
            string startRk,
            string endPk,
            string endRk)
        {
            Action<BaseEntity> queryDelegate = (tableEntity) =>
            {
                TableServiceContext context = testClient.GetTableServiceContext();
                TableServiceQuery<BaseEntity> query = (from entity in context.CreateQuery<BaseEntity>(tableName)
                                                       where entity.PartitionKey == tableEntity.PartitionKey && entity.RowKey == tableEntity.RowKey
                                                       select entity).AsTableServiceQuery(context);
                IEnumerable<BaseEntity> list = query.Execute().ToList();
                Assert.AreEqual(1, list.Count());
                BaseEntity e = list.Single();
            };

            bool expectSuccess = (accessPermissions & SharedAccessTablePermissions.Query) != 0;

            // Perform test
            TestOperationWithRange(
                tableName,
                startPk,
                startRk,
                endPk,
                endRk,
                queryDelegate,
                "point query",
                expectSuccess,
                expectSuccess ? HttpStatusCode.OK : HttpStatusCode.NotFound);
        }
        /// <summary>
        /// Test update (merge) on entities inside and outside the given range.
        /// </summary>
        /// <param name="testClient">The table client to test.</param>
        /// <param name="tableName">The name of the table to test.</param>
        /// <param name="accessPermissions">The access permissions of the table client.</param>
        /// <param name="startPk">The start partition key range.</param>
        /// <param name="startRk">The start row key range.</param>
        /// <param name="endPk">The end partition key range.</param>
        /// <param name="endRk">The end row key range.</param>
        private void TestUpdateMerge(
            CloudTableClient testClient,
            string tableName,
            SharedAccessTablePermissions accessPermissions,
            string startPk,
            string startRk,
            string endPk,
            string endRk)
        {
            Action<BaseEntity> updateDelegate = (tableEntity) =>
            {
                TableServiceContext context = testClient.GetTableServiceContext();

                // Merge entity
                tableEntity.A = "10";
                context.AttachTo(tableName, tableEntity, "*");
                context.UpdateObject(tableEntity);
                context.SaveChangesWithRetries();
            };

            bool expectSuccess = (accessPermissions & SharedAccessTablePermissions.Update) != 0;

            // Perform test
            TestOperationWithRange(
                tableName,
                startPk,
                startRk,
                endPk,
                endRk,
                updateDelegate,
                "update merge",
                expectSuccess,
                expectSuccess ? HttpStatusCode.NoContent : HttpStatusCode.NotFound);
        }
        /// <summary>
        /// Test upsert (insert or replace) on entities inside and outside the given range.
        /// </summary>
        /// <param name="testClient">The table client to test.</param>
        /// <param name="tableName">The name of the table to test.</param>
        /// <param name="accessPermissions">The access permissions of the table client.</param>
        /// <param name="startPk">The start partition key range.</param>
        /// <param name="startRk">The start row key range.</param>
        /// <param name="endPk">The end partition key range.</param>
        /// <param name="endRk">The end row key range.</param>
        private void TestUpsertReplace(
            CloudTableClient testClient,
            string tableName,
            SharedAccessTablePermissions accessPermissions,
            string startPk,
            string startRk,
            string endPk,
            string endRk)
        {
            Action<BaseEntity> upsertDelegate = (tableEntity) =>
            {
                TableServiceContext context = testClient.GetTableServiceContext();

                // Replace entity
                tableEntity.A = "10";
                context.AttachTo(tableName, tableEntity);
                context.UpdateObject(tableEntity);
                context.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
            };

            SharedAccessTablePermissions upsertPermissions = (SharedAccessTablePermissions.Update | SharedAccessTablePermissions.Add);
            bool expectSuccess = (accessPermissions & upsertPermissions) == upsertPermissions;

            // Perform test
            TestOperationWithRange(
                tableName,
                startPk,
                startRk,
                endPk,
                endRk,
                upsertDelegate,
                "upsert replace",
                expectSuccess,
                expectSuccess ? HttpStatusCode.NoContent : HttpStatusCode.NotFound);
        }
        public void TableSASConstructors()
        {
            CloudTableClient tableClient = GenerateCloudTableClient();
            CloudTable table = tableClient.GetTableReference("T" + Guid.NewGuid().ToString("N"));
            try
            {
                table.Create();

                TableServiceContext context = tableClient.GetTableServiceContext();
                context.AddObject(table.Name, new BaseEntity("PK", "RK"));
                context.SaveChangesWithRetries();

                // Prepare SAS authentication with full permissions
                string sasToken = table.GetSharedAccessSignature(
                    new SharedAccessTablePolicy
                    {
                        Permissions = SharedAccessTablePermissions.Add | SharedAccessTablePermissions.Delete | SharedAccessTablePermissions.Query,
                        SharedAccessExpiryTime = DateTimeOffset.Now.AddMinutes(30)
                    },
                    null /* accessPolicyIdentifier */,
                    null /* startPk */,
                    null /* startRk */,
                    null /* endPk */,
                    null /* endRk */);

                CloudStorageAccount sasAccount;
                StorageCredentials sasCreds;
                CloudTableClient sasClient;
                CloudTable sasTable;
                TableServiceContext sasContext;
                Uri baseUri = new Uri(TestBase.TargetTenantConfig.TableServiceEndpoint);
                int count;

                // SAS via connection string parse
                sasAccount = CloudStorageAccount.Parse(string.Format("TableEndpoint={0};SharedAccessSignature={1}", baseUri.AbsoluteUri, sasToken));
                sasClient = sasAccount.CreateCloudTableClient();
                sasTable = sasClient.GetTableReference(table.Name);
                sasContext = sasClient.GetTableServiceContext();
                count = sasContext.CreateQuery<BaseEntity>(sasTable.Name).AsTableServiceQuery(sasContext).Execute().Count();
                Assert.AreEqual(1, count);

                // SAS via account constructor
                sasCreds = new StorageCredentials(sasToken);
                sasAccount = new CloudStorageAccount(sasCreds, null, null, baseUri, null);
                sasClient = sasAccount.CreateCloudTableClient();
                sasTable = sasClient.GetTableReference(table.Name);
                sasContext = sasClient.GetTableServiceContext();
                count = sasContext.CreateQuery<BaseEntity>(sasTable.Name).AsTableServiceQuery(sasContext).Execute().Count();
                Assert.AreEqual(1, count);

                // SAS via client constructor URI + Creds
                sasCreds = new StorageCredentials(sasToken);
                sasClient = new CloudTableClient(baseUri, sasCreds);
                sasContext = sasClient.GetTableServiceContext();
                count = sasContext.CreateQuery<BaseEntity>(sasTable.Name).AsTableServiceQuery(sasContext).Execute().Count();
                Assert.AreEqual(1, count);

            }
            finally
            {
                table.DeleteIfExists();
            }
        }
        /// <summary>
        /// Test deleting entities inside and outside the given range.
        /// </summary>
        /// <param name="testClient">The table client to test.</param>
        /// <param name="tableName">The name of the table to test.</param>
        /// <param name="accessPermissions">The access permissions of the table client.</param>
        /// <param name="startPk">The start partition key range.</param>
        /// <param name="startRk">The start row key range.</param>
        /// <param name="endPk">The end partition key range.</param>
        /// <param name="endRk">The end row key range.</param>
        private void TestDelete(
            CloudTableClient testClient,
            string tableName,
            SharedAccessTablePermissions accessPermissions,
            string startPk,
            string startRk,
            string endPk,
            string endRk)
        {
            TableServiceContext referenceContext = testClient.GetTableServiceContext();

            Action<BaseEntity> deleteDelegate = (tableEntity) =>
            {
                TableServiceContext context = testClient.GetTableServiceContext();

                context.AttachTo(tableName, tableEntity, "*");
                context.DeleteObject(tableEntity);
                context.SaveChangesWithRetries();
                context.Detach(tableEntity);
            };

            bool expectSuccess = (accessPermissions & SharedAccessTablePermissions.Delete) != 0;

            // Perform test
            TestOperationWithRange(
                tableName,
                startPk,
                startRk,
                endPk,
                endRk,
                deleteDelegate,
                "delete",
                expectSuccess,
                expectSuccess ? HttpStatusCode.NoContent : HttpStatusCode.Forbidden);
        }
        /// <summary>
        /// Test adding entities inside and outside the given range.
        /// </summary>
        /// <param name="testClient">The table client to test.</param>
        /// <param name="tableName">The name of the table to test.</param>
        /// <param name="accessPermissions">The access permissions of the table client.</param>
        /// <param name="startPk">The start partition key range.</param>
        /// <param name="startRk">The start row key range.</param>
        /// <param name="endPk">The end partition key range.</param>
        /// <param name="endRk">The end row key range.</param>
        private void TestAdd(
            CloudTableClient testClient,
            string tableName,
            SharedAccessTablePermissions accessPermissions,
            string startPk,
            string startRk,
            string endPk,
            string endRk)
        {
            TableServiceContext referenceContext = testClient.GetTableServiceContext();

            Action<BaseEntity> addDelegate = (tableEntity) =>
            {
                TableServiceContext context = testClient.GetTableServiceContext();

                context.AddObject(tableName, tableEntity);
                context.SaveChangesWithRetries();
            };

            bool expectSuccess = (accessPermissions & SharedAccessTablePermissions.Add) != 0;

            // Perform test
            TestOperationWithRange(
                tableName,
                startPk,
                startRk,
                endPk,
                endRk,
                addDelegate,
                "add",
                expectSuccess,
                expectSuccess ? HttpStatusCode.Created : HttpStatusCode.NotFound);
        }
Example #7
0
        public void SingleEntityInsertOrReplaceAPM()
        {
            CloudTableClient    tableClient  = GenerateCloudTableClient();
            TableServiceContext ctx          = tableClient.GetTableServiceContext();
            TableServiceContext queryContext = tableClient.GetTableServiceContext();

            queryContext.MergeOption = MergeOption.NoTracking;

            // Insert Entity
            BaseEntity baseEntity = new BaseEntity("insert test", "foo");

            // Insert Or Merge with no pre-existing entity
            MergeEntity insertOrReplaceEntity = new MergeEntity(baseEntity.PartitionKey, baseEntity.RowKey);

            insertOrReplaceEntity.Randomize();
            ctx.AttachTo(currentTable.Name, insertOrReplaceEntity, null);
            ctx.UpdateObject(insertOrReplaceEntity);

            using (ManualResetEvent evt = new ManualResetEvent(false))
            {
                IAsyncResult asyncRes = null;
                ctx.BeginSaveChangesWithRetries(
                    SaveChangesOptions.ReplaceOnUpdate,
                    (res) =>
                {
                    asyncRes = res;
                    evt.Set();
                },
                    null);
                evt.WaitOne();

                ctx.EndSaveChangesWithRetries(asyncRes);
            }

            ctx.Detach(insertOrReplaceEntity);

            // Retrieve Entity & Verify Contents
            UnionEnitity retrievedEntity = (from ent in queryContext.CreateQuery <UnionEnitity>(currentTable.Name)
                                            where ent.PartitionKey == baseEntity.PartitionKey &&
                                            ent.RowKey == baseEntity.RowKey
                                            select ent).AsTableServiceQuery(queryContext).Execute().FirstOrDefault();

            Assert.IsNotNull(retrievedEntity);
            Assert.AreEqual(null, retrievedEntity.A);
            Assert.AreEqual(null, retrievedEntity.B);
            Assert.AreEqual(null, retrievedEntity.C);
            Assert.AreEqual(insertOrReplaceEntity.D, retrievedEntity.D);
            Assert.AreEqual(insertOrReplaceEntity.E, retrievedEntity.E);
            Assert.AreEqual(insertOrReplaceEntity.F, retrievedEntity.F);

            BaseEntity replacedEntity = new BaseEntity("insert test", "foo");

            replacedEntity.Randomize();

            ctx.AttachTo(currentTable.Name, replacedEntity, null);
            ctx.UpdateObject(replacedEntity);

            using (ManualResetEvent evt = new ManualResetEvent(false))
            {
                IAsyncResult asyncRes = null;
                ctx.BeginSaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate, (res) =>
                {
                    asyncRes = res;
                    evt.Set();
                }, null);
                evt.WaitOne();

                ctx.EndSaveChangesWithRetries(asyncRes);
            }

            // Retrieve Entity & Verify
            retrievedEntity = (from ent in queryContext.CreateQuery <UnionEnitity>(currentTable.Name)
                               where ent.PartitionKey == baseEntity.PartitionKey &&
                               ent.RowKey == baseEntity.RowKey
                               select ent).AsTableServiceQuery(queryContext).Execute().FirstOrDefault();

            Assert.IsNotNull(retrievedEntity);
            Assert.AreEqual(replacedEntity.A, retrievedEntity.A);
            Assert.AreEqual(replacedEntity.B, retrievedEntity.B);
            Assert.AreEqual(replacedEntity.C, retrievedEntity.C);
            Assert.AreEqual(null, retrievedEntity.D);
            Assert.AreEqual(null, retrievedEntity.E);
            Assert.AreEqual(null, retrievedEntity.F);
        }
Example #8
0
        private void DoSingleEntityInsertDeleteFail(TablePayloadFormat format)
        {
            CloudTableClient    tableClient = GenerateCloudTableClient();
            TableServiceContext ctx         = tableClient.GetTableServiceContext();

            SetPayloadFormatOnDataServiceContext(ctx, format, tableClient);

            // Delete Entity that does not exits
            ComplexEntity deleteEntity = new ComplexEntity("insert test", "foo" + format.ToString());

            ctx.AttachTo(currentTable.Name, deleteEntity, "*");
            ctx.DeleteObject(deleteEntity);
            OperationContext opContext = new OperationContext();

            try
            {
                ctx.SaveChangesWithRetries(SaveChangesOptions.None, null, opContext);
                Assert.Fail();
            }
            catch (StorageException)
            {
                TestHelper.ValidateResponse(opContext, 1, (int)HttpStatusCode.NotFound, new string[] { "ResourceNotFound" }, "The specified resource does not exist.");
            }


            ctx = tableClient.GetTableServiceContext();
            TableServiceContext ctx2 = tableClient.GetTableServiceContext();

            SetPayloadFormatOnDataServiceContext(ctx2, format, tableClient);

            // Insert Entity
            ComplexEntity insertEntity = new ComplexEntity("insert test", "foo" + format.ToString());

            ctx.AddObject(currentTable.Name, insertEntity);
            ctx.SaveChangesWithRetries();

            // Update Entity
            ComplexEntity retrievedEntity = (from ent in ctx2.CreateQuery <ComplexEntity>(currentTable.Name)
                                             where ent.PartitionKey == insertEntity.PartitionKey &&
                                             ent.RowKey == insertEntity.RowKey
                                             select ent).AsTableServiceQuery(ctx2).Execute().FirstOrDefault();

            retrievedEntity.String = "updated value";

            ctx2.UpdateObject(retrievedEntity);
            ctx2.SaveChangesWithRetries();

            // Now delete old reference with stale etag and validate exception
            ctx.DeleteObject(insertEntity);

            opContext = new OperationContext();
            try
            {
                ctx.SaveChangesWithRetries(SaveChangesOptions.None, null, opContext);
                Assert.Fail();
            }
            catch (StorageException)
            {
                TestHelper.ValidateResponse(opContext,
                                            1,
                                            (int)HttpStatusCode.PreconditionFailed,
                                            new string[] { "UpdateConditionNotSatisfied", "ConditionNotMet" },
                                            new string[] { "The update condition specified in the request was not satisfied.", "The condition specified using HTTP conditional header(s) is not met." });
            }
        }
Example #9
0
        public void TableServiceContextConcurrencyAllowsOnlySingleOperationAtOnce()
        {
            CloudTableClient    tableClient  = GenerateCloudTableClient();
            TableServiceContext tableContext = tableClient.GetTableServiceContext();

            // insert entities to query against
            for (int i = 0; i < 5; i++)
            {
                for (int m = 0; m < 100; m++)
                {
                    BaseEntity ent = new BaseEntity("testpartition" + i, m.ToString());
                    ent.Randomize();
                    ent.A = ent.RowKey;
                    tableContext.AddObject(currentTable.Name, ent);
                }

                tableContext.SaveChangesWithRetries(SaveChangesOptions.Batch);
            }

            List <OperationContext> opContexts = new List <OperationContext>();
            object   lockerObj = new object();
            DateTime start     = DateTime.Now;

            int threadsRunning = 0;

            Exception lastEx = null;

            // Start 10 simultaneous threads to query entities associated with same context.
            for (int j = 0; j < 10; j++)
            {
                opContexts.Add(new OperationContext());
                Thread newThread = new Thread((arg) =>
                {
                    Interlocked.Increment(ref threadsRunning);
                    try
                    {
                        lock (lockerObj)
                        {
                            Monitor.Wait(lockerObj);
                        }

                        TableServiceQuery <BaseEntity> query = (from ent in tableContext.CreateQuery <BaseEntity>(currentTable.Name)
                                                                select ent).AsTableServiceQuery(tableContext);

                        Debug.WriteLine(String.Format("Thread {0} start operation @ {1}", Thread.CurrentThread.ManagedThreadId, (DateTime.Now - start).TotalMilliseconds));

                        try
                        {
                            query.Execute(null, arg as OperationContext).ToList();
                        }
                        catch (Exception)
                        {
                            // no op, expected to have some exceptions
                        }

                        Debug.WriteLine(String.Format("Thread {0} end operation @ {1}", Thread.CurrentThread.ManagedThreadId, (DateTime.Now - start).TotalMilliseconds));
                    }
                    catch (Exception ex)
                    {
                        lastEx = ex;
                    }
                    finally
                    {
                        Interlocked.Decrement(ref threadsRunning);
                    }
                });

                newThread.Start(opContexts[j]);
            }

            // Wait for all threads to start
            while (Interlocked.CompareExchange(ref threadsRunning, 10, 10) < 10)
            {
                Thread.Sleep(200);
            }

            // pulse all threads
            lock (lockerObj)
            {
                Monitor.PulseAll(lockerObj);
            }

            // Wait for all threads to complete
            while (Interlocked.CompareExchange(ref threadsRunning, -1, 0) > -1)
            {
                Thread.Sleep(200);
            }

            if (lastEx != null)
            {
                throw lastEx;
            }

            foreach (OperationContext opContext in opContexts)
            {
                if (opContext.LastResult == null || opContext.LastResult.StartTime == null || opContext.LastResult.EndTime == null)
                {
                    continue;
                }

                TestHelper.AssertNAttempts(opContext, 1);

                RequestResult currRes = opContext.LastResult;

                // Make sure this results start time does not occur in between any other results start & end time
                IEnumerable <RequestResult> overlappingResults = (from ctx in opContexts
                                                                  where ctx.LastResult != null && ctx.LastResult != currRes &&
                                                                  ctx.LastResult.StartTime != null && ctx.LastResult.EndTime != null &&
                                                                  ctx.LastResult.StartTime.Ticks <currRes.StartTime.Ticks &&
                                                                                                  ctx.LastResult.EndTime.Ticks> currRes.StartTime.Ticks
                                                                  select ctx.LastResult);

                Assert.AreEqual(overlappingResults.Count(), 0, "Detected overlapping query");
            }
        }
        public void TableGetSetPermissionTest()
        {
            CloudTableClient tableClient = GenerateCloudTableClient();
            CloudTable       table       = tableClient.GetTableReference("T" + Guid.NewGuid().ToString("N"));

            try
            {
                table.Create();

                TableServiceContext context = tableClient.GetTableServiceContext();
                context.AddObject(table.Name, new BaseEntity("PK", "RK"));
                context.SaveChangesWithRetries();

                TablePermissions expectedPermissions;
                TablePermissions testPermissions;

                // Test new table permissions.
                expectedPermissions = new TablePermissions();
                testPermissions     = table.GetPermissions();
                AssertPermissionsEqual(expectedPermissions, testPermissions);

                // Test setting empty permissions.
                table.SetPermissions(expectedPermissions);
                Thread.Sleep(30 * 1000);
                testPermissions = table.GetPermissions();
                AssertPermissionsEqual(expectedPermissions, testPermissions);

                // Add a policy, check setting and getting.
                expectedPermissions.SharedAccessPolicies.Add(Guid.NewGuid().ToString(), new SharedAccessTablePolicy
                {
                    Permissions            = SharedAccessTablePermissions.Query,
                    SharedAccessStartTime  = DateTimeOffset.Now - TimeSpan.FromHours(1),
                    SharedAccessExpiryTime = DateTimeOffset.Now + TimeSpan.FromHours(1)
                });

                table.SetPermissions(expectedPermissions);
                Thread.Sleep(30 * 1000);
                testPermissions = table.GetPermissions();
                AssertPermissionsEqual(expectedPermissions, testPermissions);

                // Add a policy, check setting and getting.
                expectedPermissions.SharedAccessPolicies.Add(Guid.NewGuid().ToString(), new SharedAccessTablePolicy
                {
                    Permissions            = SharedAccessTablePermissions.Delete | SharedAccessTablePermissions.Add,
                    SharedAccessStartTime  = DateTimeOffset.Now + TimeSpan.FromHours(1),
                    SharedAccessExpiryTime = DateTimeOffset.Now + TimeSpan.FromDays(1)
                });

                table.SetPermissions(expectedPermissions);
                Thread.Sleep(30 * 1000);
                testPermissions = table.GetPermissions();
                AssertPermissionsEqual(expectedPermissions, testPermissions);

                // Add a null policy, check setting and getting.
                expectedPermissions.SharedAccessPolicies.Add(Guid.NewGuid().ToString(), new SharedAccessTablePolicy
                {
                    Permissions = SharedAccessTablePermissions.None,
                });

                table.SetPermissions(expectedPermissions);
                Thread.Sleep(30 * 1000);
                testPermissions = table.GetPermissions();
                AssertPermissionsEqual(expectedPermissions, testPermissions);

                // Add a policy, check setting and getting.
                expectedPermissions.SharedAccessPolicies.Add(Guid.NewGuid().ToString(), new SharedAccessTablePolicy
                {
                    Permissions            = SharedAccessTablePermissions.Add | SharedAccessTablePermissions.Query | SharedAccessTablePermissions.Update | SharedAccessTablePermissions.Delete,
                    SharedAccessStartTime  = DateTimeOffset.Now + TimeSpan.FromDays(0.5),
                    SharedAccessExpiryTime = DateTimeOffset.Now + TimeSpan.FromDays(1)
                });

                table.SetPermissions(expectedPermissions);
                Thread.Sleep(30 * 1000);
                testPermissions = table.GetPermissions();
                AssertPermissionsEqual(expectedPermissions, testPermissions);

                // Add a policy, check setting and getting.
                expectedPermissions.SharedAccessPolicies.Add(Guid.NewGuid().ToString(), new SharedAccessTablePolicy
                {
                    Permissions            = SharedAccessTablePermissions.Update,
                    SharedAccessStartTime  = DateTimeOffset.Now + TimeSpan.FromHours(6),
                    SharedAccessExpiryTime = DateTimeOffset.Now + TimeSpan.FromHours(6.5)
                });

                table.SetPermissions(expectedPermissions);
                Thread.Sleep(30 * 1000);
                testPermissions = table.GetPermissions();
                AssertPermissionsEqual(expectedPermissions, testPermissions);
            }
            finally
            {
                table.DeleteIfExists();
            }
        }
        public void TableSASConstructors()
        {
            CloudTableClient tableClient = GenerateCloudTableClient();
            CloudTable       table       = tableClient.GetTableReference("T" + Guid.NewGuid().ToString("N"));

            try
            {
                table.Create();

                TableServiceContext context = tableClient.GetTableServiceContext();
                context.AddObject(table.Name, new BaseEntity("PK", "RK"));
                context.SaveChangesWithRetries();

                // Prepare SAS authentication with full permissions
                string sasToken = table.GetSharedAccessSignature(
                    new SharedAccessTablePolicy
                {
                    Permissions            = SharedAccessTablePermissions.Add | SharedAccessTablePermissions.Delete | SharedAccessTablePermissions.Query,
                    SharedAccessExpiryTime = DateTimeOffset.Now.AddMinutes(30)
                },
                    null /* accessPolicyIdentifier */,
                    null /* startPk */,
                    null /* startRk */,
                    null /* endPk */,
                    null /* endRk */);

                CloudStorageAccount sasAccount;
                StorageCredentials  sasCreds;
                CloudTableClient    sasClient;
                CloudTable          sasTable;
                TableServiceContext sasContext;
                Uri baseUri = new Uri(TestBase.TargetTenantConfig.TableServiceEndpoint);
                int count;

                // SAS via connection string parse
                sasAccount = CloudStorageAccount.Parse(string.Format("TableEndpoint={0};SharedAccessSignature={1}", baseUri.AbsoluteUri, sasToken));
                sasClient  = sasAccount.CreateCloudTableClient();
                sasTable   = sasClient.GetTableReference(table.Name);
                sasContext = sasClient.GetTableServiceContext();
                count      = sasContext.CreateQuery <BaseEntity>(sasTable.Name).AsTableServiceQuery(sasContext).Execute().Count();
                Assert.AreEqual(1, count);

                // SAS via account constructor
                sasCreds   = new StorageCredentials(sasToken);
                sasAccount = new CloudStorageAccount(sasCreds, null, null, baseUri);
                sasClient  = sasAccount.CreateCloudTableClient();
                sasTable   = sasClient.GetTableReference(table.Name);
                sasContext = sasClient.GetTableServiceContext();
                count      = sasContext.CreateQuery <BaseEntity>(sasTable.Name).AsTableServiceQuery(sasContext).Execute().Count();
                Assert.AreEqual(1, count);

                // SAS via client constructor URI + Creds
                sasCreds   = new StorageCredentials(sasToken);
                sasClient  = new CloudTableClient(baseUri, sasCreds);
                sasContext = sasClient.GetTableServiceContext();
                count      = sasContext.CreateQuery <BaseEntity>(sasTable.Name).AsTableServiceQuery(sasContext).Execute().Count();
                Assert.AreEqual(1, count);

                // SAS via CloudTable constructor Uri + Client
                sasCreds   = new StorageCredentials(sasToken);
                sasTable   = new CloudTable(table.Uri, tableClient);
                sasClient  = sasTable.ServiceClient;
                sasContext = sasClient.GetTableServiceContext();
                count      = sasContext.CreateQuery <BaseEntity>(sasTable.Name).AsTableServiceQuery(sasContext).Execute().Count();
                Assert.AreEqual(1, count);
            }
            finally
            {
                table.DeleteIfExists();
            }
        }
        /// <summary>
        /// Test a table operation on entities inside and outside the given range.
        /// </summary>
        /// <param name="tableName">The name of the table to test.</param>
        /// <param name="startPk">The start partition key range.</param>
        /// <param name="startRk">The start row key range.</param>
        /// <param name="endPk">The end partition key range.</param>
        /// <param name="endRk">The end row key range.</param>
        /// <param name="runOperationDelegate">A delegate with the table operation to test.</param>
        /// <param name="opName">The name of the operation being tested.</param>
        /// <param name="expectSuccess">Whether the operation should succeed on entities within the range.</param>
        private void TestOperationWithRange(
            string tableName,
            string startPk,
            string startRk,
            string endPk,
            string endRk,
            Action <BaseEntity> runOperationDelegate,
            string opName,
            bool expectSuccess,
            HttpStatusCode expectedStatusCode,
            bool isRangeQuery)
        {
            CloudTableClient    referenceClient  = GenerateCloudTableClient();
            TableServiceContext referenceContext = referenceClient.GetTableServiceContext();

            string partitionKey = startPk ?? endPk ?? "M";
            string rowKey       = startRk ?? endRk ?? "S";

            // if we expect a success for creation - avoid inserting duplicate entities
            BaseEntity tableEntity = new BaseEntity(partitionKey, rowKey);

            if (expectedStatusCode == HttpStatusCode.Created)
            {
                referenceContext.AttachTo(tableName, tableEntity, "*");
                referenceContext.DeleteObject(tableEntity);
                try
                {
                    referenceContext.SaveChangesWithRetries();
                }
                catch (Exception)
                {
                }
            }
            else
            {
                // only for add we should not be adding the entity
                referenceContext.AttachTo(tableName, tableEntity);
                referenceContext.UpdateObject(tableEntity);
                referenceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
            }

            if (expectSuccess)
            {
                runOperationDelegate(tableEntity);
            }
            else
            {
                TestHelper.ExpectedException(
                    () => runOperationDelegate(tableEntity),
                    string.Format("{0} without appropriate permission.", opName),
                    HttpStatusCode.NotFound);
            }

            if (startPk != null)
            {
                tableEntity.PartitionKey = "A";
                if (startPk.CompareTo(tableEntity.PartitionKey) <= 0)
                {
                    Assert.Inconclusive("Test error: partition key for this test must not be less than or equal to \"A\"");
                }

                TestHelper.ExpectedException(
                    () => runOperationDelegate(tableEntity),
                    string.Format("{0} before allowed partition key range", opName),
                    HttpStatusCode.NotFound);
                tableEntity.PartitionKey = partitionKey;
            }

            if (endPk != null)
            {
                tableEntity.PartitionKey = "Z";
                if (endPk.CompareTo(tableEntity.PartitionKey) >= 0)
                {
                    Assert.Inconclusive("Test error: partition key for this test must not be greater than or equal to \"Z\"");
                }

                TestHelper.ExpectedException(
                    () => runOperationDelegate(tableEntity),
                    string.Format("{0} after allowed partition key range", opName),
                    HttpStatusCode.NotFound);

                tableEntity.PartitionKey = partitionKey;
            }

            if (startRk != null)
            {
                if (isRangeQuery || startPk != null)
                {
                    tableEntity.PartitionKey = startPk;
                    tableEntity.RowKey       = "A";
                    if (startRk.CompareTo(tableEntity.RowKey) <= 0)
                    {
                        Assert.Inconclusive("Test error: row key for this test must not be less than or equal to \"A\"");
                    }

                    TestHelper.ExpectedException(
                        () => runOperationDelegate(tableEntity),
                        string.Format("{0} before allowed row key range", opName),
                        HttpStatusCode.NotFound);

                    tableEntity.RowKey = rowKey;
                }
            }

            if (endRk != null)
            {
                if (isRangeQuery || endPk != null)
                {
                    tableEntity.PartitionKey = endPk;
                    tableEntity.RowKey       = "Z";
                    if (endRk.CompareTo(tableEntity.RowKey) >= 0)
                    {
                        Assert.Inconclusive("Test error: row key for this test must not be greater than or equal to \"Z\"");
                    }

                    TestHelper.ExpectedException(
                        () => runOperationDelegate(tableEntity),
                        string.Format("{0} after allowed row key range", opName),
                        HttpStatusCode.NotFound);

                    tableEntity.RowKey = rowKey;
                }
            }
        }
        public void TableSetGetPermissionsAPM()
        {
            CloudTableClient tableClient = GenerateCloudTableClient();
            CloudTable       table       = tableClient.GetTableReference("T" + Guid.NewGuid().ToString("N"));

            try
            {
                table.Create();

                TableServiceContext context = tableClient.GetTableServiceContext();
                context.AddObject(table.Name, new BaseEntity("PK", "RK"));
                context.SaveChangesWithRetries();

                TablePermissions expectedPermissions = new TablePermissions();
                TablePermissions testPermissions     = table.GetPermissions();

                AssertPermissionsEqual(expectedPermissions, testPermissions);

                // Add a policy, check setting and getting.
                expectedPermissions.SharedAccessPolicies.Add(Guid.NewGuid().ToString(), new SharedAccessTablePolicy
                {
                    Permissions            = SharedAccessTablePermissions.Query,
                    SharedAccessStartTime  = DateTimeOffset.Now - TimeSpan.FromHours(1),
                    SharedAccessExpiryTime = DateTimeOffset.Now + TimeSpan.FromHours(1)
                });

                using (ManualResetEvent evt = new ManualResetEvent(false))
                {
                    IAsyncResult result = null;
                    table.BeginSetPermissions(expectedPermissions, (res) =>
                    {
                        result = res;
                        evt.Set();
                    }, null);

                    evt.WaitOne();

                    table.EndSetPermissions(result);
                }

                Thread.Sleep(30 * 1000);

                using (ManualResetEvent evt = new ManualResetEvent(false))
                {
                    IAsyncResult result = null;
                    table.BeginGetPermissions((res) =>
                    {
                        result = res;
                        evt.Set();
                    }, null);

                    evt.WaitOne();

                    testPermissions = table.EndGetPermissions(result);
                }

                AssertPermissionsEqual(expectedPermissions, testPermissions);
            }
            finally
            {
                table.DeleteIfExists();
            }
        }