Подтвердить что ты не робот

Как сделать метод REST WCF полностью асинхронным с параллельной библиотекой задач?

Я пытаюсь сделать метод WCF REST полностью асинхронным (я не хочу ничего блокировать). По сути, у меня есть простой сервис с тремя слоями: Service, Business Logic и Data Access Layer. Уровень доступа к данным имеет доступ к базе данных, и для получения ответа от этого метода может потребоваться несколько секунд.

Я не очень хорошо понимаю, как цепочки всех этих методов работают. Может кто-то, пожалуйста, помогите мне заполнить образец, который я пытаюсь написать ниже? Я не очень хорошо понимаю шаблон, используемый 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
        }
    }
}
4b9b3361

Ответ 1

Вот пример. Я получил работу с помощью следующих сообщений:

Изменить: добавлен пример асинхронного клиента

Внедрение классического шаблона Async с использованием 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 для вас.

Пример клиента Async

Сначала вам нужно создать прокси-сервер async. Вы можете сделать это, щелкнув правой кнопкой мыши ссылку на службу (в папке "Ссылки" вашего проекта) и выбрав "Настроить сервисную ссылку". Установите флажок "Генерировать асинхронные операции".

Теперь у вашего клиентского прокси будут новые члены, которых раньше не было. Вот как их использовать:


// 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());
}


Ответ 2

вот реализация службы, которая реализует Async. В этом обратный вызов wcf передается до команды ado.net sql. Когда команда вернется, она вызовет метод EndXXX службы, который вызовет бизнес-уровень, который, наконец, вызовет EndXXX из SqlCommand. Дайте мне знать, если вы столкнулись с какими-либо проблемами.

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;
        }
    }