CompositeActivity Windows Workflow

CompositeActivity интресна тем, что код на исполнение, равно, как и параметры, ей передает клиент. Другими словами, какой код клиент пришле тот Activity и будет выполнять. Звучит необычно. Как это происходит? Activity принимает в качестве параметров пару строк, одна строка это xaml код на исполнение, а вторая строка это Uri, параметр, который потребуется для работы присланного кода. Далее, в основной Activity - это класс Main, создаем динамическю Activity и передаем ей в качестве тела принятый xaml код. Ну, а чтобы окончательно Вас запутать :), скажем, что присланный xaml код кодержит, как составную часть, еще одну Activity - это класс HttpGetData. Далее, в основной Activity, мы бодро запускаем дачерний поток и передаем ему на исполнение динамически созданную Activity.
Однако, главное, повторюсь, в том, что есть возможность запускать в Activity код, который пришлет клиент. Представлен рабочий проект MS Visual Studio 2010 содержащий WCF + WorkFlow + Library + WindowsFormClient, в котором применяется описываемое Activity.
1. Входные аргументы CompositeActivity.
Входные аргументы Activity собраны в классе InputData и состоят из двух полей:
public class InputData
{
     public string url               { get; set; }
     public string xamlStringExecute { get; set; }
}
2. Выходные данные CompositeActivity.
Выходные данные Activity собраны в классе OutputData и состоят из двух полей:
public class OutputData
{
     public string htmlString    { get; set; }
     public string error         { get; set; }
}
3. Исходный текст CompositeActivity class Main.
Основными действиями в Activity являются прием параметров от клиента, создание динамического Activity, передача ему Xaml кода и далее, запуск дочернего процесса c новым Activity.
/* 10.02.2013.
* Activity интересна тем, что сама ничего не выполняет. Единственная ее функция состоит в том,
* чтобы динамически создать другую Activity, передав ей в качестве тела код, полученный от клиента.
* Кроме того, динамически созданной Activity передается и аргумент в виде Uri сайта.
* Далее, Activity Main запускает дочерний поток и в нем вновь созданную Activity.
* Отработав, дочерняя Activity возвращает данные через параметр "argument2".
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Activities.XamlIntegration;
using System.IO;
using System.Xml;
using System.ComponentModel;
using ActivityLibraryProject;
using ClassLibraryProject;

namespace WorkFlowProject
{
    public sealed class Main : CodeActivity
    {
        [RequiredArgument]
        [DefaultValue(null)]
        public InArgument<InputData> inputArgument  { get; set; }
        public OutArgument<OutputData> output       { get; set; }

        private InputData inputObject   = null;
        private OutputData outputObject = null;
        private string executeString    = string.Empty;
        private string uri              = string.Empty;

        protected override void Execute(CodeActivityContext context)
        {
            try
            {
                outputObject = new OutputData();
                // Input object
                inputObject = inputArgument.Get(context);

                // Execute string
                executeString = inputObject.xamlStringExecute;
                if (String.IsNullOrEmpty(executeString)) throw new ArgumentNullException("Value", "Xaml string is Empty");
                
                // Uri for AsyncHttpGet
                uri = inputObject.url;
                if (String.IsNullOrEmpty(uri)) throw new ArgumentNullException("Value", "Uri string is Empty");

                // Create XmlReader
                XmlReader reader = XmlReader.Create(new StringReader(executeString));
                // Create dinamic Activity
                Activity act = ActivityXamlServices.Load(reader);
                // Create input parametet
                IDictionary<string, object> input = new Dictionary<string, object>() { { "argument1", uri } };

                // Calling child process
                IDictionary<string, object> executeChild = WorkflowInvoker.Invoke(act, input);
               
                // Set output data
                outputObject.htmlString = (string)executeChild["argument2"];
                output.Set(context, outputObject);
            }
            catch (Exception ex)
            {
                outputObject.error = ex.ToString();
                output.Set(context, outputObject);
            }
        }
    }
}
4. Вложенная Activity class HttpGetData.
Ниже приведен текст асинхронной Activity, выполняющей http запрос.
/*  Эта Activity помещена в Inside.xaml в качестве вложенной.
*  Выполняет запрос к сайту по принятому аргументу Uri, возвращает ответ сервера в
*  виде документа Html.
*  Взята в качестве примера, вместо этой Activity может быть использована любая другая.
*/  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.ComponentModel;
using System.IO;
using System.Net;

namespace ActivityLibraryProject
{
    public sealed class HttpGetData : AsyncCodeActivity<string>
    {
        [RequiredArgument]
        [DefaultValue(null)]
        public InArgument<string> Uri { get; set; }

        WebRequest query = null;
        string uri = null;

        protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
        {
            uri = Uri.Get(context);
            query = HttpWebRequest.Create(uri);
            context.UserState = query;
            return query.BeginGetResponse(callback, state);
        }

        protected override string EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
        {
            try
            {
                WebRequest query        = (WebRequest)context.UserState;
                WebResponse answer      = query.EndGetResponse(result);
                StreamReader StrReader  = new StreamReader(answer.GetResponseStream());
                return StrReader.ReadToEnd();
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }
    }
}
5. Пример клиента для CompositeActivity.
Клиент формирует две переменные типа string, помещает их в object класса InputData и отправляет на сервер. Принимает данные в object класса OutputData и отображает их.
/* 10.02.2013. Клиент для CompositeActivity.
* Клиент считывает XAML файл приложения с локального диска м передает на сервер для исполнения.
* Кроме того, на сервер передается Uri, как параметр для этого приложения.
* От сервера клиент получает HTML строку, которую отображает в компоненте WebBrowser.
*/
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.ServiceReference1;
using System.IO;

namespace WindowsFormsClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                // String containing the text of the application software
                string xamlStringExecute = File.ReadAllText(@"G:\Data\Избранное\CompositeActiviry\WorkFlowProject\Inside.xaml");

                //Create client
                ServiceClient client = new ServiceClient();

                // References to classes
                ServiceReference1.InputData  inputObject    = new ServiceReference1.InputData();
                ServiceReference1.OutputData outputObject   = new ServiceReference1.OutputData();

                // Input parameters
                inputObject.url = @"http://www.microsoft.com";
                inputObject.xamlStringExecute = xamlStringExecute;

                // Output parameters
                outputObject = client.GetData(inputObject);
                if (!string.IsNullOrEmpty(outputObject.error)) throw new Exception("Error Activity ...");
                webBrowser1.DocumentText = outputObject.htmlString;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}

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