Calling xsd.exe in VS 2013 Build Event

July 24, 2014

While working on an XML project, I wanted to call xsd.exe on an .xsd file during the build process, and found this solution on SO, which works for VS 2010.

For VS 2013, the solution did not work anymore, especially on systems that had no prior version of VS installed, since xsd.exe hides in a different location.

A comment to the answer illustrated how to query the registry correctly on x64 systems.

So my modified pre-build event looks like this:

call "$(ProjectDir)GenerateFromVSPrompt.cmd"
  "$(ProjectDir)"
  "$([MSBuild]::GetRegistryValueFromView(
    'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1A',
    'InstallationFolder', null, RegistryView.Registry64, RegistryView.Registry32)
    )bin\NETFX 4.5.1 Tools\xsd.exe"

all in 1 line.

If you use TFS as source control, you know that generated files need to be checked out before they can be overwritten.

I already wrote about TFS and code generation, and used the vcvarsall.bat then.

However, since we just need the path to tf.exe, and use the same VS version, we can just open a VS Command Prompt, run

where tf

and get the answer

C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\TF.exe

for VS 2013.

So our batch file GenerateFromVSPrompt.cmd looks like this:

set tf="C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\TF.exe"
%tf% checkout %1MyXsdClasses.cs
call %1XSDBuilder.cmd %1 %2
%tf% checkin /comment:"build event" /noprompt %1MyXsdClasses.cs
exit 0

In case tf.exe cannot check in the file because it did not change during code generation, it will return with exit code 1 which in turn will cause the build process to issue a build error and break. So we use exit 0 to clear the error condition

Finally, my version of XSDBuilder.cmd is based on an SO answer, but stripped down to only what is necessary, since I only have 2 XSD files, AND they need to be processed together:

pushd %1
%2 MyXsd1.xsd MyXsd2.xsd /c /n:My.Project.Xsd
popd

and, as I write, I really should merge both .cmd files into one … 😉

The build event is now executed correctly from VS, the VS Command Prompt, and on the build server.


Handling Web.Config Variants in Source Control-based Solutions

April 24, 2013

If several developers work on the same projects or solutions, sooner or later you need to manage the .config files of these developers (e.g. for local databases vs. team database, local log files and directories, and so on).

Of course, if you use a source control system, such as TFS or SVN, you don’t want the personal settings of one developer to overwrite the settings of another one.

Separate SCS config files

Usually my approach is to not check in the app.config or web.config files, but instead create copies of these files as config templates (e.g. web.tfs.config, app.svn.config) and have these copies under source control.

Recently I saw a big obstacle to this approach, and it is called TFS Team Build, an implementation of Continuous Integration.

I definitely wanted to avoid having to check out the config files, rename them so as to match the CI backend, build, and then revoke the check out.

User-specific config files

Next try, have app.config and web.config in separate sub-directories of each project, and simply copy the developer-specific app.config to app root before compilation.

While this works for app.config, it does not work for web.config in cases that the web.config contains an <authentication> element. Then the error is raised

Error 3 It is an error to use a section registered as allowDefinition=’MachineToApplication’ beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.

(Yes, I encountered this error message before, and in that case you should be able to solve the problem by running Clean on the project or the solution)

Include user-specific config files using configSource

Next try, move all developer-specific (or installation-specific) config sections to separate .config files and include these .config section files using configSource.

When you’re done, your web.config will look something like this:

<configuration>
  <configSections>
  ...
  </configSections>
  <log4net configSource="Config\log4net.config" />
  <appSettings configSource="Config\appSettings.config" />
  <connectionStrings configSource="Config\connectionStrings.config" />
  <system.serviceModel>
    ...
    <client configSource="Config\system.serviceModel.client.config" />
  </system.serviceModel>
</configuration>

Copy the original contents of the web.config to the new files in the Config directory, e.g.:

<?xml version="1.0"?>
<connectionStrings>
    <add name="default" 
        connectionString="Data Source=localhost;Initial Catalog=cat;Persist Security Info=True;User ID=userid;Password=pwd" 
        providerName="System.Data.SqlClient" />
</connectionStrings>

The Config directories then further contains one directory for each developer or installation environment.

To activate one of these configurations, simply copy the config files of one directory into the Config directory, but do not check-in the changes in the Config directory itself.

You can also write a batch file to perform the copy statements and invoke the .cmd from Solution Explorer.

The only drawback I see with this solution is that you lose Intellisense with these partial config section files.

 


Typescript Projects in TFS

November 19, 2012

If you develop Typescript applications in projects using TFS as source code control, you need to take precautions to build or publish your web application.

As the .ts and .js files are related in a VS project (.csproj) in the form

<TypeScriptCompile Include="Scripts\Index.ts" />
<Content Include="Scripts\Index.js">
  <DependentUpon>Index.ts</DependentUpon>
</Content>

when you check-out the .ts file, the .js is automatically checked out. The DependentUpon clause is also responsible for displaying the two files as parent-child nodes in Solution Explorer.

However if you build and the .ts file has not been checked out, the Typescript compiler is unable to generate the .js file, as it is set read-only in the file system.

I know of two solutions to this problem:

The first (and probably simpler one) is to include the .ts file in TFS, but NOT the .js file. This is achieved by adding the .ts (and automatically, the .js) file in TFS, but reverting the Add operation for the .js file.

The other solution is similar to the one I sketched about 3 years ago, namely to run tf checkout before and tf checkin after invoking tsc.

By the way, you can ignore the .d.ts (definition) files in this scenario, as they do not generate .js files.


Entity Designer opens edmx file only once, and other VS bugs

November 6, 2009

I found that Entity Designer in Visual Studio 2008 has some strange bugs.

→ One issue is that the installation of Visual Studio can open an .edmx file only once. Double-clicking on the edmx in the Solution Explorer after closing the file will simply expand and collapse the edmx node, but the file does not open. (Strangle, a fellow programmer never experienced this behavior on his machine).

But, I’m not alone, and somebody else already described a solution for the bug:

Right-click the edmx file, choose Open with… and select XML Editor to open the file. Close it, then the Entity Designer will open the next time (once).

→ Another issue is that VS freezes if you open the edmx in Entity Designer and navigate through it when the file is not checked out from TFS.

→ And then I wonder why, as Entity Designer implements auto-routing of foreign keys, it sometimes loses some connection points, and the connections/relations end somewhere in the diagram, instead of ending at the referencing entities, and the lines are criss-cross instead of horizontal or vertical.

→ And if you delete an entity from the diagram, the underlying object still remains in the edmx, and there is no way the Designer allows you to add the deleted entity. You either have to re-create it manually, which is cumbersome.

The solution I found was to rename the object in the database, execute Update Model so that Designer drops the object, rename the object back to its original name, and Update Model again.

→ And if you create a function mapping for a stored procedure, and define the return type as any of the entities, then the stored procedure needs to return a SELECT with the *mapped column names* instead of the original datamodel column names. In my opinion, this behavior breaks the concept of “mapping”.

→ And if you create a function mapping for a stored procedure with a SELECT that is not mappable to an entity, or returns no result or an integer result, you won’t find the mapped function in your C# code, because the generator does not create any code for it. The same is true for user defined SQL functions.

(Actually I found a solution for the missing stored procedure code generation, which I will post here soon)

Some of these issues can also be found on Connect. If you have a solution, please let me know.

Certain things will always remain a mystery to me 😉


Shared aspx and ascx Files in Visual Studio and TFS

November 5, 2009

In a Visual Studio (2008) project, we have two web applications that should share some aspx pages and ascx controls.

I found this tutorial Creating and Using User Control Libraries, which works in principal, but has left TFS integration out of the solution.

You start with two (or more) applications which have common elements. First create another Web Application Project and create a new folder named “shared” (or “common” or whatever your preferred name is).

The directory hierarchy under “shared” in the common project should be the same as expected by the referencing projects.

Now move the common files into this folder. Make sure you rename the namespace declarations in the moved files to the new web application’s namespace. Check the files in TFS.

Delete the copies of the common files from the referencing projects. After updating from TFS (Get Latest Version), make sure the files are really deleted in the file system. (I had several occurrences where deletions were not executing in the directories)

In the referencing projects, add as Pre-build event command line the following lines:

xcopy /s /y "$(SolutionDir)shared\*.ascx" "$(ProjectDir)"
xcopy /s /y "$(SolutionDir)shared\*.aspx" "$(ProjectDir)"

Adjust the path after $(SolutionDir) to match the path to the common project. You may also use $(ProjectDir) as base source path, and ..\ for parent directories.

Save the project, check in all pending changes, and build, which will execute the pre-build events, but will (most likely) fail, since you are still missing some files.

Next, use Project, Show All Files to display files in the Solution Explorer which are not part of the project.

Find the xcopied files, right-click each file individually and select Include in Project. This will add the ascx and aspx files in the project, and mark them Added in TFS.

Since we already have the files in TFS in the common project, and simply copy them into the referencing projects during build, we don’t want them in TFS in the referencing projects.

Select the added files in the Pending Changes window (i.e. all files except for the project file), right-click and execute Undo. Thus the files will not end up in TFS.

Check in the project file, and build.


Code Generation with PowerShell and TFS

October 15, 2009

If you use PowerShell to automatically generate code for your project (e.g. during the build process) and you work in TFS-based code, you need to check out existing files before overwriting them. Otherwise the files are read-only and/or not stored in TFS after code generation.

The PowerShell stub script to handle this situation looks like this (assuming the .ps1 file is also inside a TFS directory):

$scriptdir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$basepath = $scriptdir.Substring(0, $scriptdir.Length - "path\from\tfs-base\to\script".Length)

$scriptdir stores the directory name of the currently executed script. If the script file is stored inside your TFS project, you can calculate the file path to checkout from $scriptdir.

Next, we call TFS checkout, generate code, and check in again:

& .\tf-checkout.cmd $basepath
... Code generation is here ...
& .\tf-checkin.cmd $basepath

tf-checkout.cmd needs to set the Visual Studio environment variables (as in Visual Studio Command Prompt) to execute the “tf checkout” command:

@echo off
setlocal
call "c:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86

echo.
echo checking out...

tf checkout %1path\to\file1.cs
tf checkout %1path\to\file2.cs
...

endlocal

tf-checkin.cmd looks similar:

@echo off
setlocal
call "c:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86

echo.
echo checking in...

tf checkin /comment:autogenerated /noprompt %1path\to\file1.cs
tf checkin /comment:autogenerated /noprompt %1path\to\file2.cs

endlocal