August 2007 - Posts

DNN+AJAX - Using CalendarExtender

This time, I want to know that is there something I have to do when using ASP .NET AJAX Control Toolkit in DotNetNuke module. So I decide to do some exploration on how to use it in DotNetNuke module. Based on DNN forums, there are some controls that doesn't work with DNN module. I think, all control should work because it just like ASP .NET control. But maybe we have to do something trick (or maybe hack) in order to make all Ajax Control Toolkit work in DNN module.

Now, I'm gonna test CalendarExtender control.

As usual, create your new DNN module (or just open the last one). Add new web user control and name it AjaxCalendar.ascx (or whatever name you like). My complete AjaxCalendar.ascx is below :

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AjaxCalendar.ascx.cs"
          Inherits="DNNAjaxControlToolkitUsageModule.AjaxCalendar" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<style type="text/css">
.MyCalendar .ajax__calendar_container {
    border:1px solid #646464;
    background-color: lemonchiffon;
    color: red;
}
.MyCalendar .ajax__calendar_other .ajax__calendar_day,
.MyCalendar .ajax__calendar_other .ajax__calendar_year {
    color: black;
}
.MyCalendar .ajax__calendar_hover .ajax__calendar_day,
.MyCalendar .ajax__calendar_hover .ajax__calendar_month,
.MyCalendar .ajax__calendar_hover .ajax__calendar_year {
    color: black;
}
.MyCalendar .ajax__calendar_active .ajax__calendar_day,
.MyCalendar .ajax__calendar_active .ajax__calendar_month,
.MyCalendar .ajax__calendar_active .ajax__calendar_year {
    color: black;
    font-weight:bold;
}
</style>

<asp:Panel ID="pnlDummy" runat="server" Style='position: relative;'>
        <asp:TextBox runat="server" ID="Date1" autocomplete="off" /><br />
        <ajaxToolkit:CalendarExtender ID="defaultCalendarExtender" runat="server" TargetControlID="Date1" />
        <div style='font-size: 90%'>
            <em>(Set the focus to the textbox to show the calendar)</em></div>
        <br />
        <br />
        <b>Calendar with a custom style and formatted date:</b><br />
        <asp:TextBox runat="server" ID="Date2" autocomplete="off" /><br />
        <ajaxToolkit:CalendarExtender ID="customCalendarExtender" runat="server" TargetControlID="Date2"
            CssClass="MyCalendar" Format="MMMM d, yyyy" />
        <div style='font-size: 90%'>
            <em>(Set the focus to the textbox to show the calendar)</em></div>
        <br />
        <br />
</asp:Panel> 

Notes that nothing special in that code. I add reference to AjaxControlToolkit assembly. Add one Panel. Inside it, add two textbox controls and two CalendarExtender controls. The stylesheet code is copied from stylesheet.css from SampleWebsite in AjaxControlToolkit-NoSource.zip.

The result is like this :

CalendarExtender control usage

Based on my experience, if AjaxControlToolkit using relative or position calculation based on offset position, then you have to wrap those control inside a panel (or you can use <div> tag). So, I wrap two textbox and two CalendarExtender controls with one Panel control. That's the trick. If you don't do that. The position of the calendar will float and not in the right position. But some control should use a little more trick. I will explain what kind of Ajax Control Toolkit that should do more hack on it.

Hope this help.

Share this post: | | | |
Posted by agung | 1 comment(s)
Filed under: ,

DNN+AJAX - Using Accordion

This is how to use ASP .NET AJAX Control Toolkit Accordion inside DotNetNuke module.

The scenario is like this. Our Accordion control will be put in the UI. Then AccordionPane object will be created dynamically from code. I don't use declarative programming to construct AccordionPane object. Instead, I use my own code. This approach is flexible since you can control your layout or view of your Accordion object. Accordion object also support data binding, so it's easy for you to integrate it in your database based application. Let's begin.

