Bagikan melalui


Penulisan Alur Kerja, Aktivitas, dan Ekspresi Menggunakan Kode Imperatif

Definisi alur kerja adalah pohon objek aktivitas yang dikonfigurasi. Pohon aktivitas ini dapat didefinisikan dengan banyak cara, termasuk dengan mengedit XAML tangan atau dengan menggunakan Desainer Alur Kerja untuk menghasilkan XAML. Namun, penggunaan XAML bukanlah persyaratan. Definisi alur kerja juga dapat dibuat secara terprogram. Topik ini memberikan gambaran umum tentang membuat definisi, aktivitas, dan ekspresi alur kerja dengan menggunakan kode. Untuk contoh bekerja dengan alur kerja XAML menggunakan kode, lihat Serialisasi Alur Kerja dan Aktivitas ke dan dari XAML.

Membuat Definisi Alur Kerja

Definisi alur kerja dapat dibuat dengan membuat instans jenis aktivitas dan mengonfigurasi properti objek aktivitas. Untuk aktivitas yang tidak berisi aktivitas anak, ini dapat dicapai menggunakan beberapa baris kode.

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

WorkflowInvoker.Invoke(wf);

Nota

Contoh dalam topik ini digunakan WorkflowInvoker untuk menjalankan alur kerja sampel. Untuk informasi selengkapnya tentang memanggil alur kerja, meneruskan argumen, dan berbagai pilihan hosting yang tersedia, lihat Menggunakan WorkflowInvoker dan WorkflowApplication.

Dalam contoh ini, alur kerja yang terdiri dari satu WriteLine aktivitas dibuat. Argumen WriteLine aktivitas Text diatur, dan alur kerja dipanggil. Jika aktivitas berisi aktivitas anak, metode konstruksinya serupa. Contoh berikut menggunakan aktivitas Sequence yang berisi dua aktivitas WriteLine.

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

WorkflowInvoker.Invoke(wf);

Menggunakan Penginisialisasi Objek

Contoh dalam topik ini menggunakan sintaks inisialisasi objek. Sintaks inisialisasi objek dapat menjadi cara yang berguna untuk membuat definisi alur kerja dalam kode karena menyediakan tampilan hierarkis aktivitas dalam alur kerja dan menunjukkan hubungan antara aktivitas. Tidak ada persyaratan untuk menggunakan sintaks inisialisasi objek saat Anda membuat alur kerja secara terprogram. Contoh berikut secara fungsional setara dengan contoh sebelumnya.

WriteLine hello = new WriteLine();
hello.Text = "Hello";

WriteLine world = new WriteLine();
world.Text = "World";

Sequence wf = new Sequence();
wf.Activities.Add(hello);
wf.Activities.Add(world);

WorkflowInvoker.Invoke(wf);

