2009-08-06

Spring for .NET 1.3.0 Release Candidate available

Finally we made it: The next release of Spring for .NET is available for preview and brings a couple of new things as well as over 100 fixed bugs to the table. Grab the new release as usual from our website http://www.springframework.net and give it a test run. The final release is currently scheduled for the first week in September. Below I will give you a short introduction to the new features.

New Features

NVelocity Support

Erez Mazor joined the team in June and brought a nice integration of the NVelocity template library (part of the Castle project) with him. Let's asume you need to send a confirmation email after processing T-Shirt order to a customer. Most of this email will come from a template, just a few variables will be individual for each customer. Here's how such a template looks like using the Velocity Template Language: 

Dear#if ($sex == "F") Ms#else Mr#end $recipient,
 
We are happy to withdraw $1Mio from your account

Here's an example on how to use Spring.NET's integration to send an Email from your application. The configuration causes the NVelocityFactory to load templates from the NVelocityDemo assembly's "NVelocityDemo" namespace:

<objects xmlns="http://www.springframework.net" xmlns:nv="http://www.springframework.net/nvelocity">
  <nv:engine id="velocityEngine">
    <nv:resource-loader>
      <nv:spring uri="assembly://NVelocityDemo/NVelocityDemo/"/>
    </nv:resource-loader>
  </nv:engine>
 
  <object id="emailSendService" type="NVelocityDemo.EmailSendService">
    <property name="VelocityEngine" ref="velocityEngine" />
  </object>
</objects>

Here's the C# Code of my EmailSendService class:

public class EmailSendService
{
  public VelocityEngine VelocityEngine { get; private set; }

  public void SendEmail(string recipientName, string sex, string emailAddress, string subject)
  {
    Hashtable model = new Hashtable();
    model["email"] = emailAddress;
    model["recipient"] = recipientName;
    model["sex"] = sex;
    IContext modelContext = new VelocityContext(model);
    StringWriter sw = new StringWriter();
    this.VelocityEngine.MergeTemplate("confirmationEmail.vm", Encoding.UTF8.HeaderName, modelContext, sw);

    string emailContent = sw.ToString();

    // send mail
    Console.WriteLine("Sending email to {0} with subject '{1}':\n{2}", emailAddress, subject, emailContent);
  }
}

Running this example will inform our customer about our pricing for T-Shirts:

Sending email to foo@world.com with subject 'Thank you!':
Dear Mr Smith,

we are happy to withdraw $1Mio from your account
TIBCO Enterprise Message Service

Similar to our support for MSMQ and ActiveMQ, consequently there is now also support for Tibco's EMS. If you are already familiar with Spring's ActiveMQ support, you will quickly feel comfortable. To get started, check out our reference documentation and the upcoming blog post at Mark Pollack's Blog.

MS Test

Some of us are forced to write their unit tests using Microsoft's own testing framework. To support them, Spring.NET now also integrates support for writing unit and integration tests similar to the one already available for NUnit. You can find the supporting stuff in the Spring.Testing.Microsoft assembly.

Noteworthy Improvements

Testing

In response to a lot of requests, we upgraded our NUnit testing environment to the latest NUnit 2.5.1. There is also new support for writing database integration tests. Use the SimpleAdoTestUtils class to execute arbitrary SQL scripts to setup/teardown your database. An example is Spring's NHibernate Integration tests. Here is the SQL script to recreate our integration database:

IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'Spring')
BEGIN
    ALTER DATABASE Spring 
        SET SINGLE_USER 
        WITH ROLLBACK IMMEDIATE

    DROP DATABASE Spring
END
GO

IF  EXISTS (SELECT * FROM sys.server_principals WHERE name = N'springqa2')
    DROP LOGIN [springqa2]
GO

CREATE DATABASE Spring
GO

CREATE LOGIN [springqa2] WITH PASSWORD=N'springqa2', DEFAULT_DATABASE=[Spring], DEFAULT_LANGUAGE=[us_english]
GO

USE Spring
CREATE USER [springqa2] FOR LOGIN [springqa2] WITH DEFAULT_SCHEMA=[dbo]
EXEC sp_addrolemember 'db_owner', 'springqa2'
GO

Now, leveraging NUnit's [SetupFixture] feature, we can easily execute this and other scripts before the execution of each fixture:

[SetUpFixture]
public class Setup
{
  private const string DBConnection = "Data Source=SPRINGQA;Database=$DATABASE$;Trusted_Connection=False;User ID=springqa;Password=springqa";
  private readonly IResourceLoader resourceLoader = new ConfigurableResourceLoader();

  private IResource GetResource(object instance, string name)
  {
    string resourcePath = TestResourceLoader.GetAssemblyResourceUri(instance, name);
    return resourceLoader.GetResource(resourcePath);
  }

  [SetUp]
  public void InstallMSSQLDatabase()
  {
    IDbProvider dbProvider = DbProviderFactory.GetDbProvider("SqlServer-2.0");
    AdoTemplate ado = new AdoTemplate(dbProvider);

    // (re-)create database(s)
    dbProvider.ConnectionString = DBConnection.Replace("$DATABASE$", "master");
    SimpleAdoTestUtils.ExecuteSqlScript(ado, GetResource(this, "RecreateDatabases.sql"));

    // create tables
    dbProvider.ConnectionString = DBConnection.Replace("$DATABASE$", "Spring");
    SimpleAdoTestUtils.ExecuteSqlScript(ado, GetResource(this, "Data.NHibernate/creditdebit.sql"));
    SimpleAdoTestUtils.ExecuteSqlScript(ado, GetResource(this, "Data.NHibernate/testObject.sql"));
  }
}

Using the new NVelocity integration, you could even easily parameterize your SQL scripts for different environments.

Configuration Error Handling

Previously sometimes it was hard to read configuration errors. I put some effort into reducing this pain and make the error messages much more explicit. Now you get the exact filename + linenumber of the offending object definition and also a more detailled explanation, what is causing the problem.

AOP Performance

The performance of AOP proxies at runtime improved, but also *creating* such singleton proxies at startup time could cause some issues. You now get a bunch of different Auto-Proxy processor implementations to choose from to better suite your exact needs.

Sometimes using <tx:attribute-driven /> or <aop:config> could cause issues when an additional XXXAutoProxyCreator was defined in your configuration. This roots in the fact that those 2 configuration elements silently registered their own DefaultAutoProxyCreator under the hoods. This is now taken care of and it is guaranteed that infrastructure AOP elements will not interfere with user-defined ones anymore.

Feedback welcome

The release will be in ~ 3-4 weeks, so please grab a copy of the release candidate and give it a whirl - We'd be happy to get your feedback!