Taoffi's blog

prisonniers du temps

Html Content Viewer for Silverlight

In a previous post, I talked about a solution to manipulate (animate for example) the Silverlight control inside the hosting html page.

 

The reverse side of the problem (displaying html content inside a Silverlight control), is a requirement which comes out in the context of numerous web projects.

 

Some of the proposed solutions suggest html translators in order to obtain the final text into a sort of ‘Rich Edit’ control.

Although it is very good to have a rich edit Silverlight control, the solution seems too tedious to solve a relatively simple problem: displaying html content into an html page (i.e. the page hosting our Silverlight control).

 

Building on the previous sample of animating the Silverlight control into the hosting page, I tried to make a user control that dynamically creates an iFrame to display the desired html content inside the main Silverlight page.

 

You can have a look at the proof of concept Here!

 

 

 

Dynamically create an html control

Suppose we want to display the html page: http://mycompany.com/page.html inside our Silverlight control.

 

The solution can be:

§  Use the HtmlPage to locate the hosting HtmlElement (where our Silverlight control lives: that can be straightforward:  HtmlPage.Plugin.Parent;)

§  Create a new html container (for example, a DIV)

§  Place an iframe html element inside this container

§  Set the iframe source to the desired page

§  Insert the container into the hosting HtmlElement

 

 

Here is a sample code illustrating these steps

 

private void CreateHtmlContent()

{

       HtmlElement  plugin_div   = HtmlPage.Plugin.Parent,

                    new_div      = HtmlPage.Document.CreateElement("div"),

                    iframe       = HtmlPage.Document.CreateElement("iframe");

 

       /// set the new div position to absolute

       new_div.SetStyleAttribute("position", "absolute");

       new_div.SetStyleAttribute("z-order", "5");

 

       new_div.SetStyleAttribute("left", "200px");

       new_div.SetStyleAttribute("top", "40px");

       new_div.SetStyleAttribute("width", "400px");

       new_div.SetStyleAttribute("height", "400px");

 

       /// setup the iframe style, attributes and target page

       iframe.SetStyleAttribute("width", "100%");

       iframe.SetStyleAttribute("height", "100%");

       iframe.SetAttribute("src", "http://www.isosoft.org/taoffi/");

 

       /// add the iframe to the new div

       new_div.AppendChild(iframe);

 

       /// add the new div to the hosting html page

       plugin_div.AppendChild(new_div);

}

 

The solution exposed here can be a suitable foundation to solve many situations where html content can be composed and/or displayed inline within a Silverlight control.

Let us begin by making a custom control, a HtmlContentViewer say!

 

The sample code exposes some more!

 

SilverHtmlContent.zip (49.34 kb)

Scrollable ComboBox

I am working on a new Silverlight user interface version of Simplesite.net and, as anyone can imagine, that involves a good dive into Silverlight as a ‘Line Of Business’ application technology.

One of the problems I encountered was to make the Combo box control able to respond to Mouse Wheel messages.

After having searched on different blogs and web sites, I didn’t find a suitable solution and ended up by delving into the subject.

Some of the proposed solutions consisted in ‘bringing into view’ one of the combo box items to force the list to scroll up or down as required… that seemed a little bit like a good workaround but was not very efficient and had a bad visual aspect.

 

What is a Silverlight ComboBox?

One of the nice (and pedagogic) aspects in Expression Blend is to be able to edit any existent control template (right click the control, select Edit Template / Edit a copy).

 

 

In Expression Blend, a Control Template reveals all the components of the control… this helps to understand some of the internal mechanisms of the control and how to use them efficiently.

 

To know more about the ComboBox control, I used Blend to insert one somewhere, and selected to edit a copy of its template.

That revealed the main components of the control:

§ A Layout Grid, containing:

§ A border (content presenter border) containing the static part of the combobox;

§ A Popup containing a ScrollViewer which will display the items.

 

It now seems clear that the scroll problem should be handled at the ScrollViewer component.

 

The SetIsMouseWheelScrollingEnabled method of the ScrollViewer can simply be used to activate the Mouse Wheel message handling for this component.

 

The solution

I saved my template to the application resources (App.xaml) with x:key=”combo_box_template”