Untuk informasi selengkapnya tentang penginisialisasi objek, lihat Cara: Menginisialisasi Objek tanpa Memanggil Konstruktor (Panduan Pemrograman C#) dan Cara: Mendeklarasikan Objek dengan Menggunakan Penginisialisasi Objek.

Bekerja dengan Variabel, Nilai Literal, dan Ekspresi

Saat membuat definisi alur kerja menggunakan kode, ketahui kode apa yang dijalankan sebagai bagian dari pembuatan definisi alur kerja dan kode apa yang dijalankan sebagai bagian dari eksekusi instans alur kerja tersebut. Misalnya, alur kerja berikut dimaksudkan untuk menghasilkan angka acak dan menulisnya ke konsol.

Variable<int> n = new Variable<int>
{
    Name = "n"
};

Activity wf = new Sequence
{
    Variables = { n },
    Activities =
    {
        new Assign<int>
        {
            To = n,
            Value = new Random().Next(1, 101)
        },
        new WriteLine
        {
            Text = new InArgument<string>((env) => "The number is " + n.Get(env))
        }
    }
};

Ketika kode definisi alur kerja ini dijalankan, panggilan ke Random.Next dilakukan dan hasilnya disimpan dalam definisi alur kerja sebagai nilai harfiah. Banyak instance alur kerja ini dapat dipanggil, dan semua akan memperlihatkan angka yang sama. Agar pembuatan angka acak terjadi selama eksekusi alur kerja, ekspresi harus digunakan yang dievaluasi setiap kali alur kerja berjalan. Dalam contoh berikut, ekspresi Visual Basic digunakan dengan VisualBasicValue<TResult>.

new Assign<int>
{
    To = n,
    Value = new VisualBasicValue<int>("New Random().Next(1, 101)")
}

Ekspresi dalam contoh sebelumnya juga dapat diimplementasikan menggunakan CSharpValue<TResult> dan ekspresi C#.

new Assign<int>
{
    To = n,
    Value = new CSharpValue<int>("new Random().Next(1, 101)")
}

Ekspresi C# harus dikompilasi sebelum alur kerja yang berisinya dipanggil. Jika ekspresi C# tidak dikompilasi, NotSupportedException akan ditampilkan saat alur kerja dipanggil dengan pesan yang mirip dengan yang berikut ini: Expression Activity type 'CSharpValue`1' requires compilation in order to run. Please ensure that the workflow has been compiled. Dalam sebagian besar skenario yang melibatkan alur kerja yang dibuat di Visual Studio ekspresi C# dikompilasi secara otomatis, tetapi dalam beberapa skenario, seperti alur kerja kode, ekspresi C# harus dikompilasi secara manual. Untuk contoh cara mengkompilasi ekspresi C#, lihat bagian Menggunakan ekspresi C# di alur kerja kode dari topik Ekspresi C# .

mewakili VisualBasicValue<TResult> ekspresi dalam sintaks Visual Basic yang dapat digunakan sebagai nilai r dalam ekspresi, dan CSharpValue<TResult> mewakili ekspresi dalam sintaks C# yang dapat digunakan sebagai nilai r dalam ekspresi. Ekspresi ini dievaluasi setiap kali aktivitas yang mengandungnya dijalankan. Hasil ekspresi ditetapkan ke variabel nalur kerja , dan hasil ini digunakan oleh aktivitas berikutnya dalam alur kerja. Untuk mengakses nilai variabel n alur kerja pada runtime, ActivityContext diperlukan. Ini dapat diakses dengan menggunakan ekspresi lambda berikut.

Nota

Perhatikan bahwa kedua kode ini adalah contoh yang menggunakan C# sebagai bahasa pemrograman, tetapi satu menggunakan VisualBasicValue<TResult> dan satu menggunakan CSharpValue<TResult>. VisualBasicValue<TResult> dan CSharpValue<TResult> dapat digunakan dalam proyek Visual Basic dan C#. Secara default, ekspresi yang dibuat dalam perancang alur kerja cocok dengan bahasa proyek hosting. Saat membuat alur kerja dalam kode, bahasa yang dipilih tergantung pada kewenangan penulis alur kerja.

Dalam contoh ini hasil ekspresi ditetapkan ke variabel nalur kerja , dan hasil ini digunakan oleh aktivitas berikutnya dalam alur kerja. Untuk mengakses nilai variabel n alur kerja pada runtime, ActivityContext diperlukan. Ini dapat diakses dengan menggunakan ekspresi lambda berikut.

new WriteLine
{
    Text = new InArgument<string>((env) => "The number is " + n.Get(env))
}

Untuk informasi selengkapnya tentang ekspresi lambda, lihat Ekspresi Lambda (referensi C#) atau Ekspresi Lambda (Visual Basic).

Ekspresi Lambda tidak dapat diserialisasikan ke format XAML. Jika upaya untuk menserialisasikan alur kerja dengan ekspresi lambda dibuat, LambdaSerializationException dilemparkan dengan pesan berikut: "Alur kerja ini berisi ekspresi lambda yang ditentukan dalam kode. Ekspresi ini tidak dapat diserialisasikan XAML. Untuk membuat alur kerja Anda dapat diserialisasikan XAML, gunakan VisualBasicValue/VisualBasicReference atau ExpressionServices.Convert(lambda). Ini akan mengonversi ekspresi lambda Anda menjadi aktivitas ekspresi." Untuk membuat ekspresi ini kompatibel dengan XAML, gunakan ExpressionServices dan Convert, seperti yang ditunjukkan dalam contoh berikut.

new WriteLine
{
    //Text = new InArgument<string>((env) => "The number is " + n.Get(env))
    Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
}

A VisualBasicValue<TResult> juga dapat digunakan. Perhatikan bahwa tidak ada ekspresi lambda yang diperlukan saat menggunakan ekspresi Visual Basic.

new WriteLine
{
    //Text = new InArgument<string>((env) => "The number is " + n.Get(env))
    //Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
    Text = new VisualBasicValue<string>("\"The number is \" + n.ToString()")
}

Saat runtime, ekspresi Visual Basic dikompilasi ke dalam ekspresi LINQ. Kedua contoh sebelumnya dapat diserialisasikan ke XAML, tetapi jika XAML berseri dimaksudkan untuk dilihat dan diedit di perancang alur kerja, gunakan VisualBasicValue<TResult> untuk ekspresi Anda. Alur kerja berseri yang menggunakan ExpressionServices.Convert dapat dibuka di perancang, tetapi nilai ekspresi akan kosong. Untuk informasi selengkapnya tentang menserialisasikan alur kerja ke XAML, lihat Serialisasi Alur Kerja dan Aktivitas ke dan dari XAML.

Ekspresi Literal dan Jenis Referensi

Ekspresi harfiah diwakili dalam alur kerja oleh aktivitas Literal<T>. Aktivitas berikut WriteLine setara secara fungsional.

new WriteLine
{
    Text = "Hello World."
},
new WriteLine
{
    Text = new Literal<string>("Hello World.")
}

Tidak valid untuk menginisialisasi ekspresi harfiah dengan jenis referensi apa pun kecuali String. Dalam contoh berikut, properti Assign dari sebuah aktivitas Value diinisialisasi dengan ekspresi literal menggunakan List<string>.

new Assign
{
    To = new OutArgument<List<string>>(items),
    Value = new InArgument<List<string>>(new List<string>())
},

Ketika alur kerja yang berisi aktivitas ini divalidasi, kesalahan validasi berikut dikembalikan: "Literal hanya mendukung jenis nilai dan tipe System.String yang tidak dapat diubah. Tipe System.Collections.Generic.List'1[System.String] tidak dapat digunakan sebagai harfiah." Jika alur kerja dipanggil, InvalidWorkflowException dilemparkan yang berisi teks kesalahan validasi. Ini adalah kesalahan validasi karena membuat ekspresi harfiah dengan jenis referensi tidak membuat instans baru dari jenis referensi untuk setiap instans alur kerja. Untuk mengatasinya, ganti ekspresi literal dengan ekspresi yang membuat dan mengembalikan instance baru dari tipe referensi.

new Assign
{
    To = new OutArgument<List<string>>(items),
    Value = new InArgument<List<string>>(new VisualBasicValue<List<string>>("New List(Of String)"))
},

Untuk informasi selengkapnya tentang ekspresi, lihat Ekspresi.

Memanggil Metode pada Objek menggunakan Ekspresi dan Aktivitas InvokeMethod

Aktivitas InvokeMethod<TResult> dapat digunakan untuk memanggil metode statis dan instans kelas dalam .NET Framework. Dalam contoh sebelumnya dalam topik ini, angka acak dihasilkan menggunakan Random kelas .

new Assign<int>
{
    To = n,
    Value = new VisualBasicValue<int>("New Random().Next(1, 101)")
}

Aktivitas InvokeMethod<TResult> juga dapat telah digunakan untuk memanggil metode Next dari kelas Random.

new InvokeMethod<int>
{
    TargetObject = new InArgument<Random>(new VisualBasicValue<Random>("New Random()")),
    MethodName = "Next",
    Parameters =
    {
        new InArgument<int>(1),
        new InArgument<int>(101)
    },
    Result = n
}

Karena Next bukan metode statis, instans kelas Random diberikan kepada properti TargetObject. Dalam contoh ini, instans baru dibuat menggunakan ekspresi Visual Basic, tetapi juga dapat dibuat sebelumnya dan disimpan dalam variabel alur kerja. Dalam contoh ini, akan lebih mudah menggunakan aktivitas Assign<T> daripada aktivitas InvokeMethod<TResult>. Jika panggilan metode yang pada akhirnya dinvokasi oleh aktivitas Assign<T> atau InvokeMethod<TResult> berjalan lama, InvokeMethod<TResult> memiliki keuntungan karena memiliki properti RunAsynchronously. Ketika properti ini diatur ke true, metode yang dipanggil akan berjalan secara asinkron sehubungan dengan alur kerja. Jika aktivitas lain secara paralel, aktivitas tersebut tidak akan diblokir saat metode dijalankan secara asinkron. Selain itu, jika metode yang akan dipanggil tidak memiliki nilai pengembalian, maka InvokeMethod<TResult> adalah cara yang tepat untuk memanggil metode .

Argumen dan Aktivitas Dinamis

Definisi alur kerja dibuat dalam kode dengan merakit aktivitas ke dalam pohon aktivitas dan mengonfigurasi properti dan argumen apa pun. Argumen yang ada dapat terikat, tetapi argumen baru tidak dapat ditambahkan ke aktivitas. Ini termasuk argumen alur kerja yang diteruskan ke aktivitas root. Dalam kode imperatif, argumen alur kerja ditentukan sebagai properti pada jenis CLR baru, dan di XAML mereka dideklarasikan dengan menggunakan x:Class dan x:Member. Karena tidak ada jenis CLR baru yang dibuat ketika definisi alur kerja dibuat sebagai pohon objek dalam memori, argumen tidak dapat ditambahkan. Namun, argumen dapat ditambahkan ke DynamicActivity. Dalam contoh ini, DynamicActivity<TResult> dibuat yang mengambil dua argumen bilangan bulat, menambahkannya bersama-sama, dan mengembalikan hasilnya. DynamicActivityProperty dibuat untuk setiap argumen, dan hasil operasi ditetapkan ke Result argumen DynamicActivity<TResult>.

InArgument<int> Operand1 = new InArgument<int>();
InArgument<int> Operand2 = new InArgument<int>();

DynamicActivity<int> wf = new DynamicActivity<int>
{
    Properties =
    {
        new DynamicActivityProperty
        {
            Name = "Operand1",
            Type = typeof(InArgument<int>),
            Value = Operand1
        },
        new DynamicActivityProperty
        {
            Name = "Operand2",
            Type = typeof(InArgument<int>),
            Value = Operand2
        }
    },

    Implementation = () => new Sequence
    {
        Activities =
        {
            new Assign<int>
            {
                To = new ArgumentReference<int> { ArgumentName = "Result" },
                Value = new InArgument<int>((env) => Operand1.Get(env) + Operand2.Get(env))
            }
        }
    }
};

Dictionary<string, object> wfParams = new Dictionary<string, object>
{
    { "Operand1", 25 },
    { "Operand2", 15 }
};

int result = WorkflowInvoker.Invoke(wf, wfParams);
Console.WriteLine(result);

Untuk informasi selengkapnya tentang aktivitas dinamis, lihat Membuat Aktivitas di Runtime.

Aktivitas yang Dikompilasi

Aktivitas dinamis adalah salah satu cara untuk menentukan aktivitas yang berisi argumen menggunakan kode, tetapi aktivitas juga dapat dibuat dalam kode dan dikompilasi ke dalam jenis. Aktivitas sederhana dapat dibuat yang berasal dari CodeActivity, dan aktivitas asinkron yang berasal dari AsyncCodeActivity. Aktivitas ini dapat memiliki argumen, mengembalikan nilai, dan menentukan logikanya menggunakan kode imperatif. Untuk contoh pembuatan jenis aktivitas ini, lihat Kelas Dasar CodeActivity dan Membuat Aktivitas Asinkron.

Aktivitas yang berasal dari NativeActivity dapat menentukan logika mereka menggunakan kode imperatif dan mereka juga dapat berisi aktivitas anak yang menentukan logika. Mereka juga memiliki akses penuh ke fitur waktu jalan seperti membuat penanda. Untuk contoh pembuatan aktivitas berbasis NativeActivity, lihat Kelas Dasar NativeActivity, Cara: Membuat Aktivitas, dan contoh Komposit Kustom menggunakan Native Activity.

Aktivitas yang berasal dari Activity menentukan logika mereka hanya melalui penggunaan aktivitas anak. Aktivitas ini biasanya dibuat dengan menggunakan perancang alur kerja, tetapi juga dapat didefinisikan dengan menggunakan kode. Dalam contoh berikut, didefinisikan sebuah aktivitas Square yang berasal dari Activity<int>. Aktivitas Square memiliki satu InArgument<T> yang bernama Value, dan mendefinisikan logikanya dengan menetapkan aktivitas Sequence melalui properti Implementation. Aktivitas Sequence mengandung aktivitas WriteLine dan aktivitas Assign<T>. Secara bersama-sama, ketiga kegiatan ini menerapkan logika aktivitas Square.

class Square : Activity<int>
{
    [RequiredArgument]
    public InArgument<int> Value { get; set; }

    public Square()
    {
        this.Implementation = () => new Sequence
        {
            Activities =
            {
                new WriteLine
                {
                    Text = new InArgument<string>((env) => "Squaring the value: " + this.Value.Get(env))
                },
                new Assign<int>
                {
                    To = new OutArgument<int>((env) => this.Result.Get(env)),
                    Value = new InArgument<int>((env) => this.Value.Get(env) * this.Value.Get(env))
                }
            }
        };
    }
}

Dalam contoh berikut, definisi alur kerja yang terdiri dari satu Square aktivitas dipanggil menggunakan WorkflowInvoker.

Dictionary<string, object> inputs = new Dictionary<string, object> {{ "Value", 5}};
int result = WorkflowInvoker.Invoke(new Square(), inputs);
Console.WriteLine("Result: {0}", result);

Saat alur kerja dipanggil, output berikut ditampilkan ke konsol:

Mengkuadratkan nilai: 5Hasil: 25