NHibernate and xmlpoke


Some time ago I wrote about targeting multiple environments through NAnt.  The basic concept is to use the xmlpoke task in NAnt to modify any XML configuration files your application might use.  One setting that changes in each deployment we have is the “connection.connection_string” setting in the NHibernate hibernate.cfg.xml file.  This setting controls the connection string NHibernate uses to connect to the database.

Unfortunately, I ran into some annoying problems that caused me a lot of frustration.  When I tried to use the xmlpoke task:

<xmlpoke file="${dir.website}hibernate.cfg.xml"
 xpath="//*/property[@name='connection.connection_string']"
 value="Data Source=${database.server};Initial Catalog=${database.name};Integrated Security=true">
</xmlpoke>

I kept getting this from NAnt:

[xmlpoke] No matching nodes were found with XPath expression '//*/property[@name='connection.connection_string']'.

I double, triple, and eleventy-tuple-checked the XPath and tried many other XPath combinations.  But the problem wasn’t with the XPath, it was with the hibernate.cfg.xml file.  Looking at the top at the NHibernate configuration file, I saw that the root element had an XML namespace applied to it:

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0">

I’ve used XML namespaces in the past to add prefixes to element names.  The xmlpoke documentation mentions namespaces, and this post confirmed it: I have to specify a namespace in the xmlpoke task for the XPath query to work correctly.

Armed with this, I was able to get my xmlpoke task working correctly:

<xmlpoke file="${dir.website}hibernate.cfg.xml"
         xpath="//*/hbm:property[@name='connection.connection_string']"
     value="Data Source=${database.server};Initial Catalog=${database.name};Integrated Security=true">
  <namespaces>
    <namespace prefix="hbm" uri="urn:nhibernate-configuration-2.2" />
  </namespaces>
</xmlpoke>

I had to do few things to get it to work:

  • Add the namespace element with the correct uri attribute
  • Give it a prefix, “foo” if you want, it doesn’t matter
  • Changed the XPath to use the prefix specified earlier on each element in the query

Now the XPath works and my property is changed correctly.  Even though I don’t specify a prefix in the namespace in the configuration file, I still have to declare the prefix in the xmlpoke task.  I’m sure there’s some smarty-pants XML guru that could tell me the details, but all I care is that my build is deploying the correct connection strings.

One less future headache for me.

Last XML serializer I’ll ever write