Blog from July, 2008

NHibernate Tips

If you want to learn NHibernate - read this blog: http://www.ayende.com/Blog/.

Today I tried to figure out why there are problems in serialization of DelftShell and it looks like most problems can be solved only by changing one line of code in XML mapping file. For example when project is created and any item is added to it, then save and then create a new folder and drag item to it - it will crash with some strange message telling something about cascades (hmm... I forgot what it really says). Looks like a problem was in:

cascade="all-delete-orphan"

if it is set to all relations like <any>, <many-to-one>, <one-to-many> - it will crash soon (smile). Solution is that for composition it must be all-delete-orphan and for a simple aggregation it must be "all".

It is explained here on Ayende's blog: NHibernate Cascades: the different between all, all-delete-orphans and save-update

Time Series and Network Editor

Gui components

DelftShell is slowly starting to take a new shape. We're currently busy implementing blocking functionality required for:

  • Network Editor
  • Cross-section Editor
  • Time series Editor

See a couple of screenshots below to get an idea about direction of current development.

These still a lot of cosmetic changes

Persistency

One of the source of pain in the application for developers is persistancy / data access. But thanks to NHibernate it is simplified a lot, so that mapping network or time series to a database, which is currently used as a file format of a DeltShell, seems to be a relatively easy task. Actually if classes are implemented correctly, which means:

  • id field of long type is present
  • relations to other entities are well-defined
  • relations to collections use only interfaces such as IList<>, ICollection<>
  • value objects do not contain any manipulation logic such as generating data when properties are set

In this case adding mapping file as a *.hbm.xml file embedded into assembly seems to be sufficient to write/read data from a database.

We still should think about backward-compatibility of a file format. Probably use of some classes providing upgrade strategy from version to version will be sufficient.

In some exceptional cases we need to make implementation of own persistent types, e.g. mapping Geometry to database or some types such as Brush, Pen. See example here: Composite types in NHibernate.

Here how mapping of a TimeSeries / Function classes looks like. Note that currently we use DataTable-based value store for a Function which is mapped to a database using custom type which simply calls FromXml / ToXml for a DataTable. As result we're getting all data of one time series embedded into a text field. In the future it probably will be NetCDF-based store when a lot of results will be produced by a model.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping>
  <class name="DelftTools.DataObjects.Functions.Function, DelftTools.DataObjects" 
   table="function" lazy="false" discriminator-value="function">
    <id name="Id" column="id" type="Int64" unsaved-value="0" access="nosetter.camelcase">
      <generator class="native" />
    </id>
    <discriminator column="type" type="string"/>

    <property name="Name" column="name" />

    <list name="Arguments" cascade="all-delete-orphan" table="function_arguments">
      <key column="function_id"/>
      <list-index base="1" column="list_index" />
      <many-to-many class="DelftTools.DataObjects.Functions.Variable, DelftTools.DataObjects"
       column="variable_id"/>
    </list>

    <list name="Components" cascade="all-delete-orphan" table="function_components">
      <key column="function_id"/>
      <list-index base="1" column="list_index" />
      <many-to-many class="DelftTools.DataObjects.Functions.Variable, DelftTools.DataObjects" 
       column="variable_id"/>
    </list>

    <property name="Values" 
     type="DelftShell.Plugins.Data.NHibernate.DelftTools.Core.Dao.DataTableFunctionValueStoreUserType,
      DelftShell.Plugins.Data.NHibernate">
      <column name="values_xml"/>
      <column name="values_xml_schema"/>
    </property>

    <subclass name="DelftTools.DataObjects.Functions.TimeSeries, DelftTools.DataObjects" 
     discriminator-value="time_series" lazy="false" />
  </class>

  <class name="DelftTools.DataObjects.Functions.Variable, DelftTools.DataObjects" 
   table="function_variables">
    <id name="Id" column="id" type="Int64" unsaved-value="0" access="property">
      <generator class="native" />
    </id>
    <discriminator column="type" type="string"/>

    <property name="Name" column="name" />
    <property name="ValueType" column="value_type" type="Type" not-null="true"/>

    <subclass name="DelftTools.DataObjects.Functions.FunctionArgument`1[System.DateTime], DelftTools.DataObjects" 
     discriminator-value="argument_date_time" />
    <subclass name="DelftTools.DataObjects.Functions.FunctionArgument`1[System.Double], DelftTools.DataObjects" 
     discriminator-value="argument_double" />
    <subclass name="DelftTools.DataObjects.Functions.FunctionComponent`1[System.Double], DelftTools.DataObjects" 
     discriminator-value="component_double" />
  </class>
</hibernate-mapping>
Threads, BackgroundWorker and future of parallel .NET

http://www.interact-sw.co.uk/iangblog/2005/05/16/endinvokerequired

One more reason to jump to .net 3.5 somewhere in the future:

I tried to implement thread-based map rendering so that it will re-start rendering. Useful to use when rendering takes >1 sec. With low-level threads it sounds a bit more complicated, plus .NET implementation seems to be a bit unstable when you call Abort() on a worker thread, something happens with context of a thread and application becomes unusable (sad). Similar experience is explained here by Sacha Barber:

Finally I implement it using BackgroundWoker which is actually a wrapper around Thread and now it seems to work stable. Have a look on MapControl.Refresh() to see details.

Also during implementation calls to Controls via InvokeRequired attribute stopped working, application hangs, I made it work by splitting Invoke() call into InvokeBegin() and then InvokeEnd() in a separate thread. Maybe a bit costly but seems to work fine. See InvokeRequiredAttribute.cs

Here is also a nice video about future way to run parallel + worker work in a separate threads:

http://msdn.microsoft.com/en-us/concurrency/default.aspx