Biztalk Server 2006 and Windows Workflow Foundation: Better Together

This is a short note from last night's MICUG meeting about Biztalk and WF.

A typical system is like below:

Customers -- Processes -- Services -- Data Library -- Database

It is relatively easy to create Database, Data Library, Service programs, and Business Process Programs.

Developer can use
  • Biztalk to model business process across heterogeneous applications
  • Windows Workflow Foundation (WF) to model business processes involving human intervention

But:
  • How should I manage or control the state of a business process involving systems and people?
  • How do I allow business users to modify the business process on demand?

Biztalk 2006 does not allow to modify process on demand. Business users have to ask developer to change, compile and deploy new orchestration assembly. But WF allows to change or create workflow on demand.

Biztalk is not designed for user intervention either, while WF has sequential and stateful workflow to support user intervention.

So we can combine Biztalk and WF together to make everybody happy in two ways:
1. WF accesses Biztalk

This is a common design. WF deals with user interaction to fill in forms, then WF sends request to Biztalk to complete process across other systems.

2. Biztalk accesses WF services

In this design, Biztalk sends Web Service request to WF service for business users intervention. Also business users can access WF program to get Biztalk statistics through Biztalk BAM.

Although Biztalk R2 will build WF engine inside, the above two kinds of integration will still be reasonable.

A difference between OO method call and web service method call

Nowadays, .NET framework is making web service call similar to local method call to developers. There is no much learning curve to write integrated/distributed software using web service.

Traditionally when we develop OO software, we can define base class in API and call that API by passing subclass object. The benefit of this design is that we can use a single API to process multiple subclasses, which is easy and reasonable.

How about the API for Web Service method then? Can we follow the same style (ie. passing subclass object to the API) to define base class in API?

Unfortunately, no!

For example: There is a web method HelloWorld() like below

public class BaseClass
{
public string Name = "Base Class";
}

public class SubClass : BaseClass
{
public string Type = "Sub class";
}

[WebMethod]
public string HelloWorld(BaseClass oClass)
{
return "Hello " + oClass.Name;
}

1. If client gets web service definition through WSDL, it cannot get definition of SubClass. So it has no way to create a SubClass object to call HelloWorld()

2. If client shares BaseClass and SubClass library with web service, then the client can create a SubClass object (oSubClass). But the client can not call HelloWorld(oSubClass) that generated by WSDL either, because WSDL does not have any information for SubClass, not to mention the relationship between SubClass and BaseClass

3. If client shares BaseClass and SubClass library with web service, and call System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke() directly:

Invoke("HelloClass", new object[] {oSubClass});

That still can not work: XmlSerializer will throw exception because we are passing an incompatible object.

So when we design the interface of web service, we have to define APIs separately for each kind of subclass!

ASP.NET TreeView shows old datasource

I do not know what's wrong in my code: The TreeView always shows the old data source, although I assign a new data source to it.

My purpose: To show XML in a tree view. Each leaf element is in "element_name = element_value" format:

name
|
+-- first = F1
|
+-- last = L1

Here is a sample code:

Add a TreeView control to Default.aspx:

<asp:TreeView ID="xmlTreeView" runat="server"
OnTreeNodeDataBound="XmlTreeView_OnTreeNodeDataBound"
ShowLines="True" EnableViewState="False">

In code behind:

public partial class _Default : System.Web.UI.Page
{
List<string> xmlStrList = new List<string>();

protected void Page_Load(object sender, EventArgs e)
{
// Initiate XML data
xmlStrList.Add("<name><first>F1</first><last>L1</last></name>");
xmlStrList.Add("<name><first>F2</first><last>L2</last></name>");
xmlStrList.Add("<name><first>F3</first><last>L3</last></name>");
xmlStrList.Add("<name><first>F4</first><last>L4</last></name>");

// Get random XML string
int i = DateTime.Now.Second % xmlStrList.Count;

// Create XmlDataSource
XmlDataSource dataSource = new XmlDataSource();
dataSource.Data = xmlStrList[i];

// Bind XMLDataSource to tree view
xmlTreeView.DataSource = dataSource;
xmlTreeView.DataBind();
}

// To show leaf element in "element_name = element_value" format
protected virtual void XmlTreeView_OnTreeNodeDataBound(Object sender, TreeNodeEventArgs e)
{
if (e.Node.DataItem is XmlElement)
{
XmlElement element = (XmlElement)e.Node.DataItem;
if (!element.FirstChild.HasChildNodes)
{
e.Node.Text = e.Node.Text + " = " + element.InnerText;
}
}
}
}

