Bagikan melalui


Menggunakan WorkflowInvoker dan WorkflowApplication

Windows Workflow Foundation (WF) menyediakan beberapa metode alur kerja hosting. WorkflowInvoker menyediakan cara sederhana untuk memanggil alur kerja seolah-olah itu adalah panggilan 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, pengendalian eksekusi, pemulihan kembali marka buku, dan ketahanan. WorkflowServiceHost menyediakan dukungan untuk aktivitas olahpesan 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, lihat Gambaran Umum Layanan Alur Kerja dan Layanan Alur Kerja Hosting.

Menggunakan WorkflowInvoker

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

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

WorkflowInvoker.Invoke(wf);

Ketika alur kerja dipanggil menggunakan WorkflowInvoker, alur kerja dijalankan pada thread pemanggil dan metode Invoke akan memblokir hingga alur kerja selesai, termasuk waktu idle. Untuk mengonfigurasi batas waktu di mana alur kerja harus selesai, gunakan salah satu overload yang memiliki parameter Invoke. 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);
}

Nota

Hanya TimeoutException dilemparkan jika interval waktu habis dan alur kerja menjadi tidak aktif selama eksekusi. Alur kerja yang membutuhkan waktu lebih lama dari interval batas waktu yang ditentukan akan selesai dengan sukses jika alur kerja tidak memasuki kondisi diam.

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

Mengatur Parameter Input Alur Kerja

