public class TableServiceContextV2 : TableServiceContext
{
private const string StorageVersionHeader = "x-ms-version";
private const string August2011Version = "2011-08-18";
public TableServiceContextV2(string baseAddress, StorageCredentials credentials):
base (baseAddress, credentials)
{
this.SendingRequest += SendingRequestWithNewVersion;
}
private void SendingRequestWithNewVersion(object sender, SendingRequestEventArgs e)
{
HttpWebRequest request = e.Request as HttpWebRequest;
// Apply the new storage version as a header value
request.Headers[StorageVersionHeader] = August2011Version;
}
}
// The mobile app collects the customer's phone number
CustomerEntity mobileCustomer = new CustomerEntity("John", "Smith");
mobileCustomer.PhoneNumber = "505-555-0122";
// Notice how the AttachTo method is called with a null Etag which indicates that this is an Upsert Command
mobileServiceContext.AttachTo(customersTableName, mobileCustomer, null);
mobileServiceContext.UpdateObject(mobileCustomer);
// No SaveChangeOptions is used, which indicates that a MERGE verb will be used. This set of steps will result in an InsertOrMerge command to be sent to Windows Azure Table
mobileServiceContext.SaveChanges();
Note: Prior to version “20011-08-18”, Windows Azure Table will reject any MERGE or PUT requests made against it where the If-Match header was not specified, as those earlier versions do not support Upsert commands.
you will receive error like
"<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n<error xmlns=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">\r\n <code>InvalidInput</code>\r\n <message xml:lang=\"en-US\">0:One of the request inputs is not valid.\nRequestId:eba57c2c-2be4-416c-8098-5b662e039bad\nTime:2012-01-10T16:18:35.1336142Z</message>\r\n</error>"
Here is the verification:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;
using System.Data.Services.Client;
using System.Net;
namespace AzureStorageTest
{
public class TableServiceContextV2 : TableServiceContext
{
private const string StorageVersionHeader = "x-ms-version";
// private const string August2011Version = "2011-08-18";
private const string August2011Version = "2009-09-19";
public TableServiceContextV2(string baseAddress, StorageCredentials credentials) :
base(baseAddress, credentials)
{
this.SendingRequest += SendingRequestWithNewVersion;
}
private void SendingRequestWithNewVersion(object sender, SendingRequestEventArgs e)
{
HttpWebRequest request = e.Request as HttpWebRequest;
// Apply the new storage version as a header value
request.Headers[StorageVersionHeader] = August2011Version;
}
}
}
using System;
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using System.Data.Services.Common;
using System.Data.Services.Client;
namespace AzureStorageTest
{
class Program
{
static void Main(string[] args)
{
CloudStorageAccount account = CloudStorageAccount.
Parse(System.Configuration.ConfigurationManager.AppSettings["StorageAccountConnectionString"]);
CloudTableClient tableClient = account.CreateCloudTableClient();
//Create Movie Table
string tableName = "Movies";
tableClient.CreateTableIfNotExist(tableName);
//TableServiceContext context = tableClient.GetDataServiceContext();
TableServiceContext context = new TableServiceContextV2(
"https://cloudcover.table.core.windows.net", account.Credentials);
//context.AddObject(tableName, new Movie("Action", "White Water Rapids Survival"));
//context.AddObject(tableName, new Movie("Action", "You are the one"));
context.SaveChangesWithRetries();
//Update
context.MergeOption = MergeOption.NoTracking;
var q = (from movie in context.CreateQuery<Movie>(tableName)
where movie.PartitionKey == "Action" && movie.Rating > 4.0
select movie).AsTableServiceQuery<Movie>();
foreach (Movie movieToUpdate in q)
{
movieToUpdate.Favorite = true;
string time = movieToUpdate.Timestamp.ToString();
context.AttachTo(tableName, movieToUpdate, null);
context.UpdateObject(movieToUpdate);
}
//WHEN HERE HAPPEN CONCURRENCY UPDATE ! ! !
context.SaveChangesWithRetries(SaveChangesOptions.Batch);
Console.ReadKey();
}
}
[DataServiceKey("PartitionKey", "RowKey")]
public class Movie
{
/// Movie Category is the partition key
public string PartitionKey { get; set; }
/// Movie Title is the row key
public string RowKey { get; set; }
public DateTime Timestamp { get; set; }
public int ReleaseYear { get; set; }
public double Rating { get; set; }
public string Language { get; set; }
public bool Favorite { get; set; }
public Movie() { }
public Movie(string partitionKey, string rowkey)
{
PartitionKey = partitionKey;
RowKey = rowkey;
Rating = 5;
}
}
}
Since we wish to send an InsertOrReplace Entity request, we would first need to AttachTo the websiteServiceContext without providing any Etag value before calling the UpdateObject method. In addition, the SaveChanges method would need to be invoked with the SaveChangesOptions.ReplaceOnUpdate parameter to indicate that the Upsert operation should replace the existing entity in case it exists.
CustomerEntity mailServiceCustomer = new CustomerEntity("David", "Alexander");
mailServiceCustomer.PhoneNumber = "333-555-0155";
mailServiceCustomer.Address = "234 Main St, Anaheim, TX, 65000";
mailServiceCustomer.Email = "David@wideworldimporters.com";
// Note how SaveChanges is called with ReplaceOnUpdate which is the differentiation
// factor between an InsertOrMerge Entity API and InsertOrReplace Entity Api
websiteServiceContext.AttachTo(customersTableName, mailServiceCustomer);
websiteServiceContext.UpdateObject(mailServiceCustomer);
websiteServiceContext.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);