First, open your module (if your module is already open) or create a new one. Add one web user control and name it with AjaxAccordion.ascx (mine using Visual C# as my primary language). Add one Accordion control inside it. This is complete code for AjaxAccordion.ascx (my solution name is DNNAjaxControlToolkitUsageModule, it may differ than yours) :

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AjaxAccordion.ascx.cs" Inherits="DNNAjaxControlToolkitUsageModule.AjaxAccordion" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<style type="text/css">
.accordionHeader
{
    border: 1px solid #2F4F4F;
    color: white;
    background-color: #2E4d7B;
    font-family: Arial, Sans-Serif;
    font-size: 12px;
    font-weight: bold;
    padding: 5px;
    margin-top: 5px;
    cursor: pointer;
}

.accordionContent
{
    background-color: #D3DEEF;
    border: 1px dashed #2F4F4F;
    border-top: none;
    padding: 5px;
    padding-top: 10px;
}

</style>

<ajaxToolkit:Accordion ID="MyAccordion"
    runat="server"
    TransitionDuration="250" HeaderCssClass="accordionHeader"  ContentCssClass="accordionContent"
    FramesPerSecond="40" SelectedIndex="0"></ajaxToolkit:Accordion>

 

Notes:
There is nothing special about the syntax above. Just add a refference to AjaxControlToolkit assembly, then add one Accordion control. I copied the stylesheet from SampleWebsite inside AjaxControlToolkit-NoSource.zip. You can modify based on your need.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using AjaxControlToolkit;

namespace DNNAjaxControlToolkitUsageModule {
    public partial class AjaxAccordion : DotNetNukwrap glyph
e.Entities.Modules.PortalModuleBase {

        private string VeryLongContent = "The Accowrap glyph
rdion is written using an extender like most of thwrap glyph
e other extenders in the AJAX Control Toolkit.<br wrap glyph
/>"
 +
                    "The extender expects its inpuwrap glyph
t in a very specific hierarchy of container elemenwrap glyph
ts (like divs), so <br />"
 +
                    "the Accordion and AccordionPawrap glyph
ne web controls are used to generate the expected wrap glyph
input for the extender.<br />"
 +
                    "The extender can also be usedwrap glyph
 on its own if you provide it appropriate input."
;wrap glyph


        protected void Page_Load(object sender, Evwrap glyph
entArgs e) {
            if(!IsPostBack) {
                BuildAccordion();
            }
        }

        private void BuildAccordion() {
            
            string header = string.Empty;
            string content = string.Empty;

            for(int i = 0; i < 10; i++) {
                header = "Header-" + i.ToString();wrap glyph

                content = "Data-" + i.ToString() +wrap glyph
 "<br />" + VeryLongContent;

                Literal litHeader = new Literal();wrap glyph

                Literal litContent = new Literal()wrap glyph
;

                litHeader.Text = header;
                litContent.Text = content;

                AccordionPane pane = new Accordionwrap glyph
Pane();
                pane.ID = "AccordionPane" + i.ToStwrap glyph
ring();
                pane.HeaderContainer.Controls.Add(wrap glyph
litHeader);
                pane.ContentContainer.Controls.Addwrap glyph
(litContent);

                MyAccordion.Panes.Add(pane);
            }

        }

    }
}

 

There is no trick to use Accordion Ajax Control Toolkit inside your DotNetNuke module. In my code behind, I only create dummy data and put it inside AccordionPane using Literal control. If you work with database, you can also using binding technique based on collection.

This is my screenshot result :

Accordion result

Hope this help.

Share this post: | | | |
Posted by agung | with no comments
Filed under: ,

Using ValidatorCalloutExtender

Someone has a problem when using ValidatorCalloutExtender in DotNetNuke module. Hopefully my article can solve his problem.

Ok, let's begin.

First, create your new module in Visual Studio using your favourite technique. Or maybe just load your existing module source into Visual Studio. Suppose that i have a name of my module is "SimpleHTML". Then i have one SimpleHTMLModule.ascx.

The next step is, if you want to use ASP .NET AJAX functionality then the easiest way is modify your control (.ascx) from DotNetNuke Control Panel and add Support Partial Rendering feature like below :

Go to Host --> Module Definition . Then choose your module that you want to add ajax functionality.

Host, Module Definition

Choose your control that you want to add partial rendering feature. Mine is SimpleHTMLModule.ascx. Then check Supports Partial Rendering checkbox.

Support Partial Rendering feature

Then, design your UI like below. I add these controls in my UI design :

  • One UpdatePanel control
  • One Panel control 
  • One Button control
  • Two TextBox control
  • Two RequiredFieldValidator control
  • Two ValidatorCalloutExtender control

Module UI design

I'll explain it. First, we add one UpdatePanel control. Then add one Panel control. Inside Panel control, add two TextBox control, two RequiredFieldValidator control, and two ValidatorCalloutExtender control. Change their properties like below :

  • UpdatePanel --> nothing changes
  • Panel --> Add Style attribute to Style='position: relative;' 
  • Button --> Text=Click Me, ValidationGroup=vg1
  • TextBox1 --> ValidationGroup=vg1
  • TextBox2 --> ValidationGroup=vg1
  • RequiredFieldValidator1 --> ControlToValidate=TextBox1, Display=None, ValidationGroup=vg1, ErrorMessage=Fill your UserId
  • RequiredFieldValidator2 --> ControlToValidate=TextBox2, Display=None, ValidationGroup=vg1, ErrorMessage=Fill also your User name
  • ValidatorCalloutExtender1 --> ID=vcalUserId, TargetControlID=RequiredFieldValidator1
  • ValidatorCalloutExtender2 --> ID=vcalUserId, TargetControlID=RequiredFieldValidator2

That's it ! Now you can use ValidatorCalloutExtender in your DotNetNuke module. This is the screenshot of my module.

Module result

The trick is when adding one Panel with Style='position:relative;' into UpdatePanel. If you don't do that, your ValidatorCalloutExtender will shown in wrong position.

This is the complete code in SimpleHTMLModule.ascx :

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="SimpleHTMLModule.ascx.vb" Inherits="DesktopModules_SimpleHTML_SimpleHTMLModule" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Click Me" ValidationGroup="vg1" /><br />
        <br />
        User Id :
        <br />
        <asp:Panel ID="pnlDummy" style='position: relative;' runat='server'>
        <asp:TextBox ID="TextBox1" runat="server" ValidationGroup="vg1"></asp:TextBox><br />
        <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1"
            ErrorMessage="User Id should not be blank !" ValidationGroup="vg1" Display="None"></asp:RequiredFieldValidator>
        <cc1:ValidatorCalloutExtender ID="vcalUserId" runat="server" TargetControlID="RequiredFieldValidator1">
        </cc1:ValidatorCalloutExtender>
        <br />
        User Name :<br />
        <asp:TextBox ID="TextBox2" runat="server" ValidationGroup="vg1"></asp:TextBox><br />
        <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="TextBox2"
            ErrorMessage="User Name also should not be blank" ValidationGroup="vg1" Display="None"></asp:RequiredFieldValidator>
        <cc1:ValidatorCalloutExtender ID="vcalUserName" runat="server" TargetControlID="RequiredFieldValidator2">
        </cc1:ValidatorCalloutExtender>
        </asp:Panel>
        <br />
        <br />
        <br />
    </ContentTemplate>
</asp:UpdatePanel>

Hope this help.

UPDATE: You can remove UpdatePanel control in the code above. When you check the Support Partial Rendering is the same like put your module inside one UpdatePanel control by DNN. So the control will work. Here, I justr demonstrate to make it clear.

Share this post: | | | |
Posted by agung | with no comments
Filed under: ,

Passing Data antara Host dan Workflow - Bagian 1

Sebelum kita mulai, tentunya kita familiar dengan tipe-tipe data string, integer, boolean, decimal, atau mungkin yang berupa collection seperi array, arraylist, hashtable, generic<T>. Tapi, seberapa familiar kita dengan tipe data generic bertipe Dictionary<Key,Value> atau NameValueCollection ? Kebanyakan mereka yang belajar workflow masih bingung dengannya. Kenapa saya bahas ini, karena ini juga merupakan basic fundamental dari passing data dari host ke workflow dan bagaimana retrieve data dari workflow ke host.

Di level Host, kita membuat sebuah turunan dan workflow engine dan kemudian create workflow instance berdasarkan workflow yang kita load. Misalkan ada kode berikut ini pada level Host (anggaplah kita menggunakan pendekatan console UI).

static void Main(string[] args)
{
    using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
    {
        AutoResetEvent waitHandle = new AutoResetEvent(false);
        workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) 
        {
            waitHandle.Set();
        };

        workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
        {
            Console.WriteLine(e.Exception.Message);
            waitHandle.Set();
        };

        WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1));
        instance.Start();

        waitHandle.WaitOne();

    }
}

Untuk melakukan passing data secara sederhanya, inilah yang harus dilakukan.

  1. Buat variabel bertipe dictionary di level host, lalu passing ke dalam factory method CreateWorkflow sebagai parameter setelah workflow.
  2. Pada workflow yang kita buat (misalkan namanya Workflow1.cs), tambahkan satu atau lebih public properties dengan tipe data yang kita sesuaikan. Properties ini nantinya akan bisa diambil kembali setelah workflow itu selesai dikerjakan melalui event WorkflowCompleted melalui WorkflowCompletedEventArgs.
  3. Host kemudian mengambil nilai properties ini melalui WorkflowCompletedEventArgs menggunakan collection OutputParameters.

Jadi, kode tersebut akan kita ubah sedikit sebagai berikut :

static void Main(string[] args)
{
    using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
    {
        AutoResetEvent waitHandle = new AutoResetEvent(false);
        workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) 
        {
            waitHandle.Set();
        };

        workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
        {
            Console.WriteLine(e.Exception.Message);
            waitHandle.Set();
        };

        Dictionary<string, object> wfParams = new Dictionary<string, object>();
        wfParams.Add("PublicProperty01InsideWorkflow1", "Suatu nilai"); 
        wfParams.Add("PublicProperty02InsideWorkflow1", "Suatu nilai"); 

        WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1), wfParams);
        instance.Start();

        waitHandle.WaitOne();

    }
}

Perhatikan yang dicetak tebal. Itu adalah tipe data generic Dictionary<> yang akan dipassing pada workflow. Key pada wfParams itu harus sesuai dengan public property yang dideklarasikan pada workflow. Jadi, kode pada workflow Workflow1.cs sekarang akan kita tambahkan dua buah public property sebagai berikut :

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;

