4

如何将实体异步保存到 Windows Azure 表服务?

下面的代码同步工作,但在尝试异步保存时引发异常。

这个说法:

context.BeginSaveChangesWithRetries(SaveChangesOptions.Batch,
    (asyncResult => context.EndSaveChanges(asyncResult)), null);

结果 System.ArgumentException:“当前对象并非源自异步结果。参数名称:asyncResult”。

此外,异步保存时创建服务上下文的正确模式是什么?我应该为每个写操作创建一个单独的上下文吗?是否太贵(例如需要通过网络拨打电话)?

TableStorageWriter.cs

using System;
using System.Data.Services.Client;
using System.Diagnostics;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace WorkerRole1
{
    public class TableStorageWriter
    {
        private const string _tableName = "StorageTest";
        private readonly CloudStorageAccount _storageAccount;
        private CloudTableClient _tableClient;

        public TableStorageWriter()
        {
            _storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
            _tableClient = _storageAccount.CreateCloudTableClient();
            _tableClient.CreateTableIfNotExist(_tableName);
        }

        public void Write(string message)
        {
            try
            {
                DateTime now = DateTime.UtcNow;
                var entity = new StorageTestEntity
                    {
                        Message = message,
                        PartitionKey = string.Format("{0:yyyy-MM-dd}", now),
                        RowKey = string.Format("{0:HH:mm:ss.fff}-{1}", now, Guid.NewGuid())
                    };

                // Should I get this context before each write? It is efficient?
                TableServiceContext context = _tableClient.GetDataServiceContext();

                context.AddObject(_tableName, entity);

                // This statement works but it's synchronous
                context.SaveChangesWithRetries();

                // This attempt at saving asynchronously results in System.ArgumentException:
                // The current object did not originate the async result. Parameter name: asyncResult
                // context.BeginSaveChangesWithRetries(SaveChangesOptions.Batch,
                //                                  (asyncResult => context.EndSaveChanges(asyncResult)), null);
            }
            catch (StorageClientException e)
            {
                Debug.WriteLine("Error: {0}", e.Message);
                Debug.WriteLine("Extended error info: {0} : {1}",
                                e.ExtendedErrorInformation.ErrorCode,
                                e.ExtendedErrorInformation.ErrorMessage);
            }
        }
    }

    internal class StorageTestEntity : TableServiceEntity
    {
        public string Message { get; set; }
    }
}

WorkerRole.cs调用:

using System.Net;
using System.Threading;
using Microsoft.WindowsAzure.ServiceRuntime;
using log4net;

namespace WorkerRole1
{
    public class WorkerRole : RoleEntryPoint
    {
        public override void Run()
        {
            var storageWriter = new TableStorageWriter();
            while (true)
            {
                Thread.Sleep(10000);
                storageWriter.Write("Working...");
            }
        }

        public override bool OnStart()
        {
            ServicePointManager.DefaultConnectionLimit = 12;
            return base.OnStart();
        }
    }
}

使用适用于 .NET 1.8 的 Windows Azure SDK 的示例。

4

1 回答 1

6

You should call EndSaveChangesWithRetries instead of EndSaveChanges, as otherwise the IAsyncResult object returned by BeginSaveChangesWithRetries cannot be used by EndSaveChanges. So, could you please try changing your End method call as below?

context.BeginSaveChangesWithRetries(SaveChangesOptions.Batch,
    (asyncResult => context.EndSaveChangesWithRetries(asyncResult)),
    null);

And for your other question, I would recommend creating a new TableServiceContext for each call, as DataServiceContext is not stateless (MSDN) and the way you implemented TableStorageWriter.Write with the asynchronous call might allow concurrent operations. Actually, in Storage Client Library 2.0, we explicitly prevented concurrent operations that uses a single TableServiceContext object. Moreover, creating a TableServiceContext does not result in a request to Azure Storage.

于 2012-12-07T21:33:21.377 回答