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 года.
|