Archive for March 2010

Updating and restarting a singleton WCF service hosted in IIS

I recently moved a singleton WCF service to IIS that used to be hosted within a Windows service. When it was hosted in a Windows service restarting it was easy, you just go to the service manager in Windows and click restart, but to my knowledge there is no direct equivalent for a WCF service running inside IIS. Since a singleton service continues to run indefinitely once it starts I was afraid that even after publishing an update or changing the web.config the old code would continue to run until I forced a restart somehow. I didn’t want to have to restart IIS every time there is a change to the config file, so I made a test project and ran some experiments. It turns out that when any change is made to the service (ie: any of the binaries, the svc file, or the web.config are replaced) the service immediately restarts. This means that all of your existing connections will be terminated and the service class will be recreated. Unlike a Windows service you don’t have to manually force a restart when you publish an update. The bad news is that I still don’t know of a way to simply restart the service without making any changes. Aside from restarting IIS (which is obviously a terrible solution) the only way I know is to make some random negligible change to the config file (like changing a comment or maybe even just whitespace). I guess that in theory singleton services are not meant to be restarted, but you’re bound to want to do it at some point.

CruiseControl.NET: Versioning ccnet.config and integrating it into CI

Cruise Control is meant to help manage your builds based on the code in source control, and since ccnet config files are basically code, it only makes sense that they should be a part of this process. Basically, there are two problems I wanted to solve:

  • If I change ccnet.config and introduce an error, I want to be able to roll back to a previous version in SVN.
  • When multiple developers are working on ccnet.config, they should not be fighting over the same file on the CI server. Instead, each developer should be editing a file in their working copy and then merging changes into the repository.

Both of these issues are actually very easy to solve. Using SVN, you can create a repository with only one file: ccnet.config. Then add this project to both the ccnet.config in SVN and the one actually used by Cruise Control (probably in C:\Program Files\CruiseControl.NET or something like that):

  <project name="ccnet config">
    <sourcecontrol type="svn">
      <executable>C:\path\to\svn\on\CI\server\svn.exe</executable>
      <workingDirectory>C:\CI</workingDirectory>
      <trunkUrl>http://localhost/svn/myrepo/trunk</trunkUrl>
      <autoGetSource>true</autoGetSource>
      <username>SVN_USERNAME</username>
      <password>SVN_PASSWORD</password>
    </sourcecontrol>
    <tasks>
      <exec>
        <executable>C:\Windows\System32\xcopy.exe</executable>
        <buildArgs>/Y C:\CI\ccnet-config\ccnet.config "C:\Program Files\CruiseControl.NET\server"</buildArgs>
      </exec>
    </tasks>
  </project>

All this does is check out the config file and then copy it to the location used by Cruise Control. Remember to edit all the paths above with the actual ones for your setup.

That’s all you need to do! The config file is now versioned and Cruise Control is automatically updating itself every time you commit.

LINQ to SQL Gotcha #5: Column Default Values

A common pattern in database design is to use make a column required, give it a default value and then never think about it when doing INSERTs. A perfect example would be a createdDate column on the Users with a default value of GetDate(). Here’s the full table definition:

  • userID (identity key)
  • userName
  • password
  • ts (timestamp)
  • createdDate (default value = GetDate())

In this case we can easily insert into the table without worrying about the createdDate, userID, or ts columns:

INSERT INTO Users (userName, password) VALUES ('asdf', 'qwer')

However, since this is the 21st century, we don’t want to do this in SQL, we want to do it with an ORM. Unfortunately, LINQ to SQL doesn’t do a very good job with this.

Using context = New TestDataContext
 
    ' Output SQL to the console for debugging
    context.Log = Console.Out
 
    ' Attach a new user and submit the changes
    Dim newUser As New User With {.userName = "NewUser", .password = "password"}
    context.Users.InsertOnSubmit(newUser)
    context.SubmitChanges()
 
End Using

The above code generates the following INSERT statement when SubmitChanges() is called (note: I replaced @p0, @p1, etc with their actual values to make the query more readable):

INSERT INTO [dbo].[Users]([userName], [password], [createdDate]) VALUES ('NewUser', 'password', NULL)

This query fails and we get a SqlTypeException because createdDate is NOT NULL and NULL cannot be converted to a valid date. Notice that the generated SQL does not attempt to explicitly set a value for userID or ts. It appears that LINQ to SQL knows how to deal with IDENTITY fields and TIMESTAMPs, but not how to deal with other required columns that happen to have a default value.

I would have expected LINQ to SQL to generate a query that does not explicitly set createdDate so that SQL Server could handle it, but no such luck. You can easily set the createdDate manually like this:

Dim newUser As New User With {.userName = "NewUser", .password = "password", .createdDate = Date.Now}

It really sucks to have to do this every time though, especially if you have many fields to fill in. A possible alternative is to put a partial class on either your DataContext or just on the User class and write some code that will automatically initialize fields like createdDate. If you want to make generic behaviour for this (eg: automatically set columns named “createdDate” to Date.Now when SubmitChanges is called) you can do something like this in the DataContext partial class:

Public Overrides Sub SubmitChanges(ByVal failureMode As ConflictMode)
 
    ' NOTE: this is just a sample to get you started
    For Each insert In GetChangeSet().Inserts
        Dim createdDateProp = insert.GetType.GetProperty("createdDate")
        If createdDateProp IsNot Nothing Then
            createdDateProp.SetValue(insert, Date.Now, Nothing)
        End If
    Next
 
    MyBase.SubmitChanges(failureMode)
 
End Sub