<!-- ************* scrollable combo box template *******************************-->

<ControlTemplate x:Key="combo_box_template" TargetType="ComboBox">

 

 

I then created a class named ScrollableComboBox deriving from ComboBox

The control template is loaded and applied at class’s instantiation… and Mouse Wheel handling activated on the corresponding ScrollViewer component.

 

 

public partial class ScrollableComboBox : ComboBox

{

       protected ScrollViewer            m_scroll_viewer            = null;

 

       public ScrollableComboBox()

       {

             this.Style   = (Style) App.Current.Resources["ScrollableComboBoxStyle"];

             this.Template = (ControlTemplate) App.Current.Resources["combo_box_template"];

             this.ApplyTemplate();

            

             m_scroll_viewer = (ScrollViewer)GetTemplateChild("ScrollViewer");

 

             if (m_scroll_viewer != null)

             {

                    m_scroll_viewer.SetIsMouseWheelScrollingEnabled(true);

             }

    }

 

 

Surprisingly, that is all we need to activate scrolling on a Silverlight ComboBox!

We can now insert a ScrollableComboBox anywhere we need.

 

Animating Silverlight control inside the HTML Page

Live demo

.

 

 

A Silverlight control can communicate and interact with other HTML elements of the page on which it is placed.

This is done by using the HtmlPage object (namespace System.Windows.Browser)

 

By using HtmlPage, you can have access to all HTML elements of the current page and be able to get and set many of their properties and attributes, and, accessorily, call DHTML script functions that may be located there.

This means, among other things, that you can get and set style attributes of the HTML element inside which your own Silverlight control is hosted (the DIV element having the id commonly named "silverlightControlHost")

 

<div id="silverlightControlHost"

       ...

 

This gave me an idea about creating a custom control which would represent this same hosting html DIV element and, through this custom control, interact with the hosting element properties (width, height, location… etc.)

One of the nice features of Silverlight is the Storyboard animations usually used to animate Silverlight controls.

So, what if we try to use a storyboard to animate the Silverlight hosting html element!

 

Suppose that our animation should move the html host element across the page. From an html viewpoint, this would mean to change its top/left coordinates.

A Silverlight user control doesn’t contain Top / Left properties required to do such a job. So we have to add those properties to our custom control.

 

I first created an (empty) custom user control:

 

Xaml:

<UserControl x:Class="SilverHtmlInteraction.HostingDivControl"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       Background="Transparent"

       SizeChanged="UserControl_SizeChanged"

    Width="200" Height="200">

    <Grid x:Name="LayoutRoot" Background="White">

 

    </Grid>

</UserControl>

 

The control members:

 

private HtmlElement m_div        = null;

 

public HtmlElement Div

{

       get { return m_div; }

       set { m_div = value; }

}

 

The new Left and Top properties

 

public double Left

{

       get { return attrib = GetDimension("left", "offsetLeft", 0.0); }

       set { SetDimentionAttribute("left", value); }

}

public double Top

{

       get { return attrib = GetDimension("top", "offsetTop", 0.0); }

       set { SetDimentionAttribute("top", value); }

}

 

 

How to obtain html element dimensions (width, height, left, top... etc.)

 

// get the html element’s dimension (example: width, height, left...)

// first try to find the dimension in the element’s style (width, height, left, top...)

// if not found, try to obtain the element’s dimension property (offsetWidth, offsetHeight...)

private double GetDimension(string attrib_name, string property_name, double default_value)

{

       double attrib = GetDimensionAttribute(attrib_name, default_value);

 

       // if the style attribute is not present, try to get the dimension property

       if (attrib <= 0.0)

             return GetDimensionProperty( property_name, default_value);

       return attrib;

}

 

// get the html element’s style attribute (example: width, height, left...)

protected double GetDimensionAttribute(string attrib_name, double default_value)

{

       if (m_div == null)

       {

             debug_display_div_error();

             return default_value;

       }

 

       // try to get the style attribute’s value

       string str_value = m_div.GetStyleAttribute(attrib_name);

 

       if (string.IsNullOrEmpty(str_value))

       {

             return default_value;

       }

       // remove the dimension's unit (px)

       str_value = str_value.Replace("px", "");

 

       double value = default_value;

 

       // try to convert to numeric value

       if (double.TryParse(str_value, out value) == false)

             return default_value;

       return value;

}

 

// get the html element’s dimension property (example: offsetHeight, offsetWidth...)

protected double GetDimensionProperty(string property_name, double default_value)

{

       if( m_div == null)

       {

             debug_display_div_error();

             return default_value;

       }

      

       string str_value    = m_div.GetProperty( property_name).ToString();

 

       if (string.IsNullOrEmpty(str_value))

       {

             return default_value;

       }

       double value = default_value;

 

       // try to convert to numeric value

       if (double.TryParse(str_value, out value) == false)

             return default_value;

 

       return value;

}

 

I then registered the new Left and Top properties with the dependency system:

 

public static DependencyProperty LeftProperty = DependencyProperty.Register(

                    "Left", typeof(double), typeof(HostingDivControl),

                    new PropertyMetadata(

                           new PropertyChangedCallback(LeftPropertyChanged) ));

 

public static DependencyProperty TopProperty = DependencyProperty.Register(

                    "Top", typeof(double), typeof(HostingDivControl),

                    new PropertyMetadata(

                           new PropertyChangedCallback(TopPropertyChanged)));

 

private static void LeftPropertyChanged(DependencyObject obj,

                           DependencyPropertyChangedEventArgs e)

{

       if (obj == null)

             return;

       if (obj is HostingDivControl != true)

             return;

 

       HostingDivControl ctrl = (HostingDivControl)obj;

 

       ctrl.Left    = (double)e.NewValue;

}

 

 

private static void TopPropertyChanged(DependencyObject obj,

                           DependencyPropertyChangedEventArgs e)

{

       if( obj == null)

             return;

       if( obj is HostingDivControl !=true)

             return;

 

       HostingDivControl   ctrl   = (HostingDivControl) obj;

 

       ctrl.Top     = (double) e.NewValue;

}

 

 

The html element accessor should do some work each time this member is changed:

 

public HtmlElement Div

{

       get { return m_div; }

       set

       {

             m_div = value;

 

             if (m_div != null)

             {

                    base.Width   = this.Width;

                    base.Height  = this.Height;

                    Left         = GetDimension("left", "offsetLeft", 0.0);

                    Top          = GetDimension("top", "offsetTop", 0.0);

 

                    // to be able to change the left and top propertites,

                    // the html element should have its position style set

                    // to relative or absolute.

                    If (string.IsNullOrEmpty( m_div.GetStyleAttribute("position")))

                           m_div.SetStyleAttribute("position", "relative");

             }

             else

                    m_div_id = "";

       }

}

 

 

We are now ready to use our new custom control to animate the Silverlight control inside its hosting html page.

 

<Grid x:Name="LayoutRoot" Background="Transparent">

       <local:HostingDivControl x:Name="host_div" Margin="0"/>

 

The animation Storyboard

Let’s create a storyboard that changes the top/left properties of our control (which is the silver light control itself)

Note: I couldn’t use Expression Blend to create the storyboard. Blend seemed confused about the properties to be animated for this ‘special’ control. So I ended up by creating the storyboard by hand!

 

<UserControl.Resources>

       <Storyboard x:Name="Storyboard1">

             <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"

             Storyboard.TargetName="host_div" Storyboard.TargetProperty="(Top)">

                    <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.50" Value="50"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.80" Value="85"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:01.00" Value="130"/>

             </DoubleAnimationUsingKeyFrames>

 

             <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"

             Storyboard.TargetName="host_div" Storyboard.TargetProperty="(Left)">

                    <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.50" Value="100"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.80" Value="225"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:01.00" Value="350"/>

             </DoubleAnimationUsingKeyFrames>

                    ...

                    ...

 

       </Storyboard>

</UserControl.Resources>

 

 

Live demo

 

Download the sample code

AnimatedSilverHtmlHost.zip (83.45 kb)

 

Revealing object properties

System Attributes can be used to simply tag and retrieve object properties and methods in particular context.

In my case here, I wanted to be able to know which properties are contained in objects in order to be able to dynamically assign or change their values using simple text entries in a database.

 

I created an (empty) Attribute and named it xSysPropertyAttribute:

 

[global::System.AttributeUsage(

             AttributeTargets.Property,

             Inherited = false,

             AllowMultiple = true)]

public sealed class xSysPropertyAttribute : Attribute

{

