C# Azure表中的异步插入

C# Azure表中的异步插入,c#,asynchronous,azure,azure-table-storage,C#,Asynchronous,Azure,Azure Table Storage,如何将实体异步保存到Windows Azure表服务 下面的代码同步工作,但在尝试异步保存时引发异常 本声明: context.BeginSaveChangesWithRetries(SaveChangesOptions.Batch, (asyncResult => context.EndSaveChanges(asyncResult)), null); System.ArgumentException中的结果:“当前对象未生成异步结果。参数名称:asyncResult” 此外,

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

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

本声明:

context.BeginSaveChangesWithRetries(SaveChangesOptions.Batch,
    (asyncResult => context.EndSaveChanges(asyncResult)), null);
System.ArgumentException中的结果:“当前对象未生成异步结果。参数名称:asyncResult”

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

表存储编写器.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; }
    }
}
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();
        }
    }
}
从WorkerRole.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; }
    }
}
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的示例。

您应该调用EndSaveChangesWithRetries而不是EndSaveChanges,否则EndSaveChanges不能使用BeginSaveChangesWithRetries返回的IAsyncResult对象。所以,您可以尝试更改End方法调用,如下所示吗

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

对于您的另一个问题,我建议为每个调用创建一个新的TableServiceContext,因为DataServiceContext不是无状态的(),并且使用异步调用实现TableStorageWriter.Write的方式可能允许并发操作。实际上,在Storage Client Library 2.0中,我们明确禁止使用单个TableServiceContext对象的并发操作。此外,创建TableServiceContext不会导致对Azure存储的请求。

对于异步问题,上下文的范围是什么?我想知道上下文是否存在结束问题。关于何时创建上下文,由于循环中的睡眠时间为10000,我认为创建表服务上下文的性能不会成为问题。但是,我通常倾向于为每个调用创建它,除非您打算大量使用它,并且请查看上面Write()中注释掉的代码。上下文的范围是局部的。我的尝试涉及到一个结束,但我可能做错了什么。事实上,我正在寻找一个如何做到这一点的例子。关于性能,假设Write()将被大量使用;我正在寻找最快、最安全的异步插入方法。您是否尝试过将上下文的范围向上移动?使其成为类成员?如果上下文是构造函数中初始化的私有类成员,则会出现与“谢谢”中相同的错误!我应该更仔细地阅读班级成员名单。工作版本:hi@serdar ozler我想问你,如果使用CreateTableIfNotExist方法会影响响应时间和秒计费,我想我理解azure将按操作收费,如果我有一个重复的操作,我总是先执行createifExist,然后执行查询,那么你知道这是像我想的那样工作还是我错了吗,谢谢你的指导。如果按API名称查看事务,您将获得大量CreateTable事务。