Kompensasi
Kompensasi dalam Windows Workflow Foundation (WF) adalah mekanisme di mana pekerjaan yang diselesaikan sebelumnya dapat dibatalkan atau dikompensasi (dengan mengikuti logika yang ditentukan oleh aplikasi) jika kegagalan berikutnya terjadi. Bagian ini menjelaskan cara menggunakan kompensasi dalam alur kerja.
Kompensasi vs. Transaksi
Transaksi memungkinkan Anda menggabungkan beberapa operasi menjadi satu unit kerja. Menggunakan transaksi memberi aplikasi Anda kemampuan untuk membatalkan (gulung balik) semua perubahan yang dilakukan dari dalam transaksi jika ada kesalahan yang terjadi selama bagian proses transaksi mana pun. Namun, menggunakan transaksi mungkin tidak sesuai jika pekerjaan berjalan lama. Misalnya, aplikasi perencanaan perjalanan diimplementasikan sebagai alur kerja. Langkah-langkah alur kerja bisa terdiri dari pemesanan penerbangan, menunggu persetujuan manajer, dan kemudian membayar penerbangan. Proses ini bisa memakan waktu berhari-hari dan tidaklah praktis menyertakan langkah-langkah pemesanan dan pembayaran penerbangan dalam transaksi yang sama. Dalam skenario seperti ini, kompensasi dapat digunakan untuk membatalkan langkah pemesanan alur kerja jika ada kegagalan di kemudian hari dalam pemrosesan.
Catatan
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 CompensableActivity
CompensableActivity adalah aktivitas kompensasi inti dalam WF. Setiap aktivitas yang melakukan pekerjaan yang mungkin perlu dikompensasi ditempatkan ke dalam BodyCompensableActivity. Dalam contoh ini, langkah reservasi pembelian penerbangan ditempatkan dalam BodyCompensableActivity dan pembatalan reservasi ditempatkan dalam CompensationHandler. Langsung mengikuti CompensableActivity dalam alur kerja adalah dua aktivitas yang menunggu persetujuan manajer dan kemudian menyelesaikan langkah pembelian penerbangan. Jika kondisi kesalahan menyebabkan alur kerja dibatalkan setelah CompensableActivity berhasil diselesaikan, aktivitas dalam penangan CompensationHandler dijadwalkan dan penerbangan 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, keluaran berikut ditampilkan ke konsol.
ReserveFlight: Tiket dipesan.ManagerApproval: Persetujuan manajer diterima.PurchaseFlight: Tiket dibeli.Alur kerja berhasil diselesaikan dengan status: Ditutup.
Catatan
Contoh aktivitas dalam topik ini seperti ReserveFlight
menampilkan nama dan tujuannya ke konsol untuk membantu mengilustrasikan urutan aktivitas yang dijalankan saat kompensasi terjadi.
Kompensasi Alur Kerja Default
Secara default, jika alur kerja dibatalkan, logika kompensasi dijalankan untuk aktivitas yang dapat dikompensasi yang telah berhasil sepenuhnya dan belum dikonfirmasi atau dikompensasi.
Catatan
Saat CompensableActivitydikonfirmasi, kompensasi aktivitas tidak bisa dipanggil lagi. Proses konfirmasi akan dijelaskan nanti dalam bagian ini.
Dalam contoh ini, pengecualian muncul 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 dalam 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:\n{0}: {1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else
{
Console.WriteLine("Workflow completed successfully with status: {0}.",
e.CompletionState);
}
syncEvent.Set();
};
wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Console.WriteLine("Workflow Unhandled Exception:\n{0}: {1}",
e.UnhandledException.GetType().FullName,
e.UnhandledException.Message);
return UnhandledExceptionAction.Cancel;
};
wfApp.Run();
syncEvent.WaitOne();
Saat alur kerja dipanggil, pengecualian kondisi kesalahan simulasi ditangani oleh aplikasi host di OnUnhandledException, alur kerja dibatalkan, dan logika kompensasi dipanggil.
ReserveFlight: Tiket dipesan.SimulatedErrorCondition: Menampilkan ApplicationException.Pengecualian Alur Kerja Tidak Ditangani:System.ApplicationException: Kondisi kesalahan simulasi dalam alur kerja.CancelFlight: Tiket dibatalkan.Alur kerja berhasil diselesaikan dengan status: Dibatalkan.
Pembatalan dan CompensableActivity
Jika aktivitas dalam BodyCompensableActivity belum selesai dan aktivitas dibatalkan, aktivitas dalam CancellationHandler akan dijalankan.
Catatan
CancellationHandler hanya dipanggil jika aktivitas dalam BodyCompensableActivity belum selesai dan aktivitas dibatalkan. CompensationHandler hanya dijalankan jika aktivitas dalam BodyCompensableActivity telah berhasil diselesaikan dan kompensasi kemudian dipanggil pada aktivitas.
CancellationHandler memberi penulis alur kerja kesempatan untuk memberikan logika pembatalan yang sesuai. Dalam contoh berikut, pengecualian muncul 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 dalam 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>
Saat alur kerja dipanggil, pengecualian kondisi kesalahan simulasi ditangani oleh aplikasi host di OnUnhandledException, alur kerja dibatalkan, dan logika pembatalan CompensableActivity dipanggil. Dalam contoh ini, logika kompensasi dan logika pembatalan memiliki tujuan yang berbeda. Jika Body berhasil diselesaikan, ini berarti bahwa kartu kredit ditagih dan penerbangan dipesan, sehingga kompensasi harus membatalkan kedua langkah. (Dalam contoh ini, membatalkan penerbangan akan secara otomatis membatalkan biaya kartu kredit.) Namun, jika CompensableActivity dibatalkan, ini berarti Body tidak selesai dan sehingga logika CancellationHandler harus dapat menentukan cara terbaik menangani pembatalan. Dalam contoh ini, CancellationHandler membatalkan biaya kartu kredit, tetapi karena ReserveFlight
adalah aktivitas terakhir dalam Body, pembatalan penerbangan tidak diupayakan. Karena ReserveFlight
adalah aktivitas terakhir dalam Body, jika berhasil diselesaikan, Body akan selesai dan tidak ada pembatalan yang dapat dilakukan.
ChargeCreditCard: Menagih kartu kredit untuk penerbangan.SimulatedErrorCondition: Menampilkan ApplicationException.Pengecualian Alur Kerja Tidak Ditangani:System.ApplicationException: Kondisi kesalahan simulasi dalam alur kerja.CancelCreditCard: Membatalkan biaya kartu kredit.Alur kerja berhasil diselesaikan dengan status: Dibatalkan. Untuk informasi selengkapnya tentang pembatalan, lihat Pembatalan.
Kompensasi Eksplisit Menggunakan Aktivitas Kompensasi
Kompensasi implisit telah dibahas dalam bagian sebelumnya. Kompensasi implisit dapat sesuai untuk skenario sederhana, tetapi jika kontrol yang lebih eksplisit diperlukan untuk penjadwalan penanganan kompensasi, aktivitas Compensate dapat digunakan. Untuk memulai proses kompensasi dengan aktivitas Compensate, digunakan CompensationTokenCompensableActivity yang kompensasinya diinginkan. Aktivitas Compensate dapat digunakan untuk memulai kompensasi di semua CompensableActivity yang selesai yang belum dikonfirmasi atau dikompensasi. Misalnya, aktivitas Compensate dapat digunakan di bagian Catches aktivitas TryCatch, atau kapan pun setelah CompensableActivity selesai. Dalam contoh ini, aktivitas Compensate digunakan di bagian Catches aktivitas 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 dalam 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, keluaran berikut ditampilkan ke konsol.
ReserveFlight: Tiket dipesan.SimulatedErrorCondition: Menampilkan ApplicationException.CancelFlight: Tiket dibatalkan.Alur kerja berhasil diselesaikan dengan status: Ditutup.
Mengonfirmasi Kompensasi
Secara default, aktivitas yang dapat dikompensasi dapat dikompensasi kapan pun setelah aktivitas selesai. Dalam beberapa skenario, ini mungkin tidak sesuai. Dalam contoh sebelumnya, kompensasi untuk memesan tiket adalah membatalkan reservasi. Namun, setelah penerbangan selesai, langkah kompensasi ini tidak berlaku lagi. Mengonfirmasi aktivitas yang dapat dikompensasi akan memanggil aktivitas yang ditentukan oleh ConfirmationHandler. Salah satu kemungkinan penggunaannya adalah untuk memungkinkan sumber daya yang diperlukan melakukan kompensasi yang akan dirilis. Setelah aktivitas yang dapat dikompensasi dikonfirmasi, ini tidak dapat dikompensasi, dan jika diupayakan, pengecualian InvalidOperationException akan ditampilkan. Saat alur kerja berhasil diselesaikan, semua aktivitas yang dapat dikompensasi yang belum dikonfirmasi dan belum dikompensasi yang berhasil diselesaikan akan dikonfirmasi dalam urutan penyelesaian terbalik. Dalam contoh ini, penerbangan dipesan, dibeli, dan diselesaikan, dan kemudian aktivitas yang dapat dikompensasi dikonfirmasi. Untuk mengonfirmasi CompensableActivity, gunakan aktivitas Confirm dan tentukan CompensationTokenCompensableActivity 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 dalam 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, keluaran berikut ditampilkan ke konsol.
ReserveFlight: Tiket dipesan.ManagerApproval: Persetujuan manajer diterima.PurchaseFlight: Tiket dibeli.TakeFlight: Penerbangan selesai.ConfirmFlight: Penerbangan telah diambil, tidak mungkin ada kompensasi.Alur kerja berhasil diselesaikan dengan status: Ditutup.
Aktivitas Kompensasi Bertingkat
CompensableActivity dapat ditempatkan dalam bagian Body dari CompensableActivity lainnya. CompensableActivity tidak boleh ditempatkan dalam penangan CompensableActivity lainnya. CompensableActivity induk bertanggung jawab untuk memastikan bahwa jika dibatalkan, dikonfirmasi, atau dikompensasi, semua aktivitas turunan yang dapat dikompensasi yang telah berhasil diselesaikan dan belum dikonfirmasi atau dikompensasi harus dikonfirmasi atau dikompensasi sebelum aktivitas induk menyelesaikan pembatalan, konfirmasi, atau kompensasi. Jika ini tidak dimodelkan secara eksplisit, CompensableActivity induk akan secara implisit mengompensasi aktivitas turunan yang dapat dikompensasi jika aktivitas induk menerima sinyal pembatalan atau kompensasi. Jika menerima sinyal konfirmasi, aktivitas induk akan secara implisit mengonfirmasi aktivitas turunan yang dapat dikompensasi. Jika logika untuk menangani pembatalan, konfirmasi, atau kompensasi secara eksplisit dimodelkan dalam penangan CompensableActivity induk, setiap aktivitas turunan yang tidak ditangani secara eksplisit akan dikonfirmasi secara implisit.