       public xSysPropertyAttribute()

       {

       }

}

 

I then used this attribute to ‘tag’ the desired objects’ properties:

 

[xSysProperty]

public bool BoolProperty

{

       get { return m_bool_property; }

       set { m_bool_property = value; }

}

 

[xSysProperty]

public string StringProperty

{

       get { return m_string_property; }

       set { m_string_property = value; }

}

 

Using System.Reflection, I, now, can retrieve these properties and dynamically list them:

 

foreach (PropertyInfo p in obj.GetType().GetProperties())

{

       bool   has_sys_attribute   = Attribute.IsDefined( p, typeof(xSysProperty), false);

 

       if(has_sys_attribute)

       {

             // Do something with this property

       }

}

 

We can use this simple code to dynamically build lists of object’s (tagged) properties.

Once the list of the desired properties has been built, we can now read and modify their values (using user input, xml, text files or database records):

 

Example of Read the property’s value:

 

public object GetValue(MyObject obj, string property_name)

{

       if( obj == null || string.IsNullOrEmpty( property_name))

             return null;

 

       PropertyInfo pinfo  = this[property_name];

 

       if( pinfo == null)

             return null;

       return pinfo.GetValue(obj, null);

}

 

Example of Set the property’s value:

 

public bool SetValue(MyObject obj, string property_name, object value)

{

       if( obj == null)

             return false;

      

       PropertyInfo pinfo  = this[property_name];

 

       if(pinfo == null)

             return false;

 

       // is this a readonly property?

       if(pinfo.CanWrite == false)

             return false;

 

       // get the property's data type

       Type   property_type = pinfo.PropertyType;

       object new_value;

 

       try

       {

             // try to convert the given value to property's data type

             new_value    = Convert.ChangeType( value, property_type);

       }

       catch (Exception)

       {

             return false;

       }

 

       try

       {

             // try to assign the converted value to the object

             pinfo.SetValue( obj, new_value, null);

       }

       catch (Exception)

       {

             return false;

       }

 

       return true;

}

 

Still we have another problem: we need a solid link between listed properties and the objects on which we may get and set properties’ values. i.e. we need to be sure that a property is actually part of the target object without having to check this each time a get or set is requested.

Here comes one the benefits of template classes.

We can implement our pattern using a template list like the following:

 

public class xBuiltinPropertiesListBase<PROPERTY, OBJ_TYPE, PROPERTY_SYS_ATTRIB>

             : List<PROPERTY> where PROPERTY : PropertyInfo

 

The code above defines a list template that will contain objects of type PROPERTY and that type should be a PropertyInfo (or one derived class)

It also defines that for the list to be initialized, a class (System.Type) OBJ_TYPE and a System Attribute PROPERTY_SYS_ATTRIB should be provided.

 

This way, our list explicitly integrates the Type on which it will operate.

Providing the System Attribute may also allow extending the list template for future use in other contexts.

 

On list initialization, the template will proceed to listing all properties with the attribute PROPERTY_SYS_ATTRIB defined within the class of OBJ_TYPE:

 

public bool LoadClassAttributeProperties()

{

       Clear();

 

       Type   obj_type     = typeof(OBJ_TYPE);

 

       // loop through all class's properties.

       // insert those properties defined with the specified System.Attribute.

       foreach (PropertyInfo p in obj_type.GetProperties())

       {

             // Does this property define our System.Attribute?. add it to the list.

             bool   has_sys_attribute = Attribute.IsDefined( p,

                                        typeof( PROPERTY_SYS_ATTRIB), false);

 

             if(has_sys_attribute)

             {

                    PROPERTY     new_property = (PROPERTY)p;

                    Add(new_property);

             }

       }

 

       return true;

}

 

 

The above GetValue and SetValue methods will also have to be slightly modified before being integrated in the list template. Example:

 

public bool SetValue(OBJ_TYPE obj, string property_name, object value)

{

       if( obj == null)

             return false;

      

       PROPERTY     my_property  = this[property_name];

 

       if( my_property == null)

             return false;

 

       // is this a readonly property?

       if( my_property.CanWrite == false)

             return false;

 

       // get the property's data type

       Type   property_type = my_property.PropertyType;

       object new_value;

       ...

       ...

 

 


Download the sample code ExtensibleObjectProperties.zip (46.35 kb)

 

 

Invoking methods by name. A first approach

As many developers know, one of the interesting namespaces in .NET is System.Reflection.

Here I try to use Reflection to invoke loaded assemblies’ methods by their names. This can be quite useful for applications like storing menu or button actions in a database and bringing them to life during runtime (I will post something about this subject shortly).

The attached test application demonstrates this in a very simple manner. Each dialog button invokes a method defined somewhere in the loaded assembly using its name. The search is done by few methods in MethodFinder static class.

 

The dialog displays 3 buttons. The OnClick handler for each button invokes a method by its name:

private void button1_Click(object sender, EventArgs e)

{

       invoke_method("Method1");

}

 

private void button2_Click(object sender, EventArgs e)

{

       invoke_method("Method2");

}

 

The invoke_method function, calls a method finder to retrieve the method and, if found, invokes it:

 

private void invoke_method(string method_name)

{

       MethodInfo          method       = MethodFinder.FindMethod(method_name);

       string              str_result   = "";

 

       if (method != null)

       {

             str_result = method.Invoke(this, null).ToString();

             MessageBox.Show(str_result);

       }

       else

             MessageBox.Show("Couldn't find method in loaded assemblies");

}

 

What you should know

You probably know that a .NET assembly is composed of modules (generally just one module), each module is composed of classes (Types) and each class is composed of fields, properties and methods.

The MethodFinder.FindMethod function loops through runtime loaded assemblies, modules and classes to search for the method and return the found method if any.

 

FindMethod function:

public static MethodInfo FindMethod( string method_name)

{

       foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())

       {

             method = FindAssemblyMethod( asm, method_name);

             if( method != null)

                    return method;

       }

       return null;

}

 

FindAssemblyMethod function loops through assembly’s classes:

public static MethodInfo FindAssemblyMethod(Assembly asm, string method_name)

{

       MethodInfo   method;

 

       foreach (Module module in asm.GetModules())

       {

             method = FindModuleMethod( module, method_name);

             if( method != null)

                    return method;

       }

       return null;

}

 

As you have already guessed, FindModuleMethod function loops through module’s classes to search the method:

public static MethodInfo FindModuleMethod(Module module, string method_name)

{

       Type[]       module_types;

       MethodInfo   method;

 

       module_types = module.GetTypes();

 

       foreach (Type module_class in module_types)

       {

             method = FindClassMethod( module_class, method_name);

             if( method != null)

                    return method;

       }

 

       return null;

}

 

Finally FindClassMethod searches for the method inside the class’s available methods:

public static MethodInfo FindClassMethod( Type module_class, string method_name)

{

       MethodInfo[] methods;

 

       methods = module_class.GetMethods();

       foreach (MethodInfo method in methods)

       {

             if( string.Compare( method.Name, method_name, true) == 0)

                    return method;

       }

 

       return null;

}

 

This is a first approach that I believe that many useful applications can be built on.

I will expose some ideas on the subject in a next post.

 

Download the sample code (with some more details inside) InvokeMethodByName.zip (30.31 kb)

 

Revisiting the Database design foundations

Software is about defining objects structures, relationships and behavioral contour. This is probably a too short sentence to define what software is. But it can be a good commencement for what I would like to expose here.

Years passed for the software industry to mature and be able to express the complexity of this task. And, it seems that there still will be some more years to come for this maturity to gain some sort of stability and normalization.

Remember the lessons about the ‘Date object’ which would never display a ‘February 30’ day. Although that seems so far away, we still encounter things of the same kind (remember, for example, the ‘page 1 of 0’ syndrome!). Those are simple indications about how complex it can be to mimic the ‘real world’ system, its large span of objects, relationships and behaviors.

 

One of the early, and interesting, questions that the Software had to solve was: data storage.

Historically speaking, we can summarize the maturity of the provided solution into three stages:

§ The folder/file structure

§ The data table

§ The ‘relational’ database

 

The ‘data table’ solution seems particularly interesting:

If you have a look at a data table (even within any of the most elaborate of today’s database engines) you can easily state how the simplicity of the structure looks embarrassing:

§ A meta-data space which describes the table’s columns.

§ And, a storage space where the columns’ data is stored into rows.

 

 

 

 

Basically, the column definition describes its data type and storage size. This allows the storage system to organize the data rows according to theses definitions.

 

Of course, with the time going, so many other attributes and artifacts had been added to the data table solution. Column definitions, for example, got more elaborate (ex: default value, auto-increment, indexes, data validation constraints… etc.), Columns’ relationships (foreign keys…), Operations like row insertions, row deletions or row updates can be triggered by procedures that execute particular ‘business’ logic… etc. But the foundations of the solution remain strictly the same as the early reflection and design…

One of the reasons of this design success seems to be the readability of the solution.

 

Anyway, the fact is, today, nearly no one single software solution can live without a database! And that itself is a profound demonstration of the efficiency of the initial design.

 

The Software ‘Code / Database’ schism!

Strangely, databases still live almost apart from the software development cycle. When you talk software, you seem to be supposed talking about classes, inheritance, behaviors, user interface… etc. and databases almost seem so simplistic compared to this ‘alive’ jungle!

The reasons for this schism seem to be cultural and social (within the development community) more than being a verdict against the database solution efficiency.

 

Industrially speaking, this schism seems often to be one of the most common explanations of software projects failures. It is also, probably, one of the profound reasons for some of the software industry’s inefficiencies.

 

What I think is that databases efficiency can be beneficially used for leveraging software objects properties and behaviors. And that database organization and readability bring interesting applications and solutions for controlling objects behaviors in order to create more versatile and scalable software solutions.

At a very simple level, think about application configuration files (which are great solutions but can sometimes hardly be altered without producing inconsistencies).

Think about a truly structured configuration file, one that guaranties no typing errors, no inconsistencies according to application’s specific logic.

This can be, relatively easily, achieved using a Database!

 

Database-stored menus… extending and redirecting actions

Let’s go a step forward and use the database to store, for instance, application menus.

A menu is basically a displayed text that, when clicked, invokes an application’s functionality.

By storing menus in the database, we may be wishing to expose only some of our application’s functionalities (according to specific application version for example). We may also be wishing to let our customer’s IT team to expose different functionalities to user groups according to a predefined business role hierarchy.

We can also use this structure to localize our application menus without having to recompile specific localized versions.

Another advantage: to be able to add a new menu after a new functionality is added to the software, or remove one that no more exist etc.

 

Menus are good examples for the ‘action’ concept. In fact, clicking a menu, a button or other button-like item in the user interface, results in invoking an action in the software. The action to be invoked has an entry point (a function) which is, normally, part of the handled object’s code.

 

Thanks to .NET System.Reflection (or any Reflection-like library) we can retrieve a method by its name. This can allow us to store object’s method names in the database, and, in the action properties, tell the software which method to be called. The software can then search the defined method, and, if found, invokes it when the action is activated.

 

Of course there are some other considerations to be taken into account: invoked function parameters, return value... etc. But the main concept remains correct and feasible.

 

Database-based solutions advantages?

Well, just to name a few:

§  Runtime object properties modification: Imagine a (dynamic) ‘web page section’ object which is initially created as 600/800 pixels dimensions. A database-based solution can allow the user to easily change these properties to obtain, say, a 1027/800 pixels object on runtime… without having to recompile the application’s code;

§  Runtime object properties extension: Software (compiled) objects’ structures NEVER satisfy at 100% real world structures or requirements (at least during a reasonable period of time). With a database-based solution, using, for example, an EAV (Entity Attribute Value) implementation, you can envisage extending compiled software objects… without having to recompile the application’s code;

§  Action processing redirection: Imagine a software functionality that may require authentication only in some contexts and none in other contexts, depending on the customer’s environment. Thanks to database readability and simplicity, the customer can easily choose which entry point to assign to the action according to his or her environment (without having to recompile the software code)

§ 

§ 

 

SQL recursive schemas

SQL recursive schemas

 

One of the interesting features of relational databases is the possibility for a table to reference itself.

Suppose we want to represent the file folder system in a database. We can define a table having, for example, 3 fields: id, parent_id and name:

 

We can then create a self referencing relationship between the id and parent_id field.

Simple (and elegant) solution!

Yes, but that also let us inherit all specific constraints of recursive patterns.

 

The figure above shows the simple (and somehow elegant) of the table schema or pattern.

What it doesn’t show is how the ‘real world’ records will look like when stored in the table:

 

 

Two important constraints of such recursive schema are:

·         Path readability: i.e. as the schema allows an infinite nest levels, reading an item path may cause a stack overflow;

·         Cyclic references: i.e. a folder that references itself (or one of its children) as a parent (which may end up by creating infinite path readability loop;

 

The Nest level problem

A function or stored procedure for reading an item path may look like this:

 

CREATE FUNCTION dbo.Read_path

(

       @item_id int

)

RETURNS nvarchar(max)

AS

BEGIN

       declare             @parent_id   int,

                    @path        nvarchar(max),

                    @name        nvarchar(50)

      

       select @parent_id   = parent_id,

             @name        = name

       from dbo.sample_folders

       where(id = @item_id)

      

       if @parent_id is null

             return @name

 

       -- recursive call  

       return dbo.Read_path ( @parent_id) + N'\' + @name

END

GO

 

 

Such a function will work nicely as long as the folder’s nest level remains in a ‘reasonable’ value. For example, in MS SQL Server, nest level (recursive call stack whose value can be returned at any time by the @@nestlevel system function) has a maximum of 32.

 

So, it is more precautious to test current @@nestlevel value before proceeding:

 

if @@nestlevel >= 32

       return N'Path nest level exceeds allowed value'

 

However, doing so breaks the efficiency and usability of our pattern. The best solution would be to get rid of the recursive call inside our function. Here is an example using a goto label:

 

       DECLARE @folder_name       nvarchar(255),

              @parent_id   int,

              @cur_item_id int,

              @path        nvarchar(max)

 

       SELECT @path        = N'',

              @cur_item_id = @item_id

 

-- recursion label

get_nested_path:

       SELECT @folder_name = f.name,

             @parent_id   = f.parent_id

       FROM   dbo.sample_folders AS f

       WHERE( f.id = @cur_item_id)

 

 

       if @parent_id is null

       begin

              -- We are on the root

              return @folder_name + @path

       end

 

       select @cur_item_id = @parent_id,

              @path        = N’\’ + @folder_name + @path

 

       -- goto recursion label

       goto get_nested_path

 

 

Cyclic references problem

Our read path function can still be trapped into an infinite loop that can be caused by cyclic references… i.e. a folder referencing itself or one of its children as a parent:

 

 

One way to prevent cycling references is to create a constraint on the folders table that tests for cycling references. The constraint may call a function that returns true (1) if the item is not valid:

 

CREATE FUNCTION [dbo].[Is_cyclic_folder]

(

       @item_id            int,

       @parent_id          int

)

RETURNS bit

AS

BEGIN

       -- if this is a root item: OK

       if @parent_id is null

             return 0

      

       -- self referencing?

       if @parent_id = @item_id

             return 1

      

       declare             @parent_folder_id   int,

                    @folder_id          int

      

       -- initial parent folder

       select @parent_folder_id = @parent_id

      

       while NOT @parent_folder_id is null

       begin

             -- if the current parent folder is one of children folders:

             -- then this item is cyclic (NOT VALID)

             if @parent_folder_id = @item_id

                    return 1

            

             select @folder_id   = @parent_folder_id

            

             select @parent_folder_id   = parent_id

             from dbo.sample_folders

             where( id = @folder_id)

       end

      

       return 0

END

 

The constraint may look like this:

 

ALTER TABLE dbo.sample_folders ADD CONSTRAINT

       CK_sample_folders_cyclic CHECK ([dbo].[Is_cyclic_folder](id, parent_id) = 0)