Showing posts with label WWF. Show all posts
Showing posts with label WWF. Show all posts

Monday, February 8, 2010

Workflow Custom Activity – Part 2


Introduction

Windows Workflow foundation gives many activities to use in workflow like code, delay, IfElse, Listen, Parallel etc. But in some scenario, those activities will not work as per our requirement. So we need to implement custom activity which will work as per our requirement.

To create a custom activity, Please visit http://dotnet-codeguru.blogspot.com/2010/02/windows-workflow-foundation-custom.html

In some scenario, you have to create some dynamic property in custom activity. For an example you are creating custom activity for Order Search. In this activity, User can search order by order id or customer id. So you want to give filter where user can select either “Order ID” or “Customer ID”. If user will select Order ID then new property Order Number will display in property window. And if user will select Customer ID then new property Customer Number will display in property window.

It is similar to IfElse Branch condition property. You get two options “Code Condition” and “Declarative Rule Condition”. As per your selection, you will get sub properties.



Let’s implement same thing in custom activity:

To support sub property for Order Search type we need to implement two classes “OrderSearchByOrderID” and “OrderSearchByCustomerID” which are derived from “ActvityCondition” class.

In the above classes, we need to override “Evaluate” method. In this simple scenario, we always return true from the above classes.

In OrderSearchByOrderID class, we need to add one property for OrderID which will display when user select “OrderSearchByOrderID” option from the selection box. The code for the OrderSearchByOrderID class is look like below:


public class OrderSearchByOrderID : ActivityCondition
{
public override bool Evaluate(Activity activity, IServiceProvider provider)
{
return true;
}

private string orderID;

[Browsable(true)]
public string OrderID
{
get
{
return orderID;
}
set
{
orderID = value;
}
}

}


Code for OrderSearchByCustomerID is similar and looks like below:


public class OrderSearchByCustomerID : ActivityCondition
{
public override bool Evaluate(Activity activity, IServiceProvider provider)
{
return true;
}

private string orderID;

[Browsable(true)]
public string OrderID
{
get
{
return orderID;
}
set
{
orderID = value;
}
}
}


We need to develop “TypeConvertor” which helps to display the option in dropdown box in property window.


public class CustomOrderSearchTypeConverter : TypeConverter
{

private Hashtable _conditionDecls = new Hashtable();

public CustomOrderSearchTypeConverter()
{
try
{
AddTypeToHashTable(typeof(OrderSearchByCustomerID));
AddTypeToHashTable(typeof(OrderSearchByOrderID));
}
catch (Exception ex)
{
throw;
}
}

private void AddTypeToHashTable(Type typeToAdd)
{
string key = typeToAdd.FullName;
object[] attributes = typeToAdd.GetCustomAttributes(typeof(DisplayNameAttribute), false);
if (attributes != null && attributes.Length > 0 && attributes[0] is DisplayNameAttribute)
key = ((DisplayNameAttribute)attributes[0]).DisplayName;
this._conditionDecls.Add(key, typeToAdd);
}

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
try
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
catch (Exception ex)
{

throw;
}
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
try
{
string strVal = value as string;
if (strVal != null)
{
if (strVal.Length == 0 || strVal == "")
return null;
else
return Activator.CreateInstance(this._conditionDecls[value] as Type);
}
return base.ConvertFrom(context, culture, value);
}
catch (Exception ex)
{

throw;
}
}

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
try
{
if (destinationType == typeof(string))
{
return true;
}
else
{
return base.CanConvertTo(context, destinationType);
}
}
catch (Exception ex)
{

throw;
}
}

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
try
{
if (value == null)
return "";
object convertedValue = null;
if (destinationType == typeof(string) && value is ActivityCondition)
{
foreach (DictionaryEntry conditionTypeEntry in this._conditionDecls)
{
if (value.GetType() == conditionTypeEntry.Value)
{
convertedValue = conditionTypeEntry.Key;
break;
}
}
}
if (convertedValue == null)
convertedValue = base.ConvertTo(context, culture, value, destinationType);
return convertedValue;
}
catch (Exception ex)
{

throw;
}
}

public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
try
{
ArrayList conditionDeclList = new ArrayList();
conditionDeclList.Add(null);
foreach (object key in this._conditionDecls.Keys)
{
Type declType = this._conditionDecls[key] as Type;
conditionDeclList.Add(Activator.CreateInstance(declType));
}
return new StandardValuesCollection((ActivityCondition[])conditionDeclList.ToArray(typeof(ActivityCondition)));
}
catch (Exception ex)
{

throw;
}
}

public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
try
{
// Always return true.
return true;
}
catch (Exception ex)
{

throw;
}
}

public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
try
{
// Always return true.
return true;
}
catch (Exception ex)
{

throw;
}
}

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
try
{
PropertyDescriptorCollection props = new PropertyDescriptorCollection(new PropertyDescriptor[] { });
TypeConverter typeConverter = TypeDescriptor.GetConverter(value.GetType());
if (typeConverter != null && typeConverter.GetType() != GetType() && typeConverter.GetPropertiesSupported())
{
return typeConverter.GetProperties(context, value, attributes);
}
return props;
}
catch (Exception ex)
{

throw;
}
}

