Archive

Posts Tagged ‘PropertyTree’

On the Run – PropertyTree in Action

December 4, 2009 5 comments

In the previous post in this series I introduced PropertyTree a dynamic data structure that can be used from .Net and JavaScript.

In this entry I will show you examples of using PropertyTree.

Working with Nodes

In it’s simplest form, you assign values to nodes of the tree (PropertyNodes) by specifying the name of the node and assigning an object to the Value property.

The following example shows how to create a PropertyTree, populate it with values and read back those values:

In C#:

PropertyTree pt = new PropertyTree();

pt.Node("FirstName").Value = "Pink";
pt.Node("LastName").Value = "Floyd";
pt.Node("Birthdate").Value = new DateTime(1969, 9, 1);
pt.Node("Salary").Value = 100000M;

string firstName = pt.Node("FirstName").Value;
string lastName = pt.Node("LastName").Value;
DateTime birthdate = pt.Node("Birthdate").Value;
decimal salary = pt.Node("Salary").Value;

In VB:

Dim pt As New PropertyTree()

pt.Node("FirstName").Value = "Pink"
pt.Node("LastName").Value = "Floyd"
pt.Node("Birthdate").Value = New Date(1969, 9, 1)
pt.Node("Salary").Value = 100000D

Dim firstName As String = pt.Node("FirstName").Value
Dim lastName As String = pt.Node("LastName").Value
Dim birthdate As DateTime = pt.Node("Birthdate").Value
Dim salary As Decimal = pt.Node("Salary").Value

In this case the type of the node value is the type of the object assigned. The .Net types that a property node can support are:

  • System.String
  • System.Char
  • System.DateTime
  • System.Boolean
  • System.Byte
  • System.Int16
  • System.Int32
  • System.Int64
  • System.Decimal
  • System.Single
  • System.Double
  • System.SByte
  • System.UInt16
  • System.UInt32
  • System.UInt64
  • System.Xml.XmlDocument
  • System.Byte[] (Array of Byte)

Now for the “tree” part of PropertyTree. The PropertyTree class inherits from PropertyNode and every node has a Node default property that is used to access sub-nodes. To add sub-nodes to a node you can do the following:

In C#:

// Add values hierarchically
pt.Node("Customer").Node("FirstName").Value = "Sponge Bob";
pt.Node("Customer").Node("LastName").Value = "Square Pants";
pt.Node("Customer").Node("Address").Node("Street").Value = "123 Pineapple Dr";
pt.Node("Customer").Node("Address").Node("City").Value = "Bikini Bottom";
pt.Node("Customer").Node("Address").Node("State").Value = "HI";
pt.Node("Customer").Node("Address").Node("Country").Value = "US";
pt.Node("Customer").Node("Address").Node("ZipCode").Value = 96850;

In VB:

' Add values hierarchically
pt.Node("Customer").Node("FirstName").Value = "Sponge Bob"
pt.Node("Customer").Node("LastName").Value = "Square Pants"
pt.Node("Customer").Node("Address").Node("Street").Value = "123 Pineapple Dr"
pt.Node("Customer").Node("Address").Node("City").Value = "Bikini Bottom" pt.Node("Customer").Node("Address").Node("State").Value = "HI"
pt.Node("Customer").Node("Address").Node("Country").Value = "US"
pt.Node("Customer").Node("Address").Node("ZipCode").Value = 96850

