Bagikan melalui


Menggunakan WorkflowInvoker dan WorkflowApplication

Windows Workflow Foundation (WF) menyediakan beberapa metode alur kerja hosting. WorkflowInvoker menyediakan cara sederhana untuk menjalankan alur kerja seolah-olah cara itu adalah pemanggilan metode dan hanya dapat digunakan untuk alur kerja yang tidak menggunakan persistensi. WorkflowApplication menyediakan model yang lebih kaya untuk menjalankan alur kerja yang mencakup pemberitahuan peristiwa siklus hidup, kontrol eksekusi, dimulainya kembali bookmark, dan persistensi. WorkflowServiceHost memberikan dukungan untuk aktivitas perpesanan dan terutama digunakan dengan layanan alur kerja. Topik ini memperkenalkan Anda ke hosting alur kerja dengan WorkflowInvoker dan WorkflowApplication. Untuk informasi selengkapnya tentang alur kerja hosting dengan WorkflowServiceHost, baca Layanan Alur Kerja dan Ringkasan Layanan Alur Kerja Hosting.

Menggunakan WorkflowInvoker

WorkflowInvoker menyediakan model untuk mengeksekusi alur kerja seolah-olah itu adalah pemanggilan metode. Untuk memanggil alur kerja menggunakan WorkflowInvoker, panggil metode Invoke dan teruskan definisi alur kerja dari alur kerja yang akan dipanggil. Dalam contoh ini, aktivitas WriteLine dipanggil menggunakan WorkflowInvoker.

Activity wf = new WriteLine
{
    Text = "Hello World."
};

WorkflowInvoker.Invoke(wf);

Saat alur kerja dipanggil menggunakan WorkflowInvoker, alur kerja dijalankan pada utas panggilan dan metode Invoke memblokir hingga alur kerja selesai, termasuk waktu diam apa pun. Untuk mengonfigurasi interval waktu habis ketika alur kerja harus diselesaikan, gunakan salah satu dari Invoke kelebihan beban yang menggunakan parameter TimeSpan. Dalam contoh ini, alur kerja dipanggil dua kali dengan dua interval waktu habis yang berbeda. Alur kerja pertama selesai, tetapi yang kedua tidak.

Activity wf = new Sequence()
{
    Activities =
    {
        new WriteLine()
        {
            Text = "Before the 1 minute delay."
        },
        new Delay()
        {
            Duration = TimeSpan.FromMinutes(1)
        },
        new WriteLine()
        {
            Text = "After the 1 minute delay."
        }
    }
};

// This workflow completes successfully.
WorkflowInvoker.Invoke(wf, TimeSpan.FromMinutes(2));

// This workflow does not complete and a TimeoutException
// is thrown.
try
{
    WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(30));
}
catch (TimeoutException ex)
{
    Console.WriteLine(ex.Message);
}

Catatan

TimeoutException hanya ditampilkan jika interval waktu habis dan alur kerja menjadi tidak aktif selama eksekusi. Alur kerja yang membutuhkan waktu lebih lama dari interval waktu habis yang ditentukan untuk diselesaikan dengan sukses jika alur kerja tidak menjadi diam.

WorkflowInvoker juga menyediakan versi asinkron dari metode panggilan. Untuk informasi lebih lanjut, lihat InvokeAsync dan BeginInvoke.

Mengatur Argumen Input dari Alur Kerja

Data dapat diteruskan ke alur kerja menggunakan kamus parameter input, dikunci dengan nama argumen, yang dipetakan ke argumen input alur kerja. Dalam contoh ini, WriteLine dipanggil dan nilai untuk argumen Text-nya ditentukan menggunakan kamus parameter input.

Activity wf = new WriteLine();

Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World.");

WorkflowInvoker.Invoke(wf, inputs);

Mengambil Argumen Output dari Alur Kerja

Parameter output dari alur kerja dapat diperoleh menggunakan kamus output yang dikembalikan dari panggilan ke Invoke. Contoh berikut memanggil alur kerja yang terdiri dari aktivitas Divide tunggal yang memiliki dua argumen input dan dua argumen output. Saat alur kerja dipanggil, kamus arguments diteruskan yang berisi nilai untuk setiap argumen input, dikunci dengan nama argumen. Saat panggilan ke Invoke kembali, setiap argumen output dikembalikan dalam kamus outputs, juga dikunci dengan nama argumen.

public sealed class Divide : CodeActivity
{
    [RequiredArgument]
    public InArgument<int> Dividend { get; set; }

    [RequiredArgument]
    public InArgument<int> Divisor { get; set; }

    public OutArgument<int> Remainder { get; set; }
    public OutArgument<int> Result { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        int quotient = Dividend.Get(context) / Divisor.Get(context);
        int remainder = Dividend.Get(context) % Divisor.Get(context);

        Result.Set(context, quotient);
        Remainder.Set(context, remainder);
    }
}
int dividend = 500;
int divisor = 36;

Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);

IDictionary<string, object> outputs =
    WorkflowInvoker.Invoke(new Divide(), arguments);

Console.WriteLine("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], outputs["Remainder"]);

Jika alur kerja berasal dari ActivityWithResult, seperti CodeActivity<TResult> atau Activity<TResult>, dan ada argumen output selain argumen output Result yang didefinisikan dengan baik, kelebihan beban non-generik Invoke harus digunakan dalam untuk mengambil argumen tambahan. Untuk melakukannya, definisi alur kerja yang diteruskan ke Invoke harus berjenis Activity. Dalam contoh ini, aktivitas Divide berasal dari CodeActivity<int>, tetapi dideklarasikan sebagai Activity sehingga kelebihan non-generik dari Invoke digunakan yang mengembalikan kamus argumen bukan satu nilai pengembalian.

public sealed class Divide : CodeActivity<int>
{
    public InArgument<int> Dividend { get; set; }
    public InArgument<int> Divisor { get; set; }
    public OutArgument<int> Remainder { get; set; }

    protected override int Execute(CodeActivityContext context)
    {
        int quotient = Dividend.Get(context) / Divisor.Get(context);
        int remainder = Dividend.Get(context) % Divisor.Get(context);

        Remainder.Set(context, remainder);

        return quotient;
    }
}
int dividend = 500;
int divisor = 36;

Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);

Activity wf = new Divide();

IDictionary<string, object> outputs =
    WorkflowInvoker.Invoke(wf, arguments);

Console.WriteLine("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], outputs["Remainder"]);

Menggunakan WorkflowApplication

WorkflowApplication menyediakan serangkaian fitur yang kaya untuk pengelolaan instans alur kerja. WorkflowApplication bertindak sebagai proksi aman utas ke WorkflowInstance yang sebenarnya, yang merangkum waktu proses, dan menyediakan metode untuk membuat dan memuat instans alur kerja, menjeda dan melanjutkan, menghentikan, dan pemberitahuan peristiwa siklus hidup. Untuk menjalankan alur kerja menggunakan WorkflowApplication, Anda membuat WorkflowApplication, berlangganan peristiwa siklus hidup yang diinginkan, memulai alur kerja, lalu menunggu hingga selesai. Dalam contoh ini, definisi alur kerja yang terdiri dari aktivitas WriteLine dibuat dan WorkflowApplication dibuat menggunakan definisi alur kerja yang ditentukan. Completed ditangani sehingga host diberi tahu saat alur kerja selesai, alur kerja dimulai dengan panggilan ke Run, lalu host menunggu alur kerja selesai. Saat alur kerja selesai, AutoResetEvent diatur dan aplikasi host dapat melanjutkan eksekusi, seperti yang ditunjukkan pada contoh berikut.

AutoResetEvent syncEvent = new AutoResetEvent(false);

Activity wf = new WriteLine
{
    Text = "Hello World."
};

// Create the WorkflowApplication using the desired
// workflow definition.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Handle the desired lifecycle events.
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    syncEvent.Set();
};

// Start the workflow.
wfApp.Run();

// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();

Peristiwa Siklus Hidup WorkflowApplication

Selain Completed, pembuat host dapat diberi tahu saat alur kerja dibongkar (Unloaded), dibatalkan (Aborted), menjadi tidak aktif (Idle dan PersistableIdle), atau terjadi pengecualian yang tidak tertangani (OnUnhandledException). Pengembang aplikasi alur kerja dapat menangani pemberitahuan ini dan mengambil tindakan yang sesuai, seperti yang ditunjukkan dalam contoh berikut.

wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
        Console.WriteLine("Exception: {0}\n{1}",
            e.TerminationException.GetType().FullName,
            e.TerminationException.Message);
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
    }
    else
    {
        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        // Console.WriteLine("The winner is {0}.", e.Outputs["Winner"]);
    }
};

wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine("Workflow {0} Aborted.", e.InstanceId);
    Console.WriteLine("Exception: {0}\n{1}",
        e.Reason.GetType().FullName,
        e.Reason.Message);
};

wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
    // Perform any processing that should occur
    // when a workflow goes idle. If the workflow can persist,
    // both Idle and PersistableIdle are called in that order.
    Console.WriteLine("Workflow {0} Idle.", e.InstanceId);
};

wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
{
    // Instruct the runtime to persist and unload the workflow.
    // Choices are None, Persist, and Unload.
    return PersistableIdleAction.Unload;
};

wfApp.Unloaded = delegate(WorkflowApplicationEventArgs e)
{
    Console.WriteLine("Workflow {0} Unloaded.", e.InstanceId);
};

wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
    // Display the unhandled exception.
    Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
        e.InstanceId, e.UnhandledException.Message);

    Console.WriteLine("ExceptionSource: {0} - {1}",
        e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);

    // Instruct the runtime to terminate the workflow.
    // Other choices are Abort and Cancel. Terminate
    // is the default if no OnUnhandledException handler
    // is present.
    return UnhandledExceptionAction.Terminate;
};

Mengatur Argumen Input dari Alur Kerja

Data dapat diteruskan ke alur kerja saat dimulai menggunakan kamus parameter, mirip dengan cara data diteruskan saat menggunakan WorkflowInvoker. Setiap item dalam kamus dipetakan ke argumen input dari alur kerja yang ditentukan. Dalam contoh ini, alur kerja yang terdiri dari aktivitas WriteLine dipanggil dan argumen Text-nya ditentukan menggunakan kamus parameter input.

AutoResetEvent syncEvent = new AutoResetEvent(false);

Activity wf = new WriteLine();

// Create the dictionary of input parameters.
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World!");

// Create the WorkflowApplication using the desired
// workflow definition and dictionary of input parameters.
WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);

// Handle the desired lifecycle events.
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    syncEvent.Set();
};

// Start the workflow.
wfApp.Run();

// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();

Mengambil Argumen Output dari Alur Kerja

Saat alur kerja selesai, argumen output apa pun dapat diambil di penanganan Completed dengan mengakses kamus WorkflowApplicationCompletedEventArgs.Outputs. Contoh berikut meng-hosting alur kerja menggunakan WorkflowApplication. Instans WorkflowApplication dibuat menggunakan definisi alur kerja yang terdiri dari aktivitas DiceRoll tunggal. Aktivitas DiceRoll memiliki dua argumen output yang mewakili hasil operasi pelemparan dadu. Saat alur kerja selesai, output diambil di penanganan Completed.

public sealed class DiceRoll : CodeActivity
{
    public OutArgument<int> D1 { get; set; }
    public OutArgument<int> D2 { get; set; }

    static Random r = new Random();