public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
try
{
// Always return true.
return true;
}
catch (Exception ex)
{

throw;
}
}

}


Now we need to use these classes in our custom activity. Just create a new class “OrderSearchActivity” and derive it from “Activity” class.

To give support, we need to implement one dependency property which is ActivityCondition type. Here we declared “OrderSearchTypeProperty” dependency property.

And also implement property for the same “OrderSearchType”.


public static DependencyProperty OrderSearchTypeProperty = DependencyProperty.Register("OrderSearchType", typeof(ActivityCondition), typeof(OrderSearchActivity));

[Description("Order Search Type")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[TypeConverter(typeof(CustomOrderSearchTypeConverter))]
public ActivityCondition OrderSearchType
{
get
{
return ((ActivityCondition)(base.GetValue(OrderSearchTypeProperty)));
}
set
{
base.SetValue(OrderSearchTypeProperty, value);
}
}


Now when you will use this OrderSearchActivity in your workflow then you will see option for OrderSearchType.




If you select “OrderSearchByCustomerID” then you will see “+” sign in front of “OrderSearchType” property which indicate that you have some sub properties. When you click on “+” sign you will get “Customer ID” as a sub property.





Now let’s see how we can use the above property in your custom activity. For this I have override Execute method in OrderSearchActivity.


protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{

if (OrderSearchType.GetType() == typeof(OrderSearchByCustomerID))
{
OrderSearchByCustomerID orderSearchByCustomerID = (OrderSearchByCustomerID)OrderSearchType;
string CustomerID = orderSearchByCustomerID.CustomerID;
// User CustomerID variable to search orders
}
else if (OrderSearchType.GetType() == typeof(OrderSearchByOrderID))
{
OrderSearchByOrderID orderSearchByOrderID = (OrderSearchByOrderID)OrderSearchType;
string OrderID = orderSearchByOrderID.OrderID;
// User OrderID variable to search orders
}

return base.Execute(executionContext);
}


In Execute method, we first check OrderSearchType property value type, if it is OrderSearchByCustomerID then we convert it in that and find customer id which is assigned by user. If type is OrderSearchByOrderID then we convert it in that and find order id which is assigned by user.


Windows Workflow Foundation Custom Activity - Part 1


Introduction

Windows Workflow foundation gives many activities to use in workflow like code, delay, IfElse, Listen, Parallel etc. But in some scenario, those activities will not work as per our requirement. So we need to implement custom activity which will work as per our requirement.

To create a custom activity, we need to implement one class which is derived from “Activity” base class because every workflow activity is directly/indirectly derived from “Activity” class.

Example

Let’s take one example. We have to create one workflow for Employee management. For this workflow, we need one activity which has some properties like FirstName, LastName, Age etc… (For employee entity) and have one function which will insert employee data into database.

For the above scenario, we need to create our own activity which takes care of above functionality.

Here below I created one class “EmployeeActivity” which is derived from the “Activity” class.

public class EmployeeActivity : Activity

{

}

Now we need to add properties for Employee entities. Here I took only 3 properties for demo purpose.

public class EmployeeActivity : Activity

{

private string firstName;

public string FirstName

{

get { return firstName; }

set { firstName = value; }

}

private string lastName;

public string LastName

{

get { return lastName; }

set { lastName = value; }

}

private int age;

public int Age

{

get { return age; }

set { age = value; }

}

}

In above code, I have created three private variables and public properties for FirstName, LastName and Age. You can add any number of variables and properties which you need.

Now we need to override “Execute” method of “Activity” class. This method will take care of inserting employee data into database.

Let’s implement “Execute” method:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)

{

/// Code to insert employee data into database

return base.Execute(executionContext);

}

Now you can put your custom activity on workflow and set the property. Whenever this activity will execute, it will insert employee data into database.

Now think that user wants to do some pre authorization before data will store into database. Means you have to implement one event which will execute before employee information will store in employee database.

Let’s create one event and integrate it with EmployeeActvity:

public delegate void PreAuthorizationEventHandler(object sender, CustomActivityEventArgs e);

public event PreAuthorizationEventHandler PreAuthorizationEvent;

Here we created one delegate and one event. We create one class “CustomActivityEventArgs” which is derived from “EventArgs” base class.

[Serializable]

public class CustomActivityEventArgs : EventArgs

{

private string firstName;

public string FirstName

{

get { return firstName; }

set { firstName = value; }

}

private string lastName;

public string LastName

{

get { return lastName; }

set { lastName = value; }

}

private int age;

public int Age

{

get { return age; }

set { age = value; }

}

}

Now we need to change “Execute” method code to execute event. Updated code is look as below:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)

{

CustomActivityEventArgs customActivityEventArgs = new CustomActivityEventArgs();

customActivityEventArgs.FirstName = FirstName;

customActivityEventArgs.Age = Age;

customActivityEventArgs.LastName = lastName;

//Here we are executing event

if (PreAuthorizationEvent != null)

PreAuthorizationEvent(this, customActivityEventArgs);

/// Code to insert employee data into database

return base.Execute(executionContext);

}

When you will use this EmployeeActivity in you workflow, you will get one event “PreAuthorization” which will execute before employee data inserting in the database.


DotNet Code Guru