Memakai Persistence Di Windows Workflow


Pada artikel Memakai Bookmark Di Windows Workflow (WF), saya membuat sebuah aplikasi yang memakai Windows Workflow (WF). Bila aplikasi tersebut ditutup, seluruh state workflow akan hilang, walaupun workflow belum mencapai tahap terakhir. Benarkah demikian? Untuk membuktikannya, saya akan membuat sebuah Windows Form dengan men-klik kanan nama proyek dan memilih menu Add, Windows Form…. Saya mengisi nama form berupa FormKendali. Saya kemudian menambahkan tiga Button masing-masing untuk mewakili resume untuk bookmark MulaiDimasak, SelesaiDimasak dan PelangganMembayar. Saya juga menambahkan sebuah Timer ke dalam form tersebut untuk men-enabled Button sesuai dengan bookmark yang sedang aktif. Saya kemudian menambahkan event handler dan constructor pada form seperti yang terlihat pada kode program berikut ini:

using System;
using System.Windows.Forms;
using System.Activities;
using System.Activities.Hosting;

namespace LatihanBookmark
{
    public partial class FormKendali : Form
    {
        private WorkflowApplication workflowApp;        

        public FormKendali(WorkflowApplication app)
        {
            InitializeComponent();
            this.workflowApp = app;
            timer1.Enabled = true;
            button1.Enabled = false;
            button2.Enabled = false;
            button3.Enabled = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {            
            workflowApp.ResumeBookmark("MulaiDimasak", "Master Chef");
            ((Button)sender).Enabled = false;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            workflowApp.ResumeBookmark("SelesaiDimasak", "Master Chef");
            ((Button)sender).Enabled = false;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            workflowApp.ResumeBookmark("PelangganMembayar", "Cute Girl");
            timer1.Enabled = false;                        
            ((Button)sender).Enabled = false;            
        }

        private void timer1_Tick(object sender, EventArgs e)
        {                        
            foreach (BookmarkInfo bookmark in workflowApp.GetBookmarks())
            {
                switch (bookmark.BookmarkName)
                {
                    case "MulaiDimasak":                        
                        button1.Enabled = true;
                        break;
                    case "SelesaiDimasak":
                        button2.Enabled = true;                                                
                        break;
                    case "PelangganMembayar":
                        button3.Enabled = true;                        
                        break;
                }
            }
        }

    }
}

Berikutnya, saya mengubah kode program Program.cs agar ia menampilkan form di atas, seperti yang terlihat pada kode program berikut ini:

using System;
using System.Activities;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace LatihanBookmark
{

    class Program
    {
        static void Main(string[] args)
        {
            Order contohOrder = new Order
            {
                Id = "ORDER1",
                MenuMakanan = "Tasty Food",
                NamaPelanggan = "A Snake"
            };

            var argumen = new Dictionary()
            {
                { "order", contohOrder }
            };


            AutoResetEvent syncEvent = new AutoResetEvent(false);
            WorkflowApplication app = new WorkflowApplication(new ProsesPemesanan(), argumen);
            app.Completed = (e) => syncEvent.Set();
            app.Run();

            Application.EnableVisualStyles();
            Application.Run(new FormKendali(app));

            // Menunggu hingga workflow selesai dikerjakan
            syncEvent.WaitOne();

            Console.ReadLine();
        }
    }
}

Bila saya menjalankan aplikasi, saya akan menjumpai hasil seperti pada gambar berikut ini:

Menunggu bookmark di-resume

Menunggu bookmark di-resume

Menunggu bookmark di-resume

Menunggu bookmark di-resume

Menunggu bookmark di-resume

Menunggu bookmark di-resume

Bila saya menutup aplikasi pada saat workflow baru sampai pada tahap Mulai Dimasak, maka saat saya menjalankan ulang aplikasi, workflow akan kembali mulai dari awal. Bukankah proses pada workflow adalah proses yang berlangsung pada jangka waktu relatif lama? Walaupun aplikasi ditutup atau server dimatikan, sebaiknya workflow engine harus bisa mengingat state workflow sebelumnya. Salah satu cara yang mungkin adalah dengan menyimpan state workflow pada database. Untuk menyimpan workflow pada database SQL Server, saya dapat menggunakan class SqlWorkflowInstanceStore.

Untuk menyimpan workflow pada database, saya perlu membuat tabel yang dibutuhkan. .NET Framework 4 sudah menyediakan script SQL yang bisa langsung saya kerjakan di SQL Server. Script SQL tersebut terletak di C:\Windows\Microsoft.NET\Framework\v4.0\SQL\en. Saya perlu mengerjakan script SqlWorkflowInstanceStoreSchema.sql terlebih dahulu baru diikuti dengan SqlWorkflowInstanceStoreLogic.sql. Setelah mengerjakan kedua script tersebut, saya akan menemukan tabel dan stored procedure baru yang terlihat seperti pada gambar berikut ini:

Tabel untuk menyimpan workflow

Tabel untuk menyimpan workflow

Berikutnya, saya perlu menambahkan referensi ke System.Activities.DurableInstancing dan System.Runtime.DurableInstancing dengan men-klik kanan nama proyek dan memilih menu Add Reference….

Setelah itu, saya mengubah kode program di Program.cs menjadi seperti berikut ini:

using System;
using System.Activities;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Activities.DurableInstancing;
using System.Runtime.DurableInstancing;
using System.Data.SqlClient;

namespace LatihanBookmark
{

    class Program
    {
        private static readonly string CONNECTION_STRING = "Server=.\\SQLEXPRESS;Initial Catalog=LatihanWorkflow;Integrated Security=SSPI";

        public static Guid? GetSavedInstanceId()
        {            
            using (SqlConnection cn = new SqlConnection(CONNECTION_STRING))
            {
                cn.Open();
                SqlCommand cmd = new SqlCommand("SELECT Id FROM [System.Activities.DurableInstancing].[InstancesTable]", cn);
                return (Guid?) cmd.ExecuteScalar();
            }            
        }

        static void Main(string[] args)
        {
            WorkflowApplication app;
            ProsesPemesanan workflow = new ProsesPemesanan();
            InstanceStore store = new SqlWorkflowInstanceStore(CONNECTION_STRING);
            InstanceHandle handle = store.CreateInstanceHandle();
            CreateWorkflowOwnerCommand createOwnerCmd = new CreateWorkflowOwnerCommand();
            var view = store.Execute(handle, createOwnerCmd, TimeSpan.FromSeconds(30));
            store.DefaultInstanceOwner = view.InstanceOwner;

            Guid? instanceId = GetSavedInstanceId();

            if (instanceId != null)
            {
                //
                // Lanjutkan workflow yang sudah ada
                //

                app = new WorkflowApplication(workflow);
                app.InstanceStore = store;
                app.Load(instanceId.Value);                
            }
            else
            {
                //
                // Membuat instance workflow baru
                //

                Order contohOrder = new Order
                {
                    Id = "ORDER1",
                    MenuMakanan = "Tasty Food",
                    NamaPelanggan = "A Snake"
                };

                var argumen = new Dictionary<string, object>()
                {
                    { "order", contohOrder }
                };

                app = new WorkflowApplication(workflow, argumen);
                app.InstanceStore = store;
                app.Run();
            }

            Application.EnableVisualStyles();
            Application.Run(new FormKendali(app));

            DeleteWorkflowOwnerCommand deleteOwnerCmd = new DeleteWorkflowOwnerCommand();
            store.Execute(handle, deleteOwnerCmd, TimeSpan.FromSeconds(30));

            Console.ReadLine();
        }
    }
}

Setiap instance workflow selalu memiliki id yang unik. Pada kode program di atas, saya memeriksa nilai id dari instance pertama yang disimpan di tabel InstancesTable. Bila ada, maka saya akan menggunakannya untuk melanjutkan workflow yang sudah tersimpan tersebut dengan memanggil method Load(). Bila tabel InstancesTable masih kosong, saya akan membuat instance workflow baru.

Selain itu, saya juga menambahkan event handler berikut pada file FormKendali.cs yang isinya berupa:

...
private void FormKendali_FormClosed(object sender, FormClosedEventArgs e)
{            
   workflowApp.Persist();
}        
...

Kode program di atas akan menyimpan workflow ke dalam database setiap kali saya menutup form. Dengan demikian, bila saya menutup aplikasi pada saat sedang berada di activity tertentu dari workflow, maka saat saya membuka aplikasi, workflow akan kembali ke activity terakhir pada saat saya menutup form. Bukan hanya itu, instance workflow tersebut juga akan tetap mengingat nilai argumen order yang telah diberikan padanya sebelumnya.

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: