Как передать произвольный метод в класс для асинхронного выполнения, отслеживания статуса и получения результата?

Приветствую уважаемое сообщество.

Краткая вводная часть
Сейчас пишу небольшой WCF Restful сервис, возникла задача выполнять длительные запросы. Наиболее простым и понятным решением было принимать такие запросы на обработку, выдавая номер задания и соответствующий статус. Затем клиент обращается с номером задания по другому адресу с номером и получает свой ответ (если он уже готов). Идея не новая, очевидная и была подтверждена поисками:
- billhiggins.us/blog/2011/04/27/resty-long-ops (очень хорошо описано)
- lostechies.com/jimmybogard/2013/05/15/eventual-con...

Что я пытался сделать
Вспомнив, что около года назад писал что-то подобное, посмотрел архивы кода. Оказалось, что в тот раз мне нужно было выполнять всего один длительный метод, так что решил я всё быстро и просто (подозреваю, что далеко не идеально, но работает) - долгий метод был обёрнут в класс, подобный этому:
using System;
using System.ComponentModel;
namespace Service
{
    public class LongTermJob
    {
	public string JobId { get; private set; }
        public bool IsCompleted
        {
            get { return !_worker.IsBusy; }
        }
        public string Result
        {
            get { return IsCompleted ? _result : null; }
        }
        private readonly BackgroundWorker _worker;
        // Результаты выполнения, вместо string нужный тип.
        private string _result;
        public LongTermJob(string jobId, WorkerArgs args)
        {
	    JobId = jobId;
            _worker = new BackgroundWorker();
            _worker.DoWork += _worker_DoWork;
            _worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
            _worker.RunWorkerAsync(args);
        }
        private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _result = "Результат выполнения метода";
        }
        private void _worker_DoWork(object sender, DoWorkEventArgs e)
        {
            var user = (WorkerArgs) e.Argument;
            Console.WriteLine("Name: {0} Age: {1}", user.Name, user.Age);
        }
    }
    public class WorkerArgs
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
Параметры запроса принимались, помещались в WorkerArgs, присваивался JobId (по которому потом осуществлялся возврат), создавался экземпляр LongTermJob и просто помещался в List<>. Затем при обращении проверялся статус задания и при надобности возвращался результат. Для того случая было удобно, даже процент выполнения при надобности можно выдавать.

Но в этот раз такой код не подходит. Требуется выполнять неопределённое время уже не один, а десятки самых разных методов, которые возвращают самые разные типы и имеют произвольное количество параметров.
Я начал читать и пробовать варианты с:
- делегатами, но они позволяют передать только конкретные параметры и возвращать один тип;
- Task'ами, но в него ещё надо как то передать метод;
- Func'ами, чтобы передавать их в Task'и, но у них перегрузки под каждое количество параметров, как передавать произвольное.

В общем, перепробовал всё, что смог. Сейчас остановится примерно на таком коде:
public class LongTermJob
    {
        public bool IsCompleted
        {
            get { return _task.IsCompleted; }
        }
        public TestClass Result
        {
            get { return IsCompleted ? _result : null; }
        }
        private readonly Task<TestClass> _task;
        private readonly TestClass _result;
        public LongTermJob(Func<TestClass> method)
        {
            _task = Task.Factory.StartNew((method));
            _result = _task.Result;
        }
    }
    public class WorkerArgs
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
Если бы вот такой код, но чтобы TestClass был <T>...

Возможно, надо подойти совсем с другой стороны или есть уже готовые решения для подобных ситуаций. Буду благодарен любому решению и предложению.
  • Вопрос задан
  • 3709 просмотров
Решения вопроса 1
Vadimyan
@Vadimyan
Программист C#
Возможно, до тех пор, как я доберусь до вашего вопроса, кто-то другой вспомнит промышленное решение.
Я для унификации сигнатур функций использовался вот этим методом:
habrahabr.ru/post/143465
То есть, любую функцию с N параметрами при помощи карринга / частичного применения можно привести к виду TResult Method(). Из недостатков - класс FunctionalExtensions у меня содержал порядка десятка методов под разное количество параметров, приводимое к разному числу параметров. Вам удобнее - нужно функции с разным числом параметров привести к одному виду - функции без параметров.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы