Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Kompensasi di Windows Workflow Foundation (WF) adalah mekanisme di mana pekerjaan yang diselesaikan sebelumnya dapat dibatalkan atau dikompensasi (mengikuti logika yang ditentukan oleh aplikasi) ketika kegagalan berikutnya terjadi. Bagian ini menjelaskan cara menggunakan kompensasi dalam alur kerja.
Kompensasi vs. Transaksi
Transaksi memungkinkan Anda menggabungkan beberapa operasi ke dalam satu unit kerja. Menggunakan transaksi memberi aplikasi Anda kemampuan untuk membatalkan (menggulung balik) semua perubahan yang dijalankan dari dalam transaksi jika ada kesalahan yang terjadi selama bagian mana pun dari proses transaksi. Namun, menggunakan transaksi mungkin tidak sesuai jika pekerjaan berjalan lama. Misalnya, aplikasi perencanaan perjalanan diimplementasikan sebagai alur kerja. Langkah-langkah alur kerja dapat terdiri dari pemesanan penerbangan, menunggu persetujuan manajer, lalu membayar penerbangan. Proses ini bisa memakan waktu berkali-hari dan tidak praktis bagi langkah-langkah pemesanan dan membayar penerbangan untuk berpartisipasi dalam transaksi yang sama. Dalam skenario seperti ini, kompensasi dapat digunakan untuk membatalkan langkah pemesanan alur kerja jika ada kegagalan nanti dalam pemrosesan.
Nota
Topik ini mencakup kompensasi dalam alur kerja. Untuk informasi selengkapnya tentang transaksi dalam alur kerja, lihat Transaksi dan TransactionScope. Untuk informasi selengkapnya tentang transaksi, lihat System.Transactions dan System.Transactions.Transaction.
Menggunakan Aktivitas yang Dapat Dikompensasi
CompensableActivity adalah aktivitas kompensasi inti dalam WF. Setiap aktivitas yang melibatkan pekerjaan yang mungkin memerlukan kompensasi ditempatkan ke dalam Body dari CompensableActivity. Dalam contoh ini, langkah reservasi pembelian penerbangan ditempatkan ke dalam Body dari CompensableActivity dan pembatalan reservasi ditempatkan ke dalam CompensationHandler. Segera setelah CompensableActivity dalam alur kerja, ada dua aktivitas yang menunggu persetujuan manajer dan kemudian menyelesaikan tahap pembelian penerbangan. Jika kondisi kesalahan menyebabkan alur kerja dibatalkan setelah CompensableActivity berhasil diselesaikan, maka aktivitas di dalam CompensationHandler handler akan dijadwalkan, dan penerbangan akan dibatalkan.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new ManagerApproval(),
new PurchaseFlight()
}
};
Contoh berikut adalah alur kerja di XAML.
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
Saat alur kerja dipanggil, output berikut ditampilkan ke konsol.
ReserveFlight: Tiket dipesan.ManagerApproval: Persetujuan manajer diterima.PurchaseFlight: Tiket dibeli.Alur kerja berhasil diselesaikan dengan status: Ditutup.
Nota
Sampel aktivitas dalam topik ini seperti ReserveFlight menampilkan nama dan tujuannya ke konsol untuk membantu mengilustrasikan urutan di mana aktivitas dijalankan saat kompensasi terjadi.
Kompensasi Alur Kerja Default
Secara default, jika alur kerja dibatalkan, logika kompensasi dijalankan untuk aktivitas yang dapat dikompensasi yang berhasil sepenuhnya dan belum dikonfirmasi atau dikompensasi.
Nota
CompensableActivity Ketika dikonfirmasi, kompensasi untuk aktivitas tidak dapat lagi dimintakan. Proses konfirmasi dijelaskan nanti di bagian ini.
Dalam contoh ini, terjadi pengecualian setelah penerbangan dipesan tetapi sebelum langkah persetujuan manajer.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
};
Contoh ini adalah alur kerja di XAML.
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:SimulatedErrorCondition />
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
AutoResetEvent syncEvent = new AutoResetEvent(false);
WorkflowApplication wfApp = new WorkflowApplication(wf);
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
if (e.TerminationException != null)
{
Console.WriteLine($"""
Workflow terminated with exception:
{e.TerminationException.GetType().FullName}: {e.TerminationException.Message}
""");
}
else
{
Console.WriteLine($"Workflow completed successfully with status: {e.CompletionState}.");
}
syncEvent.Set();
};
wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Console.WriteLine($"""
Workflow Unhandled Exception:
{e.UnhandledException.GetType().FullName}: {e.UnhandledException.Message}
""");
return UnhandledExceptionAction.Cancel;
};
wfApp.Run();
syncEvent.WaitOne();
Ketika alur kerja dipanggil, pengecualian kondisi kesalahan yang disimulasikan ditangani oleh aplikasi host di OnUnhandledException, alur kerja dibatalkan, dan logika kompensasi dipanggil.
ReserveFlight: Tiket dipesan.SimulatedErrorCondition: Melemparkan ApplicationException.Pengecualian Alur Kerja Tidak Tertangani:System.ApplicationException: Kondisi kesalahan simulasi dalam alur kerja.CancelFlight: Tiket dibatalkan.Status penyelesaian alur kerja: Dibatalkan.
Pembatalan dan Kegiatan yang Dapat Diberikan Kompensasi
Jika aktivitas di dalam BodyCompensableActivity belum selesai dan aktivitas tersebut dibatalkan, aktivitas dalam CancellationHandler akan dijalankan.
Nota
CancellationHandler hanya dipanggil jika aktivitas di Body dalam CompensableActivity belum selesai dan aktivitas dibatalkan. CompensationHandler akan dijalankan hanya jika aktivitas di Body dari CompensableActivity telah berhasil diselesaikan dan kompensasi kemudian diaktifkan pada aktivitas.
Memberikan kesempatan kepada penulis alur kerja untuk menyediakan logika pembatalan yang sesuai. Dalam contoh berikut, pengecualian dilemparkan selama eksekusi Body, dan kemudian CancellationHandler dipanggil.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new Sequence
{
Activities =
{
new ChargeCreditCard(),
new SimulatedErrorCondition(),
new ReserveFlight()
}
},
CompensationHandler = new CancelFlight(),
CancellationHandler = new CancelCreditCard()
},
new ManagerApproval(),
new PurchaseFlight()
}
};
Contoh ini adalah alur kerja di XAML
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</CompensableActivity.Result>
<Sequence>
<c:ChargeCreditCard />
<c:SimulatedErrorCondition />
<c:ReserveFlight />
</Sequence>
<CompensableActivity.CancellationHandler>
<c:CancelCreditCard />
</CompensableActivity.CancellationHandler>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
</CompensableActivity>
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
Ketika alur kerja dipanggil, pengecualian kondisi kesalahan yang disimulasikan ditangani oleh aplikasi host di OnUnhandledException, kemudian alur kerja dibatalkan, dan logika pembatalan dari CompensableActivity dipanggil. Dalam contoh ini, logika kompensasi dan logika pembatalan memiliki tujuan yang berbeda. Jika Body berhasil diselesaikan, hal ini berarti kartu kredit ditagih dan penerbangan dipesan, maka kompensasi seharusnya membatalkan kedua tindakan tersebut. (Dalam contoh ini, membatalkan penerbangan secara otomatis membatalkan biaya kartu kredit.) Namun, jika CompensableActivity dibatalkan, ini berarti Body tidak selesai dan oleh karena itu logika CancellationHandler perlu menentukan cara terbaik menangani pembatalan. Dalam contoh ini, CancellationHandler membatalkan biaya kartu kredit, tetapi karena ReserveFlight merupakan aktivitas terakhir di Body, itu tidak mencoba membatalkan penerbangan. Karena ReserveFlight merupakan aktivitas terakhir dalam Body, jika berhasil diselesaikan maka Body akan selesai dan tidak ada pembatalan yang mungkin.
ChargeCreditCard: Membebankan biaya ke kartu kredit untuk penerbangan.Kondisi Kesalahan Tersimulasi: Melempar ApplicationException.Pengecualian Alur Kerja yang Tidak Tertangani:System.ApplicationException: Kondisi kesalahan tersimulasi dalam alur kerja.CancelCreditCard: Membatalkan tagihan kartu kredit.Alur kerja berhasil diselesaikan dengan status: Dibatalkan. Untuk informasi lebih lanjut tentang pembatalan, lihat bagian Pembatalan.
Kompensasi Eksplisit Menggunakan Aktivitas Kompensasi
Di bagian sebelumnya, kompensasi implisit tercakup. Kompensasi implisit dapat sesuai untuk skenario sederhana, tetapi jika kontrol yang lebih eksplisit diperlukan atas penjadwalan penanganan kompensasi maka Compensate aktivitas dapat digunakan. Untuk memulai proses kompensasi dengan aktivitas Compensate, digunakan CompensationToken dari CompensableActivity yang diinginkan untuk kompensasi. Aktivitas Compensate ini dapat digunakan untuk memulai kompensasi pada setiap CompensableActivity yang sudah selesai tetapi belum dikonfirmasi atau dikompensasi. Misalnya, aktivitas Compensate dapat digunakan di aktivitas Catches bagian TryCatch, atau kapan saja setelah CompensableActivity telah selesai. Dalam contoh ini, aktivitas Compensate digunakan di bagian aktivitas Catches pada TryCatch untuk membalikkan tindakan CompensableActivity.
Variable<CompensationToken> token1 = new Variable<CompensationToken>
{
Name = "token1",
};
Activity wf = new TryCatch()
{
Variables =
{
token1
},
Try = new Sequence
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight(),
ConfirmationHandler = new ConfirmFlight(),
Result = token1
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
},
Catches =
{
new Catch<ApplicationException>()
{
Action = new ActivityAction<ApplicationException>()
{
Handler = new Compensate()
{
Target = token1
}
}
}
}
};
Contoh ini adalah alur kerja di XAML.
<TryCatch
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TryCatch.Variables>
<Variable
x:TypeArguments="CompensationToken"
x:Name="__ReferenceID0"
Name="token1" />
</TryCatch.Variables>
<TryCatch.Try>
<Sequence>
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken">
<VariableReference
x:TypeArguments="CompensationToken"
Variable="{x:Reference __ReferenceID0}">
<VariableReference.Result>
<OutArgument
x:TypeArguments="Location(CompensationToken)" />
</VariableReference.Result>
</VariableReference>
</OutArgument>
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<CompensableActivity.ConfirmationHandler>
<c:ConfirmFlight />
</CompensableActivity.ConfirmationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:SimulatedErrorCondition />
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
</TryCatch.Try>
<TryCatch.Catches>
<Catch
x:TypeArguments="s:ApplicationException">
<ActivityAction
x:TypeArguments="s:ApplicationException">
<Compensate>
<Compensate.Target>
<InArgument
x:TypeArguments="CompensationToken">
<VariableValue
x:TypeArguments="CompensationToken"
Variable="{x:Reference __ReferenceID0}">
<VariableValue.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</VariableValue.Result>
</VariableValue>
</InArgument>
</Compensate.Target>
</Compensate>
</ActivityAction>
</Catch>
</TryCatch.Catches>
</TryCatch>
Saat alur kerja dipanggil, output berikut ditampilkan ke konsol.
ReserveFlight: Tiket dipesan.SimulatedErrorCondition: Melempar ApplicationException.CancelFlight: Tiket dibatalkan.Alur kerja berhasil diselesaikan dengan status: Ditutup.
Mengonfirmasi Kompensasi
Secara default, aktivitas yang dapat dikompensasi dapat dikompensasi kapan saja setelah selesai. Dalam beberapa skenario, ini mungkin tidak sesuai. Dalam contoh sebelumnya, kompensasi untuk memesan tiket adalah dengan membatalkan reservasi. Namun, setelah penerbangan selesai, langkah kompensasi ini tidak berlaku lagi. Mengonfirmasi aktivitas yang dapat dikompensasi memanggil aktivitas yang ditentukan oleh ConfirmationHandler. Salah satu kemungkinan penggunaan dari ini adalah untuk memungkinkan terlepasnya sumber daya apa pun yang diperlukan untuk melakukan kompensasi. Setelah aktivitas yang dapat dikompensasi dikonfirmasi, tidak mungkin lagi untuk dikompensasi, dan jika ini dicoba, InvalidOperationException pengecualian akan dilempar. Ketika alur kerja berhasil diselesaikan, semua aktivitas kompensasi yang tidak dikonfirmasi dan tidak dikompensasi yang berhasil diselesaikan dikonfirmasi dalam urutan penyelesaian terbalik. Dalam contoh ini penerbangan dipesan, dibeli, dan selesai, lalu aktivitas yang dapat dikompensasi dikonfirmasi. Untuk mengonfirmasi CompensableActivity, gunakan aktivitas Confirm dan tentukan CompensationToken dari CompensableActivity untuk mengonfirmasi.
Variable<CompensationToken> token1 = new Variable<CompensationToken>
{
Name = "token1",
};
Activity wf = new Sequence()
{
Variables =
{
token1
},
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight(),
ConfirmationHandler = new ConfirmFlight(),
Result = token1
},
new ManagerApproval(),
new PurchaseFlight(),
new TakeFlight(),
new Confirm()
{
Target = token1
}
}
};
Contoh ini adalah alur kerja di XAML.
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Sequence.Variables>
<x:Reference>__ReferenceID0</x:Reference>
</Sequence.Variables>
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken">
<VariableReference
x:TypeArguments="CompensationToken">
<VariableReference.Result>
<OutArgument
x:TypeArguments="Location(CompensationToken)" />
</VariableReference.Result>
<VariableReference.Variable>
<Variable
x:TypeArguments="CompensationToken"
x:Name="__ReferenceID0"
Name="token1" />
</VariableReference.Variable>
</VariableReference>
</OutArgument>
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<CompensableActivity.ConfirmationHandler>
<c:ConfirmFlight />
</CompensableActivity.ConfirmationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:ManagerApproval />
<c:PurchaseFlight />
<c:TakeFlight />
<Confirm>
<Confirm.Target>
<InArgument
x:TypeArguments="CompensationToken">
<VariableValue
x:TypeArguments="CompensationToken"
Variable="{x:Reference __ReferenceID0}">
<VariableValue.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</VariableValue.Result>
</VariableValue>
</InArgument>
</Confirm.Target>
</Confirm>
</Sequence>
Saat alur kerja dipanggil, output berikut ditampilkan ke konsol.
ReserveFlight: Tiket dipesan.ManagerApproval: Persetujuan manajer diterima.PurchaseFlight: Tiket dibeli.TakeFlight: Penerbangan selesai.ConfirmFlight: Penerbangan telah diambil, tidak ada kompensasi yang mungkin.Alur kerja berhasil diselesaikan dengan status: Ditutup.
Aktivitas Kompensasi Berjenjang
CompensableActivity dapat ditempatkan di bagian Body dari CompensableActivity lain. CompensableActivity tidak boleh ditempatkan ke dalam handler dari CompensableActivity yang lain. Induk CompensableActivity bertanggung jawab untuk memastikan bahwa ketika dibatalkan, dikonfirmasi, atau dikompensasi, semua aktivitas anak yang dapat dikompensasi yang telah berhasil diselesaikan dan yang belum dikonfirmasi atau dikompensasi harus dikonfirmasi atau dikompensasi sebelum induk menyelesaikan pembatalan, konfirmasi, atau kompensasi. Jika ini tidak dimodelkan secara eksplisit, induk CompensableActivity akan secara implisit mengkompensasi aktivitas yang dapat dikompensasi anak jika induk menerima sinyal pembatalan atau kompensasi. Jika induk menerima sinyal konfirmasi, induk akan secara implisit mengonfirmasi aktivitas yang dapat dikompensasi anak. Jika logika untuk menangani pembatalan, konfirmasi, atau kompensasi secara eksplisit dimodelkan dalam penangan entitas induk CompensableActivity, setiap anak yang tidak ditangani secara eksplisit akan dikonfirmasi secara implisit.