    protected override void Execute(CodeActivityContext context)
    {
        D1.Set(context, r.Next(1, 7));
        D2.Set(context, r.Next(1, 7));
    }
}
 // Create a WorkflowApplication instance.
 WorkflowApplication wfApp = new WorkflowApplication(new DiceRoll());

 // Subscribe to any desired workflow lifecycle events.
 wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
 {
     if (e.CompletionState == ActivityInstanceState.Faulted)
     {
         Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
         Console.WriteLine("Exception: {0}\n{1}",
             e.TerminationException.GetType().FullName,
             e.TerminationException.Message);
     }
     else if (e.CompletionState == ActivityInstanceState.Canceled)
     {
         Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
     }
     else
     {
         Console.WriteLine("Workflow {0} Completed.", e.InstanceId);

         // Outputs can be retrieved from the Outputs dictionary,
         // keyed by argument name.
         Console.WriteLine("The two dice are {0} and {1}.",
             e.Outputs["D1"], e.Outputs["D2"]);
     }
 };

// Run the workflow.
 wfApp.Run();

Catatan

WorkflowApplication dan WorkflowInvoker mengambil kamus argumen input dan mengembalikan kamus argumen out. Parameter kamus, properti, dan nilai pengembalian ini berjenis IDictionary<string, object>. Instans sebenarnya dari kelas kamus yang diteruskan dapat berupa kelas apa pun yang mengimplementasikan IDictionary<string, object>. Dalam contoh ini, Dictionary<string, object> digunakan. Untuk informasi selengkapnya tentang kamus, baca IDictionary<TKey,TValue> dan Dictionary<TKey,TValue>.

Melewatkan Data ke Alur Kerja yang Berjalan Menggunakan Bookmark

Bookmark adalah mekanisme ketika suatu aktivitas dapat secara pasif menunggu untuk dilanjutkan dan merupakan mekanisme untuk meneruskan data ke instans alur kerja yang sedang berjalan. Jika suatu aktivitas menunggu data, aktivitas tersebut dapat membuat Bookmark dan mendaftarkan metode panggilan balik untuk dipanggil saat Bookmark dilanjutkan, seperti yang ditunjukkan pada contoh berikut.

public sealed class ReadLine : NativeActivity<string>
{
    [RequiredArgument]
    public InArgument<string> BookmarkName { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        // Create a Bookmark and wait for it to be resumed.
        context.CreateBookmark(BookmarkName.Get(context),
            new BookmarkCallback(OnResumeBookmark));
    }

    // NativeActivity derived activities that do asynchronous operations by calling
    // one of the CreateBookmark overloads defined on System.Activities.NativeActivityContext
    // must override the CanInduceIdle property and return true.
    protected override bool CanInduceIdle
    {
        get { return true; }
    }

    public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
    {
        // When the Bookmark is resumed, assign its value to
        // the Result argument.
        Result.Set(context, (string)obj);
    }

Saat dijalankan, aktivitas ReadLine membuat Bookmark, mendaftarkan panggilan balik, lalu menunggu Bookmark untuk dilanjutkan. Saat dilanjutkan, aktivitas ReadLine menetapkan data yang diteruskan dengan Bookmark ke argumen Result-nya. Dalam contoh ini, alur kerja dibuat yang menggunakan aktivitas ReadLine untuk mengumpulkan nama pengguna dan menampilkannya ke jendela konsol.

Variable<string> name = new Variable<string>();

Activity wf = new Sequence
{
    Variables = { name },
    Activities =
     {
         new WriteLine
         {
             Text = "What is your name?"
         },
         new ReadLine
         {
             BookmarkName = "UserName",
             Result = new OutArgument<string>(name)
         },
         new WriteLine
         {
             Text = new InArgument<string>((env) =>
                 ("Hello, " + name.Get(env)))
         }
     }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);

wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
    idleEvent.Set();
};

// Run the workflow.
wfApp.Run();

// Wait for the workflow to go idle before gathering
// the user's input.
idleEvent.WaitOne();

// Gather the user's input and resume the bookmark.
// Bookmark resumption only occurs when the workflow
// is idle. If a call to ResumeBookmark is made and the workflow
// is not idle, ResumeBookmark blocks until the workflow becomes
// idle before resuming the bookmark.
BookmarkResumptionResult result = wfApp.ResumeBookmark("UserName",
    Console.ReadLine());

// Possible BookmarkResumptionResult values:
// Success, NotFound, or NotReady
Console.WriteLine("BookmarkResumptionResult: {0}", result);

Saat aktivitas ReadLine dijalankan, Bookmark bernama UserName dibuat dan menunggu marka buku untuk dilanjutkan. Host mengumpulkan data yang diinginkan dan kemudian melanjutkan Bookmark. Alur kerja dilanjutkan, menampilkan nama, dan kemudian selesai.

Aplikasi host dapat memeriksa alur kerja untuk menentukan apakah ada bookmark yang aktif. Ini dapat dilakukan dengan memanggil metode GetBookmarks dari instans WorkflowApplication, atau dengan memeriksa WorkflowApplicationIdleEventArgs di penanganan Idle.

Contoh kode berikut ini mirip dengan contoh sebelumnya kecuali bookmark-nya yang aktif dienumerasikan sebelum bookmark dilanjutkan. Alur kerja dimulai, dan setelah Bookmark dibuat dan alur kerja tidak digunakan, GetBookmarks dipanggil. Ketika alur kerja selesai, output berikut ditampilkan ke konsol.

Siapa nama Anda?
BookmarkName: UserName - OwnerDisplayName: ReadLineSteveHalo, Steve

Variable<string> name = new Variable<string>();

Activity wf = new Sequence
{
    Variables = { name },
    Activities =
     {
         new WriteLine
         {
             Text = "What is your name?"
         },
         new ReadLine
         {
             BookmarkName = "UserName",
             Result = new OutArgument<string>(name)
         },
         new WriteLine
         {
             Text = new InArgument<string>((env) =>
                 ("Hello, " + name.Get(env)))
         }
     }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);

wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
    // You can also inspect the bookmarks from the Idle handler
    // using e.Bookmarks

    idleEvent.Set();
};

// Run the workflow.
wfApp.Run();

// Wait for the workflow to go idle and give it a chance
// to create the Bookmark.
idleEvent.WaitOne();

// Inspect the bookmarks
foreach (BookmarkInfo info in wfApp.GetBookmarks())
{
    Console.WriteLine("BookmarkName: {0} - OwnerDisplayName: {1}",
        info.BookmarkName, info.OwnerDisplayName);
}

// Gather the user's input and resume the bookmark.
wfApp.ResumeBookmark("UserName", Console.ReadLine());

Contoh kode berikut memeriksa WorkflowApplicationIdleEventArgs yang diteruskan ke penanganan Idle dari instans WorkflowApplication. Dalam contoh ini, alur kerja yang tidak aktif memiliki satu Bookmark dengan nama EnterGuess, yang dimiliki oleh aktivitas bernama ReadInt. Contoh kode ini didasarkan pada Cara: Menjalankan Alur Kerja, yang merupakan bagian dari Tutorial Memulai. Jika penanganan Idle pada langkah tersebut dimodifikasi untuk memuat kode dari contoh ini, output berikut akan ditampilkan.

BookmarkName: EnterGuess - OwnerDisplayName: ReadInt

wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
    foreach (BookmarkInfo info in e.Bookmarks)
    {
        Console.WriteLine("BookmarkName: {0} - OwnerDisplayName: {1}",
            info.BookmarkName, info.OwnerDisplayName);
    }

    idleEvent.Set();
};

Ringkasan

WorkflowInvoker menyediakan cara ringan untuk memanggil alur kerja, dan meskipun menyediakan metode untuk meneruskan data di awal alur kerja dan mengekstraksi data dari alur kerja yang sudah selesai, itu tidak menyediakan skenario yang lebih kompleks ketika WorkflowApplication dapat digunakan.