January 27, 2009, 4:56 pm
Last year, Hammett posted a blog entry on combining Javascript files into a single file using Monorail. I was looking to enhance the code so that it would minify Javascript and CSS files on the fly, so I took his original code and enhanced it. Alex Henderson was also looking for the same functionality, so he contributed to the patch as well. You can download it here: http://support.castleproject.org/projects/MR/issues/view/MR-ISSUE-457
The new component uses a .NET port of the Yahoo! UI Library Compressor that is available on Codeplex. The component worked great, but they did not have a strongly signed version of the library release. I made a request and they quickly turned out a release that same day, which was awesome response time.
You can now have all of your Javascript and CSS combined for you on the fly by using the component:
1: #blockcomponent(BuildJS with "key=layout")
2: $jsBuilder.Add("Content/css/main.css")
3: $jsBuilder.Add("Content/css/someOther.css")
4: $jsBuilder.Add("Content/js/prototype.js")
5: $jsBuilder.Add("Content/js/someOther.js")
6: #end
January 27, 2009, 4:56 pm
Although there are a number of good continuous integration servers out there, TeamCity is one of my favorites. It has great support for .NET — supporting MSBuild, NAnt, Solution builds, NUnit, MSTest, and FxCop. The price is right if you have less than 20 users and 20 build configurations – free. I was also able to a plugin to integrate with our issue tracking systems in a couple of hours timespan (my Spring MVC experience definately helped though).
One of thing that you want to do with a release build is automatically increment your version numbers. Most approaches to this problem recommend using MSBuild to autogenerate the AssemblyInfo.cs file. Although that certainly works, I prefer not to have to do that for every project. Instead, each of our projects references a single CommonAssemblyInfo.cs file.
When TeamCity builds a release build, it is configured to the IncrementVersion task:
1: <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2: ...
3: <Target Name="UnrestrictedExecutionPolicy">
4: <Exec Command="powershell set-executionPolicy unrestricted"" />
5: </Target>
6: <Target Name="IncrementVersion" DependsOnTargets="UnrestrictedExecutionPolicy">
7: <Exec Command="PowerShell .\incrementVersion.ps1 $(build_vcs_number_1)" />
8: </Target>
9: ...
10: </Project>
The $(build_vcs_number_1) property tells TeamCity to the current revision number from the first source repository attached – in my case SVN — to the project (most projects will only have one repository attached). Then, we execute a simple Powershell script to replace the file version number:
1: $buildNumber = $args[0]
2: (Get-Content ..\src\CommonAssemblyInfo.cs) | Foreach-Object {$_ -replace "(\d\.\d\.\d)\.\d*", "`$1.$buildNumber"} | Set-Content ..\src\CommonAssemblyInfo.cs
If you are more comfortable using a MSBuild task for regex replacement, there is a RegexReplace task that is part of the MSBuild Community Tasks. For my build, we do not need any other custom tasks, so it was preferable to just use a simple PS script.
January 27, 2009, 4:56 pm
There are a lot of times in your application where you will load a lot of entities in a single session (to display a lot of data on a page, build a report, etc). Since you are not planning on doing a mutable operation on an entity, there is a big saving that you can get from not flushing the session. To get an idea on everything that happens when you do a flush, check out the AbstractionFlushingEventListener from the NHibernate source.
If you are using explicit SessionScope’s, not flushing the session is trivial:
1: using (new SessionScope(FlushAction.Never))
2: {
3: repository.DoSomeReadOperation();
4: }
This will execute the operation without flushing the session at the end.
Another way to control this behavior is by using an interceptor. For example, if you are using the Castle’s Automated Transaction Facility, you probably already have a way of determining whether or not your methods do mutable operations by their transaction attributes. You can then use an interceptor to control flushing:
1: public class UnitOfWorkInterceptor : IInterceptor
2: {
3: private readonly ITransactionManager transactionManager;
4:
5: /// <summary>
6: /// Initializes a new instance of the <see cref="UnitOfWorkInterceptor"/> class.
7: /// </summary>
8: /// <param name="transactionManager">The transaction manager.</param>
9: public UnitOfWorkInterceptor(ITransactionManager transactionManager)
10: {
11: this.transactionManager = transactionManager;
12: }
13:
14: #region Implementation of IInterceptor
15:
16: /// <summary>
17: /// Intercepts the specified invocation.
18: /// </summary>
19: /// <param name="invocation">The invocation.</param>
20: public void Intercept(IInvocation invocation)
21: {
22: ITransaction transaction = transactionManager.CurrentTransaction;
23: FlushAction flushAction = transaction == null ? FlushAction.Never : FlushAction.Config;
24:
25: ActiveRecordUnitOfWork.Before(flushAction);
26: invocation.Proceed();
27: ActiveRecordUnitOfWork.After();
28: }
29:
30: #endregion
31: }
Note that in this example, interceptor ordering is important. If you register the unit of work interceptor before the transaction interceptor in the ATM facility, then transactionManager.CurrentTransaction will always return null. (The ActiveRecordUnitOfWork class in the above example is just a facade for controlling the session in a thread local as discussed in this post http://erichauser.net/2008/08/06/activerecord-session-scope-and-wcf-redux/). If you do not want to tie your flushing to transactions, then you can always create your own metadata and read that metadata at runtime in the interceptor. The ATM facility code is a great example of how to inspect metadata on initialization and use that metadata in your interceptor.
Hopefully, this little trick will some increased performance for your queries that return a lot of results.