2010/01/21

Working with XML in PowerShell (2)

In a previous post I wrote about examining XML with PowerShell. However, it is often not only necessary to peek into XML, but also to change it.

Updating XML with NAnt

In NAnt, it is possible to use xmlpoke to update XML in arbitrary files. The following NAnt snippet is taking from the build script of ActiveRecord’s test project and allows to adjust the database settings of the build machine.

  <target name="configure-tests">
    <property name="app.config" value="${build.dir}/${project::get-name()}.dll.config" />
    <xmlpoke
      file="${app.config}"
      xpath="/configuration/activerecord/config/add[@key='connection.connection_string']/@value"
      value="${ar.connection.connection_string.1}" 
    />
  </target>

In the snippet above I have omitted some 10 more xmlpoke tasks that all change values in the same application configuration file.

How can this be done in PowerShell? The preceding post in this series showed how to assign the output of a command to a variable, so we only have to use a command that reads the contents of a file and assign the output to the variable:

    $app_config = $project_path + "\build\net-3.5\debug\Castle.ActiveRecord.Tests.dll.config"
    [xml] $cfg = Get-Content $app_config

Now we have the XML to update available as an instance of XmlDocument and the full power of .NET at our hands to manipulate it. When we just had to traverse a path to change our XML, we could simply use dotted path. But if we have to differentiate siblings by its attribute values, the dotted path becomes unwieldy as we had to use a for-each-loop. But we can use XPath instead:

    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='connection.connection_string']").value = $myDatabaseConnString
    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='dialect']").value = $myDatabaseDialect
    # ...

Finally, we have to save the updated XML. There is one caveat here: XmlDocuments don’t keep whitespace but normalize where it is deemed to be safe. But if we change generated files instead of manually edited ones, this is only a minor nuisance.

    $cfg.Save($app_config)   

The full script is shown below. As before it is quite short compared to the NAnt XML script.

properties {
    [string] $project_path = "C:\dev\castle\SVN\ActiveRecord"
    $myDatabaseConnString = "PSDBCFG"
    $myDatabaseDialect = "PSDBDIALECT"
}

task default -depends configure_tests

task configure_tests {
    $app_config = $project_path + "\build\net-3.5\debug\Castle.ActiveRecord.Tests.dll.config"
    [xml] $cfg = Get-Content $app_config
    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='connection.connection_string']").value = $myDatabaseConnString
    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='dialect']").value = $myDatabaseDialect
    # ...
    $cfg.Save($app_config)   
}

2010/01/20

Working with XML in PowerShell (1)

During the last days, I started reading about PowerShell and PSake. While looking at the NAnt scripts that I currently use, I recognized that among others the capability of examining and editing XML files is required.

Examining XML in NAnt

In NAnt, inspecting XML is done with the xmlpeek-task. It takes a file name, an XPath and a property name to fill with the result.

One of the uses of xmlpeek is to find out the current revision of a code base, for example to set the private part of the version number with it. In the Castle Project’s build script this is done this way:

  <target name="common.find-svninfo">
    <!-- For adding SVN revision to builds -->
    <property name="svn.revision" value="0" overwrite="false" />
    <!-- try to update the revision -->
    <exec
      program="svn"
      commandline='info "${project::get-base-directory()}" --xml'
      output="_revision.xml"
      failonerror="false"/>
    <xmlpeek
      file="_revision.xml"
      xpath="/info/entry/@revision"
      property="svn.revision"
      failonerror="false"/>
    <delete file="_revision.xml" failonerror="false" />
    <echo message="INFO: Using Subversion revision number: ${svn.revision}"/>
  </target>

So while this is quite a clever solution to find out the projects current revision, it has some drawbacks, such as being nearly 20 lines of code and that it uses a temporary file. But it does the job, reading the revision from the project folder.

Examining XML in PowerShell

PowerShell treats XML as a first class type and also has some shortcuts available. If we take a look at the result of svn info, we see that it is thankfully simple:

<?xml version="1.0"?>
<info>
  <entry
     kind="dir"
     path="."
     revision="6536">
...
  </entry>
</info>

And because we can traverse XML nodes and attributes in PowerShell like object properties, it is possible to get the revision number even without an XPath expression.

$revision = $rev_xml.info.entry.revision

So now it is only necessary to fill the $rev_xml variable with the output of the svn info command. The NAnt script does this using a temporary file but PowerShell allows to assign the standard output of a command line program directly to a variable:

$rev_xml = svn.exe info --xml 

But PowerShell treats the output of such a program as an array of objects, in this case strings. We have to hint PowerShell that what we want is an XMLDocument instead of an array of strings: 

[xml] $rev_xml = svn.exe info --xml 

That’s all. We now only have to put this together into a PSake build script for testing and reference.

properties {
    [string] $project_path = "C:\dev\castle\SVN\ActiveRecord"
    $revision = 0
}

task default -depends get_revision
task get_revision {
    [xml] $rev_xml = svn.exe info $project_path --xml 
    $revision = $rev_xml.info.entry.revision
    "INFO: Using Subversion revision number: $revision"
}

This whole script is shorter than the snippet of NAnt script I’ve shown at the beginning. Taking only the relevant work, 5 lines of PS script replace 15 lines of NAnt XML.

The script of course ignores the fact that getting the revision number should not be a task but rather a function that is called by a build task’s precondition for example. I will get back this in a while in another post.

2010/01/14

ActiveRecord 2.1 released

Yesterday was ActiveRecord 2.1 released. The binaries can be downloaded on SourceForge.

Most of the new functionalities in this release were contributed by Krzysztof Kozmic, a fellow castle committer, who was a great help to get this release out. More thanks go out to the HornGet team, whose efforts took a lot of work out of the release process. Finally, I want to thank all contributors for patches and spotting bugs.

So now what is new in that release?

Updated Dependencies

Castle.Core and Castle.DynamicProxy were updated to their newest releases (1.2 and 2.2 resp.).

NHibernate has been updated to the last stable version 2.1.4 and compiled against the new Castle libraries.

NHibernate.Linq and NHibernate.Search were compiled against NHibernate 2.1.4.

Default configuration feature

This new feature allows to skip all of the <add/>-tags from your configuration if you are only using the database’s default values. In this case your config will be reduced to:

<activerecord>
    <config db="MsSqlServer2005" csn="MyConnectionStringName"/>
</activerecord>

The default configurations are available for most databases.

Inferring PrimaryKeyType from property type

Inspired by FluentNH, ActiveRecord now infers the PrimaryKeyType from the key’s property type:

  • For Guid PrimaryKeyType.GuidComb is used
  • For string PrimaryKeyType.Assigned is used
  • All others default to PrimaryKeyType.Native as before.

Support for backfield and readonly properties

These property accessors are now available in ActiveRecord. Especially readonly is interesting, it allows to store values computed by the entities in the database and is therefore the complement to a computed table column.

2009/08/01

ActiveRecord 2.0

Finally it’s final. I just created the ActiveRecord 2.0 zipfile.

It can be downloaded at SourceForge. This release includes the NHLinq 1.0 release.

Have Fun!

2009/07/29

ActiveRecord Fluent Configuration

Earlier this morning I committed a first spike of code to the actual (post 2.0) trunk of ActiveRecord. This fluent configuration shows a lot of features I am planning for 2.x releases.

Let’s take a look at the new syntax. This one is for ActiveRecord itself and mere syntactic sugar:

    1 IActiveRecordConfiguration configuration = Configure.ActiveRecord

    2     .ForWeb()

    3     .Flush(DefaultFlushType.Leave)

    4     .UseThreadScopeInfo<SampleThreadScopeInfo>()

    5     .UseSessionFactoryHolder<SampleSessionFactoryHolder>()

    6     .MakeLazyByDefault()

    7     .VerifyModels()

    8     .RegisterSearch();

The following code is however more interesting. It does not only show how AR is currently configured, but also which features will become available soon:

    1 IStorageConfiguration configuration = Configure.Storage

    2     .For

    3         .AllOtherTypes()

    4         .MappedBy(new XmlNhibernateMapping()

    5             .InAssemblyOf<OneOfMyEntities>())

    6     .As

    7         .ConnectionStringName("a_string")

    8         .Driver<SqlClientDriver>()

    9         .ConnectionProvider<DriverConnectionProvider>()

   10         .Dialect<MsSql2005Dialect>()

   11         .ProxiedBy<ProxyFactoryFactory>()

   12         .ShowSql();

The first thing to note is the interface IStorageConfiguration. Each storage configuration will be used to create a NHibernate SessionFactory.

The storage configuration consists of two parts: The database configuration is syntactic sugar for the current NHibernate configuration strings, which are a pain in the neck especially if you need to declare types.

The more interesting part is the storage type selection. The part begins in line 2 by using the For-property. The storage type selection allows you to specify which types should use the session factory created by the storage configuration and how they are mapped.

The selection above contains all classes that are not explicitly added to another storage configuration (line 3) and it says that these types are mapped using classic NHibernate mapping files (line 4) which should be loaded from a given assembly (line 5).

Line 6 finally switches back to the storage configuration.

Storage Type Selections

The storage type selection shown above is effectively a specification pattern for types. Its existence shows to consequences for future versions of ActiveRecord:

  1. You can distribute your classes among multiple databases without the need of a common abstract baseclass. It will be possible to select the types for a storage by namespace, assembly, supertype, interfaces implemented or simply by enumerating them in the storage type selection.
  2. ActiveRecord attributes will be only one option to map persistent types. Types mapped by HBM files or Fluent NHibernate will become first class citizens of ActiveRecord.

The specification patternfor storage type selection allows more complex combinations than currently shown. Here is another example:

    1 IStorageConfiguration auditConfig = Configure.Storage

    2     .For

    3         .SubtypesOf<AuditType>()

    4         .InNamespaceOf<DefaultAuditorType>() // logical and

    5         .MappedBy(new FluentNHibernateMapping()

    6             .InAssemblyOf<MyMappingClass>())

    7     .And // Logical or - we start the next StorageTypeSelection

    8         .TypesInAssemblyOf<MessagingImpl>()

    9         // No MappedBy defaults to ActiveRecord attributes

   10     .As

   11         .DefaultsFor<MsSqlServer2000Configuration>()

   12         .ConnectionString("server=bla;...");

Multiple selections can be created by using the And-property (line 7). Since the storage configuration holds an arbitrary number of storage type selections, this allows for a logical-or-construct. It also allows mixing different mapping types. In the example the first selection is mapped with Fluent Nhibernate (lines 5-6) and the second one with ActiveRecord attributes (line 9).

It is also possible to narrow down type selections by adding multiple specifications to a single type selection (lines 3 and 4). This means that only types are selected that satisfy all specifications. In the example only types are selected that inherit from AuditType and are in the same namespace as DefaultAuditorType.

Storage Configuration

The storage configuration (where the database is configured) is not yet as clearly defined as the storage type selection.

But it will contain default values for most database systems, a templating mechanism and special subconfigurations for fluently defining behaviour needed only occasionally, such as NHibernate Search configurations.

Does this work yet?

No. I just implemented enough to compile and run some simple tests. The features shown above will go into AR 2.1 and AR 2.2.

This article is mainly a showcase for the planned functionality. Suggestions, critic and patches are always welcome.

2009/07/21

ActiveRecord Release Candidate

 

RC 1 of ActiveRecord 2.0 is now available on SourceForge.

Unless serious bugs are found within, this version will be released as ActiveRecord 2.0 RTM on August, 1st.