Data dapat diteruskan ke alur kerja menggunakan kamus parameter input yang didasarkan pada nama argumen, yang memetakan ke argumen input alur kerja. Dalam contoh ini, WriteLine dipanggil dan nilai untuk argumennya Text 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 keluaran dari alur kerja dapat diperoleh dengan menggunakan kamus keluaran yang dikembalikan dari panggilan ke Invoke. Contoh berikut memanggil alur kerja yang terdiri dari satu Divide aktivitas yang memiliki dua argumen input dan dua argumen output. Saat alur kerja dijalankan, arguments kamus data diteruskan yang berisi nilai untuk setiap argumen input, diatur berdasarkan nama argumen. Saat panggilan ke Invoke selesai, setiap argumen output dikembalikan dalam dictionary outputs, dengan kunci 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($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");

Jika alur kerja berasal dari ActivityWithResult, seperti CodeActivity<TResult> atau Activity<TResult>, dan ada argumen keluar selain argumen keluar Result yang terdefinisi dengan baik, overload Invoke yang non-generik harus digunakan untuk mendapatkan argumen tambahan. Untuk melakukan ini, definisi alur kerja yang diteruskan Invoke harus berjenis Activity. Dalam contoh Divide ini aktivitas berasal dari CodeActivity<int>, tetapi dinyatakan sebagai Activity sehingga kelebihan beban Invoke non-generik digunakan yang mengembalikan kamus argumen alih-alih 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($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");

Menggunakan WorkflowApplication

WorkflowApplication menyediakan serangkaian fitur yang kaya untuk manajemen instans alur kerja. WorkflowApplication bertindak sebagai proksi aman utas untuk WorkflowInstance yang sebenarnya, yang merangkum runtime, serta menyediakan metode untuk membuat dan memuat instans alur kerja, menjeda dan melanjutkan, mengakhiri, serta pemberitahuan peristiwa siklus hidup. Untuk menjalankan alur kerja menggunakan WorkflowApplication Anda membuat WorkflowApplication, berlangganan peristiwa siklus hidup yang diinginkan, mulai alur kerja, lalu tunggu hingga selesai. Dalam contoh ini, definisi alur kerja yang terdiri dari WriteLine aktivitas dibuat dan WorkflowApplication dibuat menggunakan definisi alur kerja yang ditentukan. Completed ditangani sehingga host diberi tahu ketika alur kerja selesai, alur kerja dimulai dengan panggilan ke Run, lalu host menunggu alur kerja selesai. Ketika alur kerja selesai, AutoResetEvent diatur dan aplikasi host dapat melanjutkan eksekusi, seperti yang ditunjukkan dalam 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();

Kejadian Siklus Hidup WorkflowApplication

Selain Completed, penulis host dapat diberi tahu ketika alur kerja dibongkar (Unloaded), dibatalkan (Aborted), menjadi tidak aktif (Idle dan PersistableIdle), atau ketika pengecualian yang tidak tertangani terjadi (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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

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

wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine($"Workflow {e.InstanceId} Aborted.");
    Console.WriteLine($"Exception: {e.Reason.GetType().FullName}\n{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 {e.InstanceId} Idle.");
};

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 {e.InstanceId} Unloaded.");
};

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

    Console.WriteLine($"ExceptionSource: {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 Parameter Input Alur Kerja

Data dapat diteruskan ke alur kerja pada saat dimulai dengan menggunakan daftar 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 WriteLine aktivitas dipanggil dan argumennya Text 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

Ketika alur kerja selesai, argumen keluaran apa pun dapat diambil dalam Completed handler dengan mengakses WorkflowApplicationCompletedEventArgs.Outputs kamus. Contoh berikut menghosting alur kerja menggunakan WorkflowApplication. WorkflowApplication Instans dibangun menggunakan definisi alur kerja yang terdiri dari satu DiceRoll aktivitas. Aktivitas DiceRoll memiliki dua argumen output yang mewakili hasil operasi putar dadu. Ketika alur kerja selesai, output diambil di Completed handler.

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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

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

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

Nota

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

Meneruskan Data ke Alur Kerja yang Sedang Berjalan Menggunakan Penanda

Penanda adalah mekanisme di mana aktivitas dapat menunggu secara pasif untuk dilanjutkan dan merupakan mekanisme untuk memasukkan data ke dalam instans alur kerja yang sedang berjalan. Jika suatu aktivitas menunggu data, aktivitas tersebut dapat membuat Bookmark dan mendaftarkan metode callback agar dipanggil ketika Bookmark dilanjutkan, seperti yang ditunjukkan dalam 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 callback, lalu menunggu Bookmark untuk dilanjutkan. Ketika dilanjutkan, aktivitas ReadLine menetapkan data yang dioper ke Bookmark sebagai argumen Result. Dalam contoh ini, alur kerja dibuat yang menggunakan ReadLine aktivitas 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: {result}");

Ketika aktivitas ReadLine dijalankan, aktivitas tersebut membuat Bookmark yang bernama UserName dan kemudian menunggu bookmark dilanjutkan. Host mengumpulkan data yang diinginkan dan kemudian melanjutkan Bookmark. Alur kerja dilanjutkan, menampilkan nama, lalu selesai.

Aplikasi host dapat memeriksa alur kerja untuk menentukan apakah ada marka buku aktif. Ini dapat melakukan ini dengan memanggil metode GetBookmarks dari suatu WorkflowApplication instance, atau dengan memeriksa WorkflowApplicationIdleEventArgs di dalam Idle handler.

Contoh kode berikut seperti contoh sebelumnya kecuali bahwa bookmark aktif dijumlahkan sebelum bookmark dilanjutkan. Alur kerja dimulai, dan setelah Bookmark dibuat dan alur kerja menjadi tidak aktif, GetBookmarks dipanggil. Ketika alur kerja selesai, output berikut ditampilkan ke konsol.

Siapa namamu?
BookmarkName: UserName - OwnerDisplayName: ReadLineSteveHello, 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: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
}

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

Contoh kode berikut memeriksa WorkflowApplicationIdleEventArgs yang diteruskan ke handler Idle dari instansi WorkflowApplication. Dalam contoh ini, alur kerja menjadi tidak aktif dan memiliki satu Bookmark yang dinamai EnterGuess, dimiliki oleh aktivitas bernama ReadInt. Contoh kode ini didasarkan pada Cara: Menjalankan Alur Kerja, yang merupakan bagian dari Tutorial Memulai. Idle Jika handler dalam langkah tersebut dimodifikasi untuk berisi kode dari contoh ini, output berikut ditampilkan.

BookmarkName: EnterGuess - OwnerDisplayName: ReadInt

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

    idleEvent.Set();
};

Ringkasan

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