I recently started work on upgrading a BizTalk 2006 R2 application to BizTalk 2010. I upgraded the solution to VS 2010, deployed the application to my local BizTalk and verified everything was working.
The next step was to start work on automating the build and deployment process. A perfect opportunity to leverage the TFS and MSBuild support introduced in BizTalk 2009.
In order to build a BizTalk 2010 project, I need to install the BizTalk Project Build Components on the TFS build agent. Unfortunately, the client was using Windows Server 2003 R2 for the existing TFS 2008 Build Agent (BizTalk 2010 requires Windows Server 2008 and above.)
To work around this, we installed TFS 2008 Build on the BizTalk 2010 Development machine and added it as an Agent to the TFS server. (This is a short-term work-around until the client creates a dedicated VM for this purpose.)
I set-up my build definition and got the build working. Hooray!
I then used BTSTask to write a simple batch file to Create the Application, Add the Resource and Import Bindings.
The next time I ran the same build, it failed with the following error: “SGEN : error : Cannot generate serialization assembly because it already exists. Use /force to force an overwrite of the existing assembly.”.
|Target “GenerateSerializationAssemblies” in file “C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets” from project “C:\tfsbuilds\BizTalk2010\Sources\MyBizTalkProject.btproj”:
Building target “GenerateSerializationAssemblies” completely.
Output file “obj\Release\MyBizTalkProject.XmlSerializers.dll” does not exist.
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\sgen.exe
Microsoft (R) Xml Serialization support utility
[Microsoft (R) .NET Framework, Version 2.0.50727.3038] Copyright (C) Microsoft Corporation. All rights reserved.
SGEN : error : Cannot generate serialization assembly C:\tfsbuilds\BizTalk2010\Sources\MyBizTalkProject.XmlSerializers.dll because it already exists. Use /force to force an overwrite of the existing assembly. If you would like more help, please type “sgen /?”.
The command exited with code 1.
Done executing task “SGen” — FAILED.
I was somewhat puzzled why the build failed when I haven’t made any changes to the source code.
The MSDN docs for SGEN provides the following info…
The XML Serializer Generator creates an XML serialization assembly for types in a specified assembly in order to improve the startup performance of a XmlSerializer when it serializes or deserializes objects of the specified types.
Since this appears to be a performance optimization step, my initial approach was to disable Generate serialization assembly by changing it from Auto to Off in the BizTalk project properties (build tab.)
This setting had no affect on the build and did not resolve the build error. I took a break from resolving the build error and worked on automating the deployment and un-deployment of the Biztalk application.
Around this time, I noticed that the build succeeded only when the BizTalk application was not deployed!
Looking into the issue more, I noticed that the error was due to my BizTalk project assembly being in the GAC. This was mentioned as an issue in the following MSDN forum post and I verified that the build failed when the assembly was in C:\Windows\assembly and succeeded when it was removed.
So if SGEN does not like the assembly being in the GAC, I will just modify the build script to remove it from the GAC before compilation and add it back in afterwards. Messy, but a necessary step due to the TFS Build Agent residing on the DEV BizTalk Server.
To implement this, I started by adding the Un-GAC step to the build script…
<Target Name=“BeforeCompile“ Condition=“‘$(IsDesktopBuild)’!=’true’“>
<Exec Command=“gacutil /u MyBizTalkProject“ WorkingDirectory=“C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin“/>
Around the same time, I resumed working on the deployment process – now using the BizTalk Deployment Framework (BTDF). At this point, I deployed a new version of the BizTalk application using the server MSI provided by BTDF.
The next build succeeded and I wanted to verify that my build customization was working properly. When I examined the build results, I noticed that it was not finding the assembly in the GAC:
Target “BeforeCompile” in file “…\BuildType\TFSBuild.proj”:
Using “Exec” task from assembly “Microsoft.Build.Tasks.v3.5, Version=188.8.131.52, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”.
gacutil /u MyBizTalkProject
Microsoft (R) .NET Global Assembly Cache Utility. Version 3.5.30729.1
Copyright (c) Microsoft Corporation. All rights reserved.
No assemblies found matching: MyBizTalkProject
Number of assemblies uninstalled = 0
Number of failures = 0
Done executing task “Exec”.
Done building target “BeforeCompile” in project “TFSBuild.proj”.
I was a little confused – I was sure that assembly was deployed to the GAC as the application was up and running in BizTalk. Looking into this a bit more, I realized what was going on…
In .NET, assemblies compiled targeting the 4.0 CLR are now deployed to
whereas assemblies targeting the 2.0 CLR (which includes .NET 2.0, 3.0 and 3.5) are kept in
This was a BizTalk 2010 project and I verified that .NET 4 is the target framework – so it should be using the .NET 4.0 GAC. Why were my assemblies being deployed to the .NET 2.0 GAC earlier (C:\Windows\assembly)?
It was at this point that I realized that I forgot to configure the TFS Build Agent to build using the .NET 4 compiler. This was confirmed by checking the build log…
Project file contains ToolsVersion=”4.0″, which is not supported by this version of MSBuild. Treating the project as if it had ToolsVersion=”3.5″.
Building with tools version “3.5”.
TFS 2008 Build Agent was compiling it using the .NET 3.5 compiler. And when I deployed using BTSTask, I was deploying a .NET 3.5 assembly to the GAC and the 3.5 version of SGEN was generating an error during the next build.
When I switched over to using BTDF, it was compiled on my desktop as a .NET 4.0 MSI and was deployed the assembly on the DEV server to the 4.0 GAC. Since the .NET 3.5 tools and utilities cannot see the 4.0 GAC, SGEN does not generate an error during the build and gacutil is not able to find the assembly.
Side Note: the .NET 4 gacutil searches both the v4 and v2 GACs
I incorrectly assumed that it was compiled as .NET 4 since the TFS builds were succeeding and the build server already had the v4 framework and SDK installed.
Old BizTalk habits die hard and after working with BizTalk 2004-2009 for so long, I didn’t notice anything wrong when the assemblies were in the old GAC (C:\Windows\assembly).
The following table provides an overview of when my builds were failing.
|Assembly in which GAC?||MSBuild and SGEN version||Build Result|
Modify the TFS Build Agent to default to the .NET 4 MSBuild. This is easily done by…
1. Stopping the Visual Studio Team Foundation Build service
net stop VSTFBUILD
2. Edit the following file:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\tfsbuildservice.exe.config
and modify the following setting…
Set this value to the full path to the directory of MSBuild.exe to use
a location other than the default. This should only be needed if a new
version of the .NET Framework is installed. –>
<add key=“MSBuildPath“ value=“C:\Windows\Microsoft.NET\Framework\v4.0.30319“ />
Note: the build will fail with the following error if you try to point to the X64 version of MSBuild:
MSBUILD : error MSB1021: Cannot create an instance of the logger. Could not load file or assembly ‘file:///C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.Build.Server.Logger.dll’ or one of its dependencies. An attempt was made to load a program with an incorrect format.
3. Start the build service
net start VSTFBUILD
Now that TFS Build is using the proper MSBuild version, the BizTalk assembly is being compiled as a v4 CLR image.
The last step is to modify the build script to reference the correct version of gacutil in order to GAC and un-GAC the assemblies. If you aren’t paying careful attention, you may not realize which .NET version tools you are using. Compounding this problem is the subtle difference in the file paths. For example, these are the directories for the .NET 3.5 tools (depending on whether you want to run the 32-bit or 64-bit versions):
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64
These are the directories for the .NET 4.0 tools:
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\x64
Going back to my build script, changing the Working Directory of the Exec task completes the last bit of changes.
<Target Name=“BeforeCompile” Condition=“‘$(IsDesktopBuild)’!=’true’“>
<Exec Command=“gacutil /u MyBizTalkProject” WorkingDirectory=“C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools“/>
1. Just because a build succeeded on the first try, doesn’t mean you shouldn’t examine the text log. If I did that early on, I would have seen countless references to .NET 3.5. It is very easy to see the green success icon in the Build Results page and quickly move to the next task.
2. Don’t use TFS Build on the same server as BizTalk. Having to UnGAC an assembly during a build is a pain and it disrupts BizTalk processing.
3. Try to unlearn old habits – such as jumping to C:\Windows\assembly to look for an assembly in the GAC.