Since the Node property is the default property (an indexer in C#), you can use the following syntax to simplify the code:

In C#:

// Add values hierarchically
pt["Customer"]["FirstName"].Value = "Sponge Bob";
pt["Customer"]["LastName"].Value = "Square Pants";
pt["Customer"]["Address"]["Street"].Value = "123 Pineapple Dr";
pt["Customer"]["Address"]["City"].Value = "Bikini Bottom";
pt["Customer"]["Address"]["State"].Value = "HI";
pt["Customer"]["Address"]["Country"].Value = "US";
pt["Customer"]["Address"]["ZipCode"].Value = 96850;

In VB:

' Add values hierarchically
pt("Customer")("FirstName").Value = "Sponge Bob"
pt("Customer")("LastName").Value = "Square Pants"
pt("Customer")("Address")("Street").Value = "123 Pineapple Dr"
pt("Customer")("Address")("City").Value = "Bikini Bottom"
pt("Customer")("Address")("State").Value = "HI"
pt("Customer")("Address")("Country").Value = "US"
pt("Customer")("Address")("ZipCode").Value = 96850

Also, you can access a node using a “dot” notation in the node name parameter:

In C#:

// Doing the same thing using "dot" notation
pt["Customer.FirstName"].Value = "Sponge Bob";
pt["Customer.LastName"].Value = "Square Pants";
pt["Customer.Address.Street"].Value = "123 Pineapple Dr";
pt["Customer.Address.City"].Value = "Bikini Bottom";
pt["Customer.Address.State"].Value = "HI";
pt["Customer.Address.Country"].Value = "US";
pt["Customer.Address.ZipCode"].Value = 96850;

In VB:

' Doing the same thing using "dot" notation
pt("Customer.FirstName").Value = "Sponge Bob"
pt("Customer.LastName").Value = "Square Pants"
pt("Customer.Address.Street").Value = "123 Pineapple Dr"
pt("Customer.Address.City").Value = "Bikini Bottom"
pt("Customer.Address.State").Value = "HI"
pt("Customer.Address.Country").Value = "US"
pt("Customer.Address.ZipCode").Value = 96850

Notice how the parent node doesn’t have to exist in order to assign a value to a child node (that also doesn’t exist), all nodes in the path are created automatically.

JavaScript

With the exception of tree, node and type creation, PropertyTrees can be used from JavaScript. To do so, you create the PropertyTree on the server and serialize it to the client. In this next example assume that xml contains a string with the serialized version of the PropertyTree from the previous example:

In JavaScript:

var pt = new Properties.PropertyTree(xml)

// Retrieve values

var firstName = pt.node("Customer").node("FirstName").get_value()
var lastName = pt.node("Customer").node("LastName").get_value()
var zip = pt.node("Customer.Address.ZipCode").get_value()

// Update values
pt.node("Customer").node("LastName").set_value("Pink")
pt.node("Customer").node("LastName").set_value("Floyd")
pt.node("Customer.Address.Country").set_value("UK")
pt.node("Customer.Address.ZipCode").set_value(90210)

In a future post I’ll go deeper into this topic.

Creating Nodes

In the previous examples the property nodes are created implicitly, each node is typed with the value that was assigned to it and it can hold any valid value for that type.

To create nodes and set restrictions on the values that it can hold we create nodes explicitly with the CreateNode function:

In C#:

PropertyTree pt = new PropertyTree();

pt.CreateNode<String>("FirstName");

In VB:

Dim pt As New PropertyTree()

pt.CreateNode(Of String)("FirstName")

PropertyNode names are case-sensitive.

The CreateNode function returns an object of type PropertyNodeNew which has the following methods:

MinValue(obj) The minimum value accepted by the node.
MaxValue(obj) The maximum value accepted by the node.
MinLength(int) The minimum character length. Applies only to System.String.
MaxLength(int) The maximum character length. Applies only to System.String.
Mask(string) A Regular Expression to filter text. Applies only to System.String.
Format(string) A format string used when outputting the value as a String.
Value(string) The initial value of the node.
Attribute(string, string) Adds a user-defined attribute and it’s value to the node.
ReadOnly() Indicates that the property value can only be read.
Required() Indicates that a value must be assigned to the property node.

Each one of these functions returns a PropertyNodeNew object which allows for a more fluent interface:

In C#:

PropertyTree pt = new PropertyTree();

pt.CreateNode<String>("Name")
    .MaxLength(32)
    .Mask("^[a-zA-Z]{1}[a-zA-Z0-9_]*$")
    .Required();
pt.CreateNode<Decimal>("Salary")
    .MinValue(1)
    .MaxValue(100000)
    .Format("C");

pt["Name"].Value = "GoodName";
pt["Salary"].Value = 25000;

// The next two lines will raise a PropertyValidationException
pt["Name"].Value = "A Bad Name";	// Contains spaces.
pt["Salary"].Value = 250000;	// The new value is out of range.

In VB:

Dim pt As New PropertyTree()

pt.CreateNode(Of String)("Name") _
    .MaxLength(32) _
    .Mask("^[a-zA-Z]{1}[a-zA-Z0-9_]*$") _
    .Required()
pt.CreateNode(Of Decimal)("Salary") _
    .MinValue(1) _
    .MaxValue(100000) _
    .Format("C")

pt["Name"].Value = "GoodName"
pt["Salary"].Value = 25000

' The next two lines will raise a PropertyValidationException
pt["Name"].Value = "A Bad Name"     ' Contains spaces.
pt["Salary"].Value = 250000         ' The new value is out of range.

CreateNode is not available in JavaScript but all restrictions on a node such as validation rules and formatting are implemented.

Creating Types

After a while you may find that you are creating many different properties with the same restrictions or validation rules.

You can create PropertyTypes that can be used when defining a PropertyNode and can even be the basis for other PropertyTypes that can define more specific restrictions. To do this you use the CreateType method of the Types property:

In C#:

PropertyTree pt = new PropertyTree();

pt.Types.CreateType<Decimal>("SalaryType")
    .MinValue(1)
    .MaxValue(100000)
    .Format("C");

pt.CreateNode("Salary", "SalaryType");

pt["Salary"].Value = 25000;

In VB:

Dim pt As New PropertyTree()

pt.Types.CreateType(Of Decimal)("SalaryType") _
    .MinValue(1) _
    .MaxValue(100000) _
    .Format("C")

pt.CreateNode("Salary", "SalaryType")

pt("Salary").Value = 25000

PropertyType names are case-sensitive.

The CreateType method returns an object of type PropertyTypeNew that can have any of the following methods:

MinValue(obj) The minimum value accepted by the node.
MaxValue(obj) The maximum value accepted by the node.
MinLength(int) The minimum character length. Applies only to System.String.
MaxLength(int) The maximum character length. Applies only to System.String.
Mask(string) A Regular Expression to filter text. Applies only to System.String.
Format(string) A format string used when outputting the value as a String.
AllowedValue(obj,[string]) A value of the defined type that is allowed. Any number of values can be added to form lists of allowed values.An optional System.String can be included for each value to be returned when using the ToString() method of a PropertyNode value.

Type inheritance is also available to create a type based on another type:

In C#:

PropertyTree pt = new PropertyTree();

pt.Types.CreateType<Decimal>("SalaryType")
    .MinValue(1)
    .MaxValue(100000)
    .Format("C");
pt.Types.CreateType("ExecutiveSalaryType", "SalaryType")
    .MinValue(50000);

pt.CreateNode("Salary", "ExecutiveSalaryType");

pt["Salary"].Value = 80000;

In VB:

Dim pt As New PropertyTree()

pt.Types.CreateType(Of Decimal)("SalaryType") _
    .MinValue(1) _
    .MaxValue(100000) _
    .Format("C")

pt.Types.CreateType("ExecutiveSalaryType", "SalaryType") _
    .MinValue(50000)

pt.CreateNode("Salary", "ExecutiveSalaryType")

pt(Salary).Value = 80000

When inheriting from a another type, you can only set ranges within the limits of the base type otherwise an Exception will be raised.

CreateType is not available in JavaScript but all restrictions on a type such as validation rules and formatting are implemented.

What’s Next?

In future posts I’ll show more examples of how to use validation as well as programmatically navigating the tree along with the full PropertyTree API. Also I’ll show how to create a PropertyTree using an XML Document syntax and how to use a PropertyTree from JavaScript.

Although in the near future I may set it up as an open-source project, I would currently like to make the PropertyTree library bits available to download and I will as soon as I figure out a good (free) way to do so. If you have any suggestions, please add a comment.

Technorati Tags: ,,
Digg This
Categories: .Net, PropertyTree Tags: ,

Welcome to the Machine – Introducing PropertyTree

September 21, 2009 1 comment

In this first post I will introduce you to PropertyTree, a data structure that I developed to handle the problem of adding custom fields to a record in a database and that has also proven very useful for client/server communication, validation and filling a whole host of other needs.

The Problem

It all started when I began developing a Business Process Modeling framework to replace the old Workflow framework that is the platform for all applications developed by the company I currently (2005-2010) work for.

One of the features of the current framework is the ability to store arbitrary bits of information for a given Activity Instance in a SQL Server database. To do this, for each Activity Instance record in the activity_instance table, one or more data items are stored in a separate activity_instance_items table. This proves to be a problem when you need to retrieve the data items because you have to pivot the data to produce a package with all the required data bits.

The other problem is that the data is stored as a string in a varchar(8000) so on retrieval the programmer needs to cast the string into the original data type and the only way of knowing the appropriate data type is to inspect the data records directly or treat them all as string, something that happens quite a lot.

The Solution

As I designed the new framework, I figured that a better way would be to store all the data items of an Activity Instance directly in its record in an XML field. That would keep all data items together with their respective instance in the same table. Also, since we are using XML, now we have the ability to store items hierarchically along with meta-data to describe each item.

And So PropertyTree Was Born

A PropertyTree allows you to store data items kind of like the old PropertyBag of early Visual Basic (pre .Net) or the Session-State Store in ASP.Net.

Data items are stored as a collection of nodes from a root node and each node itself can have a collection of nodes allowing for a tree-like structure to be formed.

Each node has meta-data to indicate, among other things, the data type of its stored data, formatting information and validation rules. You can also add your own meta-data items through custom attributes.

A PropertyTree also exposes events for changes in values of properties and attributes as well as when validating value assignments and for handling validation errors.

Features

This is a list of features that are exposed by the PropertyTree:

  • Hierarchical collection of data items (PropertyNodes).
  • PropertyNodes contain strongly-typed values.
  • PropertyNodes can have meta-data in the form of attributes.
  • Validation information can be defined for a PropertyNode like for example:
    • Minimum value
    • Maximum value
    • Minimum and maximum length
    • A regular expression mask of allowed characters
    • A list of allowed values
  • These validation rules can be applied to a single PropertyNode or in Types that can be reused on any number of nodes.
  • An output format for a PropertyNode can be defined.
  • Events are raised for:
    • After a change in PropertyNode value (PropertyChanged event)
    • After a change in an attribute of a PropertyNode (PropertyAttributeChanged event)
    • When a non-existing PropertyNode is referenced (PropertyNotFound event)
    • A hook to allow custom validation to be performed (ValidateValue event)
    • When a value assigned to a PropertyNode has caused a validation error (ValidationException event)
  • A PropertyTree can be used on a client of a web app using a JavaScript implementation that has also been created.

What’s Next?

In future posts I’ll dive deeper into each of these features starting with how to create an instance of a PropertyTree and add nodes with validation.

Digg This
Categories: PropertyTree Tags: