【问题标题】:How to make a WCF REST method entirely asynchronous with the Task Parallel Library?如何使 WCF REST 方法与任务并行库完全异步?
【发布时间】:2011-12-23 19:26:58
【问题描述】:

我正在尝试使 WCF REST 方法完全异步(我不想在任何地方阻塞)。本质上,我有一个简单的 3 层服务:服务、业务逻辑和数据访问层。数据访问层正在访问数据库,可能需要几秒钟才能从该方法获得响应。

我不太了解如何链接所有这些方法。有人可以帮我完成我要在下面写的示例吗?我不太了解 WCF 使用的模式,也没有找到关于该主题的太多文档。

有人可以帮我完成以下示例吗?此外,我如何衡量服务将能够处理比典型同步实现更多的负载?

using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Threading.Tasks;

namespace WcfRestService1
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = 
        AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class Service1
    {
        private BusinessLogic bll = new BusinessLogic();

        // Synchronous version
        [WebGet(UriTemplate = "/sync")]
        public string GetSamples()
        {
            return bll.ComputeData();
        }

        // Asynchronous version - Begin
        [WebGet(UriTemplate = "/async")]
        [OperationContract(AsyncPattern = true)]
        public IAsyncResult BeginGetSampleAsync(AsyncCallback callback, 
            object state)
        {
            Task<string> t = bll.ComputeDataAsync();

            // What am I suppose to return here
            // return t.AsyncState; ???
        }

        // Asynchronous version - End
        public List<SampleItem> EndGetSampleAsync(IAsyncResult result)
        {
            // How do I handle the callback here?
        }
    }

    public class BusinessLogic
    {
        public Task<string> ComputeDataAsync()
        {
            DataAccessLayer dal = new DataAccessLayer();
            return dal.GetData();
        }

        public string ComputeData()
        {
            Task<string> t = this.ComputeDataAsync();

            // I am blocking... Waiting for the data
            t.Wait();

            return t.Result;
        }
    }

    public class DataAccessLayer
    {
        public Task<string> GetData()
        {
            // Read data from disk or network or db
        }
    }
}

【问题讨论】:

  • 您的部分答案在这里:stackoverflow.com/questions/5161159/…,但不确定 REST 部分
  • 您是否希望您的数据库占用时间最长并且您打算使用 EF 吗?因为 EF 没有像 BeginExecuteReader 这样的异步方法,所以你可以启动一个任务,它会释放你的 wcf 线程,但是通过任务调用 EF 仍然会阻塞......
  • 你能解释一下为什么吗?听起来您可能正在尝试使用错误的解决方案来解决问题。
  • @np-hard:我不打算使用 EF。我不是想让 WCF 服务“更快”,我希望它能够处理更多负载(即更多连接和更少线程)
  • 请查看使用sql命令更新的代码,异步模式一直到数据层。

标签: c# wcf asynchronous task-parallel-library


【解决方案1】:

这是一个例子。我在以下帖子的帮助下得到了它:

编辑:添加了异步客户端的示例

Implement Classic Async Pattern using TPL
http://pfelix.wordpress.com/2008/06/27/wcf-and-the-asyncpattern-property-part-1/ http://pfelix.wordpress.com/2008/06/28/wcf-and-the-asyncpattern-part-2/

这里有一个小服务:

namespace WcfAsyncTest { [ServiceContract] public interface IAsyncTest { [OperationContract(AsyncPattern=true)] IAsyncResult BeginOperation(AsyncCallback callback, object state); string EndOperation(IAsyncResult ar); } // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together. [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class Service1 : IAsyncTest { public IAsyncResult BeginOperation(AsyncCallback callback, object state) { Task result = Task.Factory.StartNew((x) => { // spin to simulate some work var stop = DateTime.Now.AddSeconds(10); while (DateTime.Now < stop) Thread.Sleep(100); }, state); if (callback != null) result.ContinueWith(t => callback(t)); return result; } public string EndOperation(IAsyncResult ar) { ar.AsyncWaitHandle.WaitOne(); return "Hello!!"; } } }

这是客户端(命令行):

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestClient { class Program { static void Main(string[] args) { var client = new ServiceReference1.AsyncTestClient(); var result = client.Operation(); Console.WriteLine(result); Console.ReadLine(); } } }

如果您在服务上放置跟踪点,您可以看到 WCF 确实在为您调用 EndOperation。

异步客户端示例

首先,您需要生成一个异步代理。您可以通过右键单击服务参考(在项目的参考文件夹中)并选择“配置服务参考”来完成此操作。选中“生成异步操作”复选框。

现在您的客户端代理将拥有一些以前没有的新成员。以下是它们的使用方法:

// this is in the command-line test client // no changes to your service required. static void AsyncTest() { var client = new ServiceReference1.AsyncTestClient(); client.OperationCompleted += new EventHandler(client_OperationCompleted); client.OperationAsync(); Console.WriteLine("Operation Running"); } static void client_OperationCompleted(object sender, ServiceReference1.OperationCompletedEventArgs e) { if (e.Error == null) Console.WriteLine("Operation Complete. Result: " + e.Result); else Console.WriteLine(e.Error.ToString()); }

【讨论】:

  • 缺少 } 和);在那个 TaskFactory.StartNew()
  • @dthorpe 谢谢!实际上是 while 语句上的小于号造成的——我刚刚从代码编辑器中复制粘贴,我没想过要逃避它。
  • imo 你真的不应该在 EndXXX 方法中执行 WaitOne,因为这会阻塞你的 WCF 线程,我猜想拥有异步 wcf 解决方案的主要驱动因素是不阻塞,并提高可伸缩性
  • 感谢 JMarsh!但是,我还想了解我是如何使一切异步的(包括三层:Service、BLL 和 DAO)。
  • @np-hard:结束块是 MS 异步模型标准的一部分 (msdn.microsoft.com/en-us/library/ms228963.aspx) 因为 wcf 在结束之前不会调用 EndOperation,因此在这种情况下调用永远不会阻塞.但是,调用 BeginOperation,做某事,然后调用 EndOperation() 是有效的(在非 wcf 用例中),期望您将被阻止直到操作完成(有关更详细的说明,请参见上面的链接) .
【解决方案2】:

这是一个实现异步的服务的实现。在这种情况下,wcf 的回调一直传递给 ado.net 的 sql 命令。当命令返回时,它会调用服务的EndXXX 方法,该方法会调用业务层,最终会调用SqlCommand 的EndXXX。如果您遇到任何问题,请告诉我

public class Service
    {
        private BusinessLogic businessLayer = new BusinessLogic();
        public IAsyncResult BeginAnyOperation(AsyncCallback callback, object userState)
        {
            return businessLayer.BeginComputeData(callback, userState);
        }
        public string EndAnyOperation(IAsyncResult result)
        {
            return businessLayer.EndComputeDate(result);
        }
    }

    public class MyState<T> : IAsyncResult
    {
        public MyState() { }
        public object AsyncState { get; set; }
        public WaitHandle AsyncWaitHandle { get; set; }
        public bool CompletedSynchronously
        {
            get { return true; }
        }
        public bool IsCompleted { get; set; }
        public AsyncCallback AsyncCallback { get; set; }
        public T Result { get; set; }
        public IAsyncResult InnerResult { get; set; }
    }

    public class BusinessLogic
    {
        private DataAccessLayer dal = new DataAccessLayer();
        public IAsyncResult BeginComputeData(AsyncCallback callback, object state)
        {
            return dal.BeginGetData(callback, state);
        }
        public string EndComputeDate(IAsyncResult asyncResult)
        {
            return dal.EndGetData(asyncResult);
        }
    }

    public class DataAccessLayer
    {
        public IAsyncResult BeginGetData(AsyncCallback callback, object state)
        {
            var conn = new SqlConnection("");
            conn.Open();
            SqlCommand cmd = new SqlCommand("myProc", conn);
            var commandResult = cmd.BeginExecuteReader(callback, state, System.Data.CommandBehavior.CloseConnection);
            return new MyState<string> { AsyncState = cmd, InnerResult = commandResult };
        }
        public string EndGetData(IAsyncResult result)
        {
            var state = (MyState<string>)result;
            var command = (SqlCommand)state.AsyncState;
            var reader = command.EndExecuteReader(state.InnerResult);
            if (reader.Read())
                return reader.GetString(0);
            return string.Empty;
        }
    }

【讨论】:

    猜你喜欢
    • 2015-07-01
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多