Validating and Storing Properties in the Designer
As
in any component development model, it is necessary to store properties
that a user selects for your component so you can load them at runtime
and also validate that the values chosen by the user are appropriate.
To perform validation, you need to use the IComponentUI interface and implement the Validate function. To store and load information for runtime use, you use the IPersistPropertyBag interface and implement the Load and Save functions. Example method implementations are given in the following text.
Validating User Input
IComponentUI.Validate
is used to validate any property information and display an error
message to the user when the project is compiled. Most implementations
use either a collection or an ArrayList to store the errors. You then need to return the IEnumerator object from the ArrayList or collection at the end of the method with all the error messages you want displayed populated.
The
following example demonstrates how you can validate a developer's input
from within the IDE. Any errors are returned to the user as errors in
the IDE's error window.
'<summary>
'The Validate method is called by the BizTalk Editor during the build
'of a BizTalk project.
'</summary>
'<param name="obj">An Object containing the configuration 'properties.</param>
'<returns>The IEnumerator enables the caller to enumerate through a collection of
'strings containing error messages. These error messages appear as compiler error
'messages. To report successful property validation, the method should return an
'empty enumerator.</returns>
Public Function Validate(ByVal obj As Object) As System.Collections.IEnumerator_
Implements Microsoft.BizTalk.Component.Interop.IComponentUI.Validate
'example implementation:
Dim errorArray As New ArrayList
errorArray.Add("This is an error that will be shown...")
return errorArray.GetEnumerator
End Function
Using the Property Bag to Store Property Information
In order to store property information for pipeline components, you need to implement the IPersistPropertyBag interface and give an implementation to the Save and Load methods. These methods pass in the representative IPropertyBag object that will be used to store the property information. The IPropertyBag
is simply a structure that will hold a set of key/value pairs. The key
is a string, and the value is of type Object so it can accept any type.
You may ask yourself, "Why not store the object itself rather than
storing the name of the schema and constructing a New() object in the Load method?" The answer is because the Save function of the component will fail if you do this. When the properties are written to the ContextPropertyBag,
they are actually expressed within the BTP file as XML so that they can
be used for perinstance pipeline configuration.
'<summary<
'Loads configuration properties for the component
'</summary>
'<param name="pb">Configuration property bag</param>
'<param name="errlog">Error status</param>
Public Overridable Sub Load(ByVal pb As _
Microsoft.BizTalk.Component.Interop.IPropertyBag, ByVal errlog As Integer) _
Implements Microsoft.BizTalk.Component.Interop.IPersistPropertyBag.Load
Dim val As Object = Nothing
val = Me.ReadPropertyBag(pb, "MyDocSpec")
If (Not (val) Is Nothing) Then
Me._MyDocSpec = New _
Microsoft.BizTalk.Component.Utilities.SchemaWithNone(CType(val, String))
End If
val = Me.ReadPropertyBag(pb, "OutboundDocumentSpecification")
If (Not (val) Is Nothing) Then
Me._OutboundDocumentSpecification = New _
Microsoft.BizTalk.Component.Utilities.SchemaWithNone(CType(val, String))
End If
val = Me.ReadPropertyBag(pb, "FileRootNode")
If (Not (val) Is Nothing) Then
Me._FileRootNode = val
End If
val = Me.ReadPropertyBag(pb, "DataElementNode")
If (Not (val) Is Nothing) Then
Me._DataElementNode = val
End If
End Sub
'<summary>
'Saves the current component configuration into the property bag
'<summary>
'<param name="pb">Configuration property bag</param>
'<param name="fClearDirty">not used</param>
'<param name="fSaveAllProperties">not used</param>
Public Overridable Sub Save(ByVal pb As _
Microsoft.BizTalk.Component.Interop.IPropertyBag, ByVal fClearDirty As Boolean, _
ByVal fSaveAllProperties As Boolean) Implements _
Microsoft.BizTalk.Component.Interop.IPersistPropertyBag.Save
Me.WritePropertyBag(pb, "MyDocSpec", Me.MyDocSpec.SchemaName)
Me.WritePropertyBag(pb, "OutDocSpec", Me.OutboundDocumentSpecification.SchemaName
Me.WritePropertyBag(pb, "FileRootNode", Me.FileRootNode)
Me.WritePropertyBag(pb, "DataElementNode", Me.DataElementNode)
End Sub
'<summary>
'Reads property value from property bag
'</summary>
'<param name="pb">Property bag</param>
'<param name="propName">Name of property</param>
'<returns>Value of the property</returns>
Private Function ReadPropertyBag(ByVal pb As _
Microsoft.BizTalk.Component.Interop.IPropertyBag, ByVal propName As String) As _
Object
Dim val As Object = NothingTry
pb.Read(propName, val, 0)
Catch e As System.ArgumentException
Return val
Catch e As System.Exception
Throw New System.ApplicationException(e.Message)
End Try
Return val
End Function
'<summary>
'Writes property values into a property bag.
'</summary>
'<param name="pb">Property bag.</param>
'<param name="propName">Name of property.</param>
'<param name="val">Value of property.</param>
Private Sub WritePropertyBag(ByVal pb As _
Microsoft.BizTalk.Component.Interop.IPropertyBag, ByVal propName As String, ByVal _
val As Object)Try
pb.Write(propName, val)
Catch e As System.Exception
Throw New System.ApplicationException(e.Message)
End Try
End Sub
Custom Properties and Per-Instance Pipeline Configuration
Per-instance pipeline
configuration allows you to change the values of custom properties
using the BizTalk Administration Tools. The user interface provides you
with a mechanism to set the values for pipeline properties dynamically
for a receive location without having to create a new custom pipeline
for every new receive location. A few points of interest when
attempting to use this feature with a custom pipeline and pipeline
components are described here.
Custom
pipeline component properties for per-instance pipeline configuration
are actually stored within the .btp file for the pipeline definition. A
sample of the file follows. If you find that your custom properties are
not appearing in the per-instance pipeline configuration document, you
can manually add them to the XML of the .btp file and they will appear.
<?xml version="1.0" encoding="utf-16"?>
<Document xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
PolicyFilePath="BTSReceivePolicy.xml" MajorVersion="1" MinorVersion="0">
<Description />
<Stages>
<Stage CategoryId="9d0e4103-4cce-4536-83fa-4a5040674ad6">
<Components />
</Stage>
<Stage CategoryId="9d0e4105-4cce-4536-83fa-4a5040674ad6">
<Components />
</Stage>
<Stage CategoryId="9d0e410d-4cce-4536-83fa-4a5040674ad6">
<Components>
<Component>
<Name>ABC.BizTalk.PipelineComponents.Decoder</Name>
<Properties>
<Property Name="MyUnTypedProperty" />
<Property Name="MyStringProperty">
<Value xsi:type="xsd:string">My String Value</Value>
</Property>
</Properties>
<CachedDisplayName>Decoding Component</CachedDisplayName>
<CachedIsManaged>true</CachedIsManaged>
</Component>
</Components>
</Stage>
<Stage CategoryId="9d0e410e-4cce-4536-83fa-4a5040674ad6">
<Components />
</Stage>
</Stages>
</Document>
In
the .btp file for the pipeline definition, you can set the default data
of the pipeline component property by inserting a value in the
property's <Value> element.
The
<Value< that you insert must be an XSD type. For example, the
type must be xsd:string for a string value, or xsd:int for an integer
value.