ActivityRest Windows Workflow Activity

В данном статье рассказывается об ActivityRest. Эта Activity интресна тем, что работает в составе трехзвенной архитектуры, причем работает с использованием технологий REST, WCF, WorkFlow. Происходит это следующим образом, клиентская машина передает запрос на сервер используя технологию WCF, сервер получив запрос, запускает WorkFlow, в состав которого входит ActivityRest, которая обращается за данными, по технологгии REST, находящимися в сети по определенному URI - суть к третьей машине. Считанные данных последовательно пересылаются сначала на сервер клиента, затем с сервера на машину клиента.
Представлен рабочий проект MS Visual Studio 2010 содержащий WCF + WorkFlow + REST + WindowsFormClient, в котором применяется описываемое Activity.
1. Входные данные ActivityRest.
Входные аргументы для Activity собраны в классе InputArgument и состоят из двух полей:
public class InputArgument
{
     public string UrlString     { get; set; }
     public string CustomerID    { get; set; }
}
2. Выходные данные ActivityRest.
Для выходных данных не определяется специальный класс. Однако, тип выходных данных опреден в составе Activity. В данном случае это результат Linq запроса - IEnumerable<Order>.
protected override IEnumerable<Order> EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
    try
    {
3. Исходный текст ActivityRest.
Как обычно, класс реализующий Activity создан асинхронным, содержащим два метода:
  • BeginExecute;
  • EndExecute.

В данном случае, это более чем обосновано, так как информация запрашивается и передается с одного сервера на другой, используя технологию REST протокола HTTP, так что, неминуемы задержки. Основным элементом ActivityRest является Linq запрос на получение данных, предоставляемых с URI: http://services.odata.org/Northwind/Northwind.svc/

/* 10.01.2013. Простой Activity. Замечателен тем, что получает от клиента в качестве
   входных данных Uri и фильтр для данных. Однако, данные читает не со своего сервера, а обращается
   к сервису на третьей  машине по полученному Uri: http://services.odata.org/Northwind/Northwind.svc/
   Работает асинхронно. Получив данные отправляет их клиенту используя WCF и WorkFlow.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using WorkFlowRest.ServiceReference1;
using System.Data.Services.Client;
using ClassLibrary;
using System.ComponentModel;

namespace WorkFlowRest
{
    public sealed class CodeActivityRest : AsyncCodeActivity<IEnumerable<Order>>
    {
        [RequiredArgument]
        [DefaultValue(null)]
        public InArgument<InputArgument> inputArguments { get; set; }

        NorthwindEntities entityContext             = null;
        DataServiceQuery<Order> entityQuery         = null;

        // Temporary variable for input arguments
        private InputArgument inputObject           = null;
        private string uri                          = null;
        private string customerId                   = null;

        protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
        {
            try
            {
                // Input Object
                inputObject = inputArguments.Get(context);

                //Uri for service
                uri = inputObject.UrlString;
                if (String.IsNullOrEmpty(uri)) throw new ArgumentNullException("Value", "Uri string is Empty");

                // Filter for Data
                customerId = inputObject.CustomerID;
                if (customerId == null) throw new ArgumentNullException("Value", "CustomerID string is null");

                // Set EntityContext
                entityContext = new NorthwindEntities(new Uri(uri));

                // Create entityQuery
                entityQuery = (DataServiceQuery<Order>)
                     from obj in entityContext.Orders
                     where obj.Customer.CustomerID.Contains(customerId)
                     select obj;

                context.UserState = entityQuery;
                return entityQuery.BeginExecute(callback, state);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
                return null;
            }
        }

        protected override IEnumerable<Order> EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
        {
            try
            {
                // Get context.UserState
                entityQuery = context.UserState as DataServiceQuery<Order>;

                // Return entityQuery.
                return entityQuery.EndExecute(result);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
                return null;
            }
        }
    }
}
4. Клиент для ActivityRest
Ниже приведен текст клиента, который формирует и передает URI и фильт данных для ActivityRest в виде строковых полей класса InputArgument.Затем, клиент принимает данные от сервера, и уже на принятых данных, выполняет еще один Linq запрос. Этот запрос выполняет группировку и сортировку данных по полю CustomerID. Результат второго Linq запроса предназначается для компонента ComboBox в качестве источника данных.
/* 10.03.2012. Клиент для AcnivityRest.
   Формирует и передает на сервер Uri и фильтр для получения данных.
   Отображает полученные данные. Интересен тем, что выполняет Linq запрос на
   полученном ранее результате запроса (первый запрос в ActivityRect).
   Этот второй Linq запрос используется для
   группировки данных с целью формирования DataSource для ComboBox.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using WindowsFormsClient.ServiceReference2;
using ClassLibrary;

namespace WindowsFormsClient
{
    public partial class FormClient : Form
    {
        // Link for Service Client
        ServiceClient client = null;
        // Input Class
        private ClassLibrary.InputArgument input = null;

        public FormClient()
        {
            InitializeComponent();
            // Create Objects and set parameters
            client = new ServiceClient();
            input = new InputArgument();
            input.CustomerID = string.Empty;
            input.UrlString = @"http://services.odata.org/Northwind/Northwind.svc/";
        }

        // Event click button for clean filter
        private void button1_Click(object sender, EventArgs e)
        {
            // Empty Filter comboBox1
            comboBox1.Text = "";
            input.CustomerID = string.Empty;
            //Call Form1_Load
            FormClient_Load(sender, e);
        }

        // Load Data
        private void FormClient_Load(object sender, EventArgs e)
        {
            try
            {
                // Send and Get Data
                var order = client.GetData(input);
                // Too Query
                var groupCustomerID = from row in order
                                      // orderby row.CustomerID ascending
                                      group row by row.CustomerID into grouping
                                      orderby grouping.Key ascending
                                      select grouping.Key;

                foreach (var item in groupCustomerID) comboBox1.Items.Add(item.ToString());

                // Exception is order == null
                if (order == null) { throw new ArgumentNullException("Value", "Order is null"); }

                // Set DataSource
                dataGridView1.DataSource = order;
                label3.Text = order.Count<Order>().ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(" Error: " + ex.ToString());
            }
        }

        // Change CustomerID in ComboBox
        private void comboBox1_TextChanged(object sender, EventArgs e)
        {
            try
            {
                input.CustomerID = (string)comboBox1.SelectedItem;
                // Send and Get Data
                var order = client.GetData(input);
                // Exception
                if (order == null) { throw new ArgumentNullException("Value", "Order is null"); }
                // Set DataSource
                dataGridView1.DataSource = order;
                label3.Text = order.Count<Order>().ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(" Error: " + ex.ToString());
            }
        }
    }
}

Visual Studio 2010 проект содержащий: Activity, Workflow, Windows Form клиент можно загрузить.
Евгений Вересов.
10.01.2013 года.