namespace MyWorkflowProject
{
   public sealed partial class Workflow1: SequentialWorkflowActivity
   {
        private string publicProperty01InsideWorkflow1;
        public string PublicProperty01InsideWorkflow1 {
            get { return publicProperty01InsideWorkflow1; }
            set { publicProperty01InsideWorkflow1 = value; }
        }
 
        private string publicProperty02InsideWorkflow1;
        public string PublicProperty02InsideWorkflow1 {
            get { return publicProperty02InsideWorkflow1; }
            set { publicProperty02InsideWorkflow1 = value; }
        }
 
        private string documents;
        public Documents ListDocument {
            get { return documents; }
            set { documents = value; }
        }

        public Workflow1()
        {
             InitializeComponent();
        }

    }
}

Di dalam Workflow1.cs  saya menambahkan 3 public properties. Yang dua bertipe string, dan satu lagi bertipe custom object bernama Documents.

Nah, di dalam workflow itu kita bisa memasukkan CodeActivity atau activity yang lainnya untuk memanipulasi private variabel, nilainya kemudian akan dilewatkan oleh workflow melalui WorkflowCompletedEventArgs di dalam event WorkflowCompleted. Jadi, host sekarang bisa retrieve nilainya sebagai berikut :

static void Main(string[] args)
{
    using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
    {
        AutoResetEvent waitHandle = new AutoResetEvent(false);
        workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) 
        {
            Documents docs = (Documents)e.OutputParameters["ListDocument"];
            string prop1 = (string)e.OutputParameters["PublicProperty01InsideWorkflow1"];
            string prop2 = (string)e.OutputParameters["PublicProperty02InsideWorkflow1"];
            Console.WriteLine(string.Format("Nilai property 1 dan 2 = {0} dan {1}", prop1, prop2));

            waitHandle.Set();
        };

        workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
        {
            Console.WriteLine(e.Exception.Message);
            waitHandle.Set();
        };

        Dictionary<string, object> wfParams = new Dictionary<string, object>();
        wfParams.Add("PublicProperty01InsideWorkflow1", "Suatu nilai"); 
        wfParams.Add("PublicProperty02InsideWorkflow1", "Suatu nilai"); 

        WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1), wfParams);
        instance.Start();

        waitHandle.WaitOne();

    }
}

See, itu model passing data atau komunikasi sederhana antara host dengan workflow dan workflow dengan host melalui workflow parameters. Kita bisa menambahkan properties atau method pada workflow kita lalu menyimpan nilainya. Kemudian nilai ini akan bisa diretrieve oleh host ketika workflow itu selesai. Perhatikan bahwa saya melakukan casting karena pada dictionary yang saya lewatkan menggunakan pasangan <Key,Value> bertipe <string,object>. Anda bisa saja menggunakan pasangan <Key,Value> lain seperti <string,string> atau <string, Document>, atau apapun yang Anda inginkan.

Jika Anda belum familiar dengan generic bertipe Dictionary<Key,Value> atau NameValueCollection atau semua yang berpasangan menggunakan <Key,Value>, maka saatnya mulai melakukan explorasi tipe data tersebut. Ini akan memudahkan Anda memahami konsep dasar workflow.

Share this post: | | | |
Posted by agung | with no comments
Filed under:

Host, Workflow, dan Custom Business Logic Object

Sebelum masuk ke coding, ada baiknya kita memahami istilah berikut ini :

  1. Host
  2. Workflow
  3. Custom Business Logic Object

Terminologi ini akan memudahkan kita untuk melihat sudut pandang aplikasi berbasis workflow.

HOST

Host adalah tempat dimana workflow "dilekatkan". Artinya, workflow ini tidak dapat berdiri sendiri. Dia perlu ditumpangkan pada induknya, yakni host. Host bisa berarti aplikasi console, aplikasi dekstop, aplikasi web, atau aplikasi mobile via workflow yang diexpose sebagai web service.

Host ini nantinya akan mengatur daur hidup workflow melalui object Workflow Runtime dan Workflow Instance. Artinya, workflow Anda akan di create dan di load pada saat runtime pada host. Karena di load pada saat runtime, maka secara otomatis thread yang digunakan otomatis akan mengikuti thread host-nya. Dengan teknik khusus, kita memang bisa mengatur supaya si workflow ini berjalan pada thread yang berbeda, tapi kita perlu trik untuk melakukan itu dengan cara membuat semacam workflow helper (menggunakan singleton dan factory pattern) yang akan load workflow instance dan meletakkannya pada thread tersendiri. Penjelasan detail bagaimana melakukan itu akan saya jelaskan di artikel yang akan datang.

Intinya, host adalah aplikasi pemanggil workflow dimana workflow dilekatkan. Bisa berupa console, desktop, web, atau mobile melalui mekanisme web service.

WORKFLOW

Saya tidak akan membahas workflow dari sisi skenario bisnis. Saya akan memberikan gambaran mengenai istilah 'workflow' pada terminologi Workflow Foundation. Di dalam WF, ada 4 jenis workflow yang bisa dibuat yakni Sequential, State Machine, Workflow Activity Library, dan Workflow Library.

By default, jika kita memilih Sequential maka Visual Studio akan membuatkan 1 buah workflow dan 1 buah host bertipe console application. Demikian juga untuk State Machine.

Workflow Library, secara umum merupakan kumpulan workflow dalam satu project. Ia merupakan kumpulan workflow yang nanti dicompile menjadi DLL tunggal dan direferensikan pada host yang memanggilnya. Di dalamnya Anda bisa membuat sequential workflow ataupun state machine workflow atau gabungan keduanya. Masing-masing secara logic dibedakan dari penggunaan Namespace dan nama class dari masing-masing workflow. Host nanti akan me-load dari nama namespace dan class yang dipanggil.

Workflow Activity Library, secara umum konsepnya sama seperti User Control. Disini Anda membuat sebuah activity (yaitu unit terkecil suatu workflow yang berisi fungsionalitas tertentu. Mirip seperti web part pada SharePoint atau module pada DotNetNuke atau user control pada general .NET development). Activity ini nanti akan "nempel" pada toolbox dan dapat Anda integrasikan pada workflow yang Anda pilih (sequential atau state machine). Activity ini dapat di load juga secara otomatis saat runtime atau disimpan sebagai file .xoml yang nanti akan dicompile on-the-fly oleh kode pemanggilnya untuk diintegrasikan pada suatu workflow.

CUSTOM BUSINESS LOGIC OBJECT

Custom business logic object (CBO) biasanya terdiri dari satu atau lebih object yang mewakili entiti pelaku skenario bisnis dan satu atau lebih kelas yang doing something terhadap entiti tersebut. Pendekatannya pun tergantung dari kemampuan si developer itu sendiri. Ada yang paham mengenai design pattern dan OOP yang cukup matang, lalu membuat CBO berdasarkan pendekatan tersebut. Itu bagus. Intinya, semua berbasis class dan kembali ke konsep OOP.

Meski demikian, kadang ada juga yang sederhana. Tapi umumnya, kebanyakan dari kita memuat informasi suatu class yang terdiri dari properties dan method. Kita bahkan jarang menggunakan event di dalam CBO kita. Nah, disitulah masalahnya. Kurangnya pemahaman kita dalam melihat kelengkapan suatu class dan apa yang bisa kita hasilkan dari situ. Pada beberapa activity di dalam workflow, event sangat berperan penting.

Di dalam terminologi workflow, CBO bisa dibagi menjadi dua yakni CBO yang berada di luar domain workflow (direferensikan sebagai DLL yang harus diinstantiate di dalam workflow) dan CBO yang diregister ke dalam workflow sebagai service melalui mekanisme ExternalDataExchangeService. Perbedaannya adalah dari cara mengaksesnya. Pada CBO yang direferensikan sebagai DLL bukan sebagai service, maka Anda harus melakukan instantiate suatu class sebelum menggunakannya. Problem pada CBO model tersebut secara umum tidak akan berefek pada workflow karena Anda bisa trapping itu menggunakan mekanisme error catching seperti biasa. Kelemahannya adalah lebih lambat karena berada pada domain yang berbeda dengan workflow dan tidak bisa digunakan bersamaan dengan Workflow Activity Library (WAL). Sedangkan CBO yang direferensikan sebagai service, performancenya jauh lebih cepat karena ia berada didalam bagian dari workflow itu sendiri dan dapat langsung diakses tanpa mekanisme instantiate seperti halnya model yang pertama. Ia juga bisa mengakses dan diakses via Workflow Activity Library secara langsung.

Beberapa jenis WAL yang menggunakan pendekatan tersebut adalah HandleExternalEventActivity dan CallExternalMethodActivity. Kedua activity tersebut memerlukan CBO yang diregister sebagai service dan menggunakan teknik passing data berbasis event. Maka tentunya kelas yang kita buat pada CBO harus "dipersenjatai" dengan pemahaman sintaks dan implementasi event di dalamnya. Tanpa itu, kita sulit untuk mengintegrasikannya dengan workflow.

Semoga penjelasan tersebut bisa dipahami dengan mudah. Saya akan melanjutkan di artikel yang akan datang.

Share this post: | | | |
Posted by agung | with no comments
Filed under:

Memahami Workflow Foundation

Fyuuh, akhirnya selesai juga membaca buku ke-empat mengenai Workflow Foundation. Empat buku tersebut adalah sebagai berikut :

  1. Foundations of WF. An Introduction To Windows Workflow Foundation. Dibuat oleh Brian R. Myers. Terbitan Apress.
  2. Microsoft Windows Workflow Foundation Step by Step. Dibuat oleh Kenn Scribner. Terbitan Microsoft Press.
  3. Programming Windows Workflow Foundation. Practical WF Techniques and Example using XAML and C#. Dibuat oleh K. Scott Allen. Terbitan PACKT Publishing.
  4. Professional Windows Workflow Foundation. Dibuat oleh Todd Kita. Terbitan WROX.

Saya coba akan sharing kepada pembaca sekalian seperti apa workflow foundation itu dan bagaimana mengintegrasikannya ke dalam aplikasi. Konsep dasar apa yang harus dipahami agar bisa dengan mudah memahami Workflow Foundation. Dan cukup diingat saja bahwa WF (atau yang lainnya juga seperti AJAX, WCF, ASP .NET) itu semua cuman API saja. Konsep dasarnya ada pada pemahaman mengenai language features, sisi arsitektural, dan paradigma pemrogramannya.

Pengalaman mengisi training Workflow untuk beberapa company adalah berada pada kendala language features pada bahasa C# atau Visual Basic yang rata-rata belum familiar bagi kebanyakan peserta. Anonymous method, interface, delegate, event, event handler, raising event, event arguments, hampir rata-rata peserta semua belum familiar di level sintaks dan implementasi.

Tingkat yang relatif cukup sulit di WF pada kebanyakan peserta adalah mengenai passing data dari Host ke Workflow dan sebaliknya.

Secara umum, komunikasi pada Workflow Foundation terbagi menjadi beberapa alternatif skenario sebagai berikut :

  1. Host to Workflow and vice versa
  2. Workflow to Workflow
  3. Host to Workflow via Custom Business Object and vice versa
  4. Host to Workflow via Custom Business Object as a workflow service and vice versa

Mengapa saya perlu memberikan pemahaman dasar ini adalah karena ini merupakan pemahaman mendasar yang pertama kali harus dimengerti oleh mereka yang ingin mencoba teknologi WF. Sebab pada dasarnya, WF bukan teknologi yang berdiri sendiri. Ia perlu Host sebagai "induk" tempat melekatkan workflow. Host ini bisa Console, WinForm, WebForm, atau yang lainnya. Oleh karena workflow harus dilekatkan, maka kita perlu memahami model komunikasi dan alternatif kemungkinan yang ada di dalamnya. Dan 4 kemungkinan itulah yang menjadi basic pemahaman awal untuk naik ke level yang lebih tinggi.

Setelah memahami modelnya, kemudian cara komunikasinya juga harus diperhatikan. WF menggunakan model programming based on interface untuk bisa komunikasi dengan eksternal resource workflow. Oleh karena itu konsep interface, abstract class, concrete class, event args, harus sudah familiar. Sebab ini akan menentukan bagaimana cara kita passing data kepadanya.

Contoh mudah, anggaplah ada suatu case dimana kita akan verifikasi suatu dokumen. Kita sudah tentukan bahwa custom business object itu adalah class Document yang terdiri dari DocumentId dan DocumentTitle misalnya. Dan kita akan memanfaatkan model nomor 4 (Host to Workflow via custom business object and vice versa).

So, we have to do like this (dalam pendekatan ala WF) :

1. Buat sebuah custom business object bernama Document dengan property DocumentId dan Title.

using System;

public class Document {

    private int documentId;
    private string documentTitle;

    public int DocumentId {
        get { return documentId; }
        set { documentId = value; }
    }

    public string DocumentTitle {
        get { return documentTitle; }
        set { documentTitle = value; }
    }

}

Tidak ada yang aneh pada kelas ini. Cuman single entity yang merepresentasikan object Document. Anda bisa saja membuat kelas yang merupakan kumpulan dari object Document yang diturunkan dari List<Document>. Sehingga tidak perlu lagi menggunakan ArrayList untuk menampungnya. Misal :

using System;
using System.Collections.Generic;

public class Documents : List<Document> {

    public Documents() {
    }

}

Jadi, nanti Anda tinggal menggunakan pendekatan seperti ini untuk membuat kumpulan dari object Document.

Documents docs = new Documents();
docs.Add(new Document());

2. Buat sebuah kelas EventArgs yang merupakan implementasi object yang nantinya akan dipassing ke workflow --> host atau kebalikannya. Kelas document dibungkus ke dalam eventargs ini. Tandai kelas ini dengan atribut Serializable untuk bisa dipassing antar layer. Jadi nanti kedepannya kelas inilah yang akan "dilempar" antar layer. Di dalamnya kita bisa tambahkan properties dan beberapa proses yang dibutuhkan sesuai dengan skenario business.

using System;
using System.Collections.Generic;
using System.Workflow.Activity;

[Serializable]
public class DocumentEventArgs : ExternalDataEventArgs {

    public DocumentEventArgs(Guid instanceId, Document doc) : base(instanceId) {

        docSubmit = doc;
    }

    private Document docSubmit;
    public Document DocumentSubmit {
        get { return docSubmit; }
        set { docSubmit = value; }
    }

}

Artinya sekarang kelas DocumentEventArgs merupakan object yang nanti akan kita lewatkan antar layer. Kelas ini juga diturunkan dari kelas ExternalDataEventArgs milik Workflow. Yakni agar nanti si workflow bisa mengenali kelas ini saat akan dipassing antar layer. Object ini karena beris property Document, maka akan dengan mudah mengambil object Document yang dipassing ke dalamnya.

3. Buat interface yang merupakan kontrak apa yang nanti akan dipassing ke workflow

using System;
using System.Collections.Generic;
using System.Workflow.Activity;

[ExternalDataExchange]
public interface IDocument {

    void SubmitDocument(Document doc);
    event EventHandler<DocumentEventArgs> DocumentSubmitted;

}

Disini interface IDocument di-decorate dengan atribut ExternalDataExchange karena ia adalah interface yang merupakan kontrak antara workflow dengan custom business kita.

4. Buat kelas implementasi dari interface di atas

using System;
using System.Workflow.Activity;
using System.Collections.Generic;

public class DocumentService : IDocument {

    public void RaiseDocumentSubmitted(DocumentEventArgs arg) {
        event EventHandler<DocumentEventArgs> documentSubmitted = this.DocumentSubmitted;
        if (documentSubmitted != null) {
            documentSubmitted(null, arg);
        }
    }

    public event EventHandler<DocumentEventArgs> DocumentSubmitted;

    public void SubmitDocument(Document doc) {
        // do something code here
    }

}

5. Register kelas DocumentService itu sebagai service pada host yang ingin workflow tsb kita lekatkan. Misal Console UI apps, winform, atau webform menggunakan pendekatan yang sudah di define oleh WF.

ExternalDataExchangeService dataExchange = new ExternalDataExchangeService();
workflowRuntime.AddService(dataExchange);

DocumentService docService = new DocumentService();
dataExchange.AddService(docService);

Dan sekarang service kita sudah dilekatkan ke dalam workflow engine dan siap digunakan pada workflow activity library yang tersedia.

Lihatlah pendekatan konsepnya. Sebenarnya itu tidak sulit asalkan kita paham level sintaks dan pemahaman arsitektur dasar programming berbasis interface. Konsep inheritance, interface, attribute, event, event handler, raising event, class, concrete class, abstract class, passing data, itu semua ada di level yang non API. Jadi, memahami itu akan membuat pemahaman terhadap WF menjadi lebih mudah.

Nah, rata-rata pendekatan di atas itu jarang atau bahkan belum umum bagi kebanyakan peserta. Bahkan ada yang sama sekali tidak memahami cara kerja event, menggunakan event arguments dan bagaimana mengimplementasikannya ke dalam aplikasi.

Itulah sekelumit singkat pendahuluan yang nanti akan mengiringi artikel lanjutan mengenai workflow ini.

Semoga membantu.

Share this post: | | | |
Posted by agung | 7 comment(s)
Filed under:

Workflow Foundation (WF) information in Bahasa

I hope in my spare time I can write some articles about Workflow Foundation in Bahasa Indonesia. This is based on my experience when doing some project with Workflow here in BataviaSoft.

Hopefully you will enjoy it.

Share this post: | | | |
Posted by agung | with no comments
Filed under:

DotNetNuke + Workflow Foundation + Communication Foundation

I've been playing DotNetNuke and do it in a lot of my internal project in BataviaSoft. Last month i've been playing with Workflow Foundation and Communication Foundation. I think the combination of them will be good one. Since DNN is web application framework for me for doing almost all web business case scenario whereas a workflow is something that must exist in ALL industry. Integrating workflow into DNN will provide a solid framework to doing a business scenario. Here, we do our first project using DotNetNuke + Workflow Foundation + Communication Foundation for our client in Netherland.

WF offers a lot of features, for example, we can expose a workflow as a web service. We can combine Workflow with WCF (sometimes named WF+C) and then expose it to client. You got the transparancy when dealing with your business workflow company. Or you can generate WSDL to create proxy object that you can consume from client. Because doing workflow (WF) in web application a little bit different than console or winform application, so something that you have to concern when dealing with WF in web environment is performance. Developers should beware how to create effective workflow and run it from ASP .NET. Long running workflow (or maybe a workflow that produce iteration that never end) will make your web looks like hang. So beware.

Unfortunately, a lot of my scenario is happened in web based with intranet mode, not internet. Since it is in intranet, so bandwidth is not a concern. It's time to combine DotNetNuke with Workflow Foundation and Communication Foundation.

I'm also doing a Workflow Foundation training if you want to know more about it. Last week i did training in PT. Newmont Nusa Tenggara for 5 days about "Practical Workflow Foundation". And if you already have a good skill about one of this stuffs :

  1. ASP .NET 
  2. DotNetNuke
  3. Windows Workflow Foundation
  4. Windows Communication Foundation

Just drop me your resume to agung.riyadi@bataviasoft.com

Share this post: | | | |
Posted by agung | 2 comment(s)
Filed under:

Enskilopediana Ilmu dalam Al Quran

Hari ini pesanan buku nyampe ke kantor, judulnya "Ensiklopediana Ilmu dalam Al Quran" yang dibuat oleh Afzalur Rahman yakni penulis buku termasyur "Encyclopedia of Seerah". Pengantar diisi oleh DR. K.H. Didin Hafidhuddin, M.Sc dan Kata Sambutan oleh DR. Ir. Djoko Santoso, M.Sc (Rektor ITB). Di dalamnya dibahas mengenai :

  • Astronomi
  • Politik
  • Fisika
  • Psikologi
  • Matematika
  • Ekonomi
  • Kimia
  • Arsitektur
  • Sosiologi
  • Kedokteran
  • Sejarah
  • Antropologi
  • Geologi
  • Biologi
  • Arkeologi
  • Pertanian

Sungguh menarik. Sebab sejak era renaisans di Barat, sains bisa dikatakan telah menaklukkan agama. Agama di Barat memang masih diakui keberadaannya, tetapi posisinya terus terpinggirikan dari ranah ilmu pengetahuan, hanya bersemayam di ranah misteri wahyu.

Islam, sebaliknya, tidak mempertentangkan wahyu dan ilmu pengetahuan karena keduanya merupakan dua aspek dari kebenaran yang sama. Wahyu Al Quran mengajak pembacanya untuk meneliti alam dan mengembangkan ilmu pengetahuan: "Apakah mereka tidak memperhatikan bagaimana unta diciptakan, langit ditinggikan, gunung ditegakkan, bumi dihamparkan?", demikian Quran memberitahu kita.

Buku tersebut mengajak Anda untuk mengeksplorasi isyarat-isyarat sains yagn bertaburan di dalam Al Quran. Inilah safari dzikir dan fikir menjelajahi semesta ilmu-ilmu yang wajib dibaca oleh setiap pengkaji Al Quran dan ilmu pengetahuan.

I'm gonna add it in my books collection.

Share this post: | | | |
Posted by agung | with no comments
Filed under:

DotNetNuke 4.6.0 y.a.d memperkenalkan provider otentifikasi yang lebih kaya

Ya, DotNetNuke 4.6.0 yang akan datang memperkenalkan provider otentifikasi yang lebih kaya. Jika sebelumnya hanya Forms Authentication dan Windows Authentication, maka kedepannya provider otentifikasi yang sedang dikerjakan adalah sebagai berikut :

  • CardSpace (dikenalkan sebagai bagian dari .NET Framework 3.0)
  • OpenID (sebuah repository yang tersentralisasi untuk identitas digital yang gratis dan dapat tersedia kapanpun Anda butuhkan selama ada koneksi internet)
  • LiveID (pengganti Microsoft Passport)

Menarik ya ?

Berikut ini beberapa preview screenshot yang saya ambil dari website Charles Nurse (salah satu Core Developer DNN) :

 
Tabel tempat penyimpanan informasi otentifikasi provider pada DNN 4.6 yang akan datang.

Halaman Login DNN yang lebih memuat banyak dukungan provider otentifikasi. User diberi pilihan lebih banyak untuk menggunakan model otentifikasi yang diinginkannya yang cocok dengan skenario bisnisnya.

Halaman Settings otentifikasi provider yang dipakai oleh DotNetNuke 4.6 yang akan datang.

Saat melakukan registrasi account baru, Anda bisa mengasosiasikan user account tersebut ke OpenID, CardSpace, atau LiveID. Asosiasi mapping DNN user account ini ada pada halaman Authentication Settings.

Cool !

Informasi mengenai OpenID dapat dilihat disini : http://en.wikipedia.org/wiki/OpenID atau dari situs resminya langsung disini : http://openid.net/

Share this post: | | | |
Posted by agung | with no comments
Filed under:

DotNetNuke 4.5.5 released

Yes. An update version of DotNetNuke 4.5.4 has been released. Two significan bugs which were introduced with 4.5.4 and which affected a large number of sites. You can download it here : http://www.dotnetnuke.com or directly from this link : http://sourceforge.net/project/showfiles.php?group_id=77052

Enjoy it.

Share this post: | | | |
Posted by agung | with no comments
Filed under: