Psake Builds and TeamCity Integration

Psake is a powerful build system, build on Powershell for a lot of functionality and familiarity. And TeamCity is a really nice continuous integration server, checkout the CodeBetter TeamCity install for a good demo. Hooking the two up was not a easy as using just Msbuild or Nant, but it’s not really too bad.

First, your Psake script needs to check the exit codes of the applications it calls. This is important to do regardless because Powershell doesn’t error out based on the exit code. The trunk version of Psake has a convenience function to execute a command and check it’s exit code is greater than 0, called exec:

You can also pass a message as the second argument.

With this checking in place, next is a bat script to use from the TeamCity command line build option.

@echo off
powershell -NoProfile -ExecutionPolicy unrestricted -Command "& {Import-Module ‘.\tools\psake\psake.psm1’; invoke-psake -t %1; if ($Error -ne ‘’) {write-host "ERROR: $error" -fore RED; exit $error.Count} }"`

This executes a Powershell prompt which imports the Psake module and then calls the provided Psake task. Finally, it checks the $Error variable and sets the exit code to the number of errors returned by the script. This will fail the TeamCity build and allows any error thrown by Powershell to break the build.

Last is to have TeamCity run the bat file with the desired target.

psake.bat Compile

One thing to note is that some things return exit code other then 0 but that are not errors. In that case you can check the Powershell variable $LastExitCode and throw errors based on that.

Thanks to the psake users group for all their help!


error CS1668: Warning as Error: Invalid search path ‘C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib’ specified in ‘LIB environment variable’

I’m not sure what caused my setup to produce this error, but after searching for a while and not finding any reason, I just added the lib directory and all was good. I was getting this when doing command line compiles and when using the spark view engine. I hope this might save me or someone else some time in the future.

error CS1668: Warning as Error: Invalid search path ‘C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib’ specified in ‘LIB environment variable’

A New Year & A New Venture: Red27 Consulting

business-card-frontI’ve decided to take the plunge and start consulting full time. My company, Red27 Consulting, has been around for few years, but this year I will be pursuing consulting/independent development work full time. Red27 will be an agile/alt.net/craftsmanship based business, building websites and web applications. We will partner with customers to build what they need quickly and with high quality. The challenge is how to show the value in these agile/alt.net/craftsmanship processes and principles to people not in software development. I believe, over time, using these processes and principles will prove to be a better way to build software. I will write about these experiences here along with the technical content that I’ve been writing.

Lastly, keep Red27 Consulting in mind when someone has a website or web application they want built!


Making Sure A NHibernate IInterceptor Is In The Session

Building on the previous post Intercepting NHibernate to Handle Additional Database Work, I used the ILifecycle interface to ensure the IInterceptor was setup on the session before saving MyEntity. I ILifecycle interface is deprecated, but I needed some way to make sure the interceptor was there. If an entity has the ILifecycle interface nothing more is needed, NHibernate will call the methods. I used the OnSave method, to inspect the session instance and throw and exception if needed. Anyway, here’s the code:

public class MyEntity : ILifecycle
{
    public virtual int Id { get; set; }
    public virtual int ParentId { get; set; }
  
    public virtual LifecycleVeto OnSave(ISession s)
    {
        var sessionDelegate = s as SessionDelegate;
        var session = sessionDelegate != null ? sessionDelegate.InnerSession as SessionImpl : s as SessionImpl;
  
        if (session != null && session.Interceptor.GetType().Equals(typeof(MyEntityInterceptor)))
            return LifecycleVeto.NoVeto;
  
        throw new ApplicationException("MyEntityInterceptor needs to be registered with the container.");
    }
  
    public virtual LifecycleVeto OnUpdate(ISession s)
    {
        return LifecycleVeto.NoVeto;
    }
  
    public virtual LifecycleVeto OnDelete(ISession s)
    {
        return LifecycleVeto.NoVeto;
    }
  
    public virtual void OnLoad(ISession s, object id)
    {
    }
}

I think there are potentially better ways to handle this, but with the existing constraints on the code and time work something out, this was the way I went.


Intercepting NHibernate to Handle Additional Database Work

Today I needed to take addition database action when an entity is saved or deleted. A trigger might have been an option, but I try to limit the use of triggers. NHibernate to the rescue! Since the code was already using NHibernate it was an easy choice. There are a couple of options here. First is to use a stored procedure for the insert. However, that requires not using identity columns and wouldn’t work in this case. There is the ILifecycle interface, but it is deprecated and doesn’t have after-save hooks. Which leaves the IInterceptor interface.

Sub-classing EmptyInterceptor, I overrode the needed methods. If the entity is the one I’m concerned with, I store it or it’s data for later use in a queue collection. In the PostFlush method the entities stored in the queue will have their Identity set and can be used to set the properties for the additional work. Also, the deleted object will have been deleted which is important. I’m not sure I needed lock the queue objects, but I guess there is a change the session could be used on multiple threads and to be save they are locked.

The named queries are stored in the MyEntity Mapping file using the sql-query element.

public class MyEntityInterceptor : EmptyInterceptor
{
    private ISession session; 
    private readonly Queue<MyEntity> saves = new Queue<MyEntity>();
    private readonly Queue<int> deletes = new Queue<int>(); 
  
    public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, global::NHibernate.Type.IType[] types)
    {
        var myEntity = entity as MyEntity;
  
        if (myEntity != null)
            saves.Enqueue(myEntity);
  
        return true;
    }
  
    public override void OnDelete(object entity, object id, object[] state, string[] propertyNames, global::NHibernate.Type.IType[] types)
    {
        var myEntity = entity as MyEntity;
  
        if (myEntity != null)
            lock (deletes) 
                if (!deletes.Contains(myEntity.ParentId))
                    deletes.Enqueue(myEntity.ParentId); 
    }
      
    public override void PostFlush(ICollection entities)
    {
        ExecuteSaves();
        ExecuteDeletes();
    }
      
    private void ExecuteSaves()
    {
        lock (saves) 
            while (saves.Count > 0) 
                {
                    var myEntity = saves.Dequeue();

                    session.GetNamedQuery("SqlToExecuteForSaves") 
                        .SetInt32("Id", myEntity.Id)
                        .ExecuteUpdate();
                }
    }
          
    private void ExecuteDeletes()
    {
        lock (deletes)
            while (deletes.Count > 0) 
                {
                    var id = deletes.Dequeue();

                    session.GetNamedQuery("SqlToExecuteForDeletes") 
                        .SetInt32("Id", id)
                        .ExecuteUpdate();
                }
        }
  
    public override void SetSession(ISession session)
    {
        session = session;
    }
}
              
public class MyEntity
{
    public int ParentId;
    public int Id;
}

Since it’s using the ISession and IQuery interface, unit testing was easy.

The code is also using the NHibernateFacility from the CastleProject. Which makes adding interceptors as easy as adding them to the container. The naming is important as that’s how the facility looks up the interceptors. The first will be used for all session factories unless there is on for the specific factory as the second option shows.

  container.Register(Component.For<IInterceptor>()
      .ImplementedBy<MyEntityInterceptor>()
      .Named("nhibernate.session.interceptor"));

  container.Register(Component.For<IInterceptor>()
      .ImplementedBy<MyEntityInterceptor>()
      .Named("nhibernate.session.interceptor.MyOtherFactoryAlias"));

N2CMS Build From Source

image

  1. Download source
  2. Run Prepare_Dependencies-vs2008.bat
  3. Deploy_Everything-vs2008.bat
  4. Copy the output/Templates_Mvc directory to a new directory for the projects
  5. Edit permissions on the wwwroot sub folder to add IIS_USERS if needed.
  6. x64 only: download SQLite and copy the x64 version of System.Data.SQLite.dll to wwwroot\bin
  7. Follow the instructions in the install.txt

Brutalist Framework