Everytime when I refresh the page, I get different XML string data source, but I see the SAME tree view data in browser! --- That is weird ...

I don't know why?

[Update] Copied from asp.net forum:
------------------------------------
This looks like a bug in the XmlDataSource control where modifying the Data property does not invalidate the cache. Setting EnableCaching=false is the workaround to use here.

Thanks,

Eilon
------------------------------------

XmlSerializers.dll?

After I generated web service proxy using WSDL file and ran my web service client, I noticed this log:"Resolver failed to load assembly 'myAssembly.XmlSerializers.dll'". What is that?

I did not use any XmlSerializers DLL reference, or even did not know its existence until I saw XML Serializer Generator Tool:

The XML Serializer Generator creates an XML serialization assembly for types in a specified assembly in order to improve the startup performance of a XmlSerializer when it serializes or deserializes objects of the specified types.

Anyway, I added this line to "Post-build event" in Visual Studio project to generate myAssembly.XmlSerializers.dll dynamically:

"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\sgen" "$(TargetPath)"

propertySpecified in XML Serialization

What will be saved when an object of SomeDate class is serialized?

public class SomeDate
{
private short yearField;
private bool yearFieldSpecified;

public short Year
{
get
{
return this.yearField;
}
set
{
this.yearField = value;
}
}

[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool YearSpecified
{
get
{
return this.yearFieldSpecified;
}
set
{
this.yearFieldSpecified = value;
}
}
}

The serialization code is:

private static void InitializeDateFile()
{
SomeDate oDate = new SomeDate();
oDate.Year = 1910;
oDate.YearSpecified = true;

XmlSerializer xmlSer =
new XmlSerializer(typeof(SomeDate));
XmlTextWriter writer =
new XmlTextWriter(@"SomeDate.xml",
Encoding.Default);
writer.Formatting = Formatting.Indented;
xmlSer.Serialize(writer, oDate);
writer.Close();
}

With oDate.YearSpecified = true;, the output is:

<SomeDate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Year>1910</Year>
</SomeDate>

But without oDate.YearSpecified = true;, the output becomes:

<SomeDate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" />

Where is Year element?

Here I found the reason:

The XML Schema Definition Tool (Xsd.exe) occasionally generates the XmlIgnoreAttribute when creating classes from a schema file (.xsd). This behavior occurs because value types cannot be set to a null reference (Nothing in Visual Basic), but all XML data types can be. Therefore, the tool creates two fields when it encounters an XML type that maps to a value type: one to hold the value, and another, special field that takes the form of fieldnameSpecified, where the fieldname is replaced by the name of the field or property. Notice, however, that this special field is generated only when the schema specifies that the element need not occur (minOccurs = "0") and that the element has no default value. The XmlSerializer sets and checks this special field to determine whether a value has been set for the field or property. Because the special field must not be serialized, the tool applies the XmlIgnoreAttribute to it.

Good Videos (Taiji)

Although I have practiced Chen-style Taiji for several years, I did not learn Taiji Push-Hand.

From last month, I began to learn Push-Hand with Yang-style Taiji teachers. Their teaching also changed my feeling that Yang-style Taiji is eaiser than Chen-style. With good teachers around, I am confident that I can improve my health and Taiji skills from now on. :)

There are some good videos about Chen-style and Yang-style Taiji here. I save the link in this post for my later reference.