11/30/10

How to Deploy Files in Different Servers with Team Build

I use Team Build to automate the build and deployment of my projects.  In the build project, there are settings that allow us to configure the build directory and drop location of the files. The build directory is a path in the build machine, and this does not really change. The drop location is a path where you want the files to be copied after a successful build.  Depending on your development environment, you may want these files to be copied to different servers. For example, there may be a development, staging and production location.  For our process, we keep  a copy of the build in the drop location, and from this location , we copy to the other servers. This allows us to recover previous builds.
Team Build uses MSBuild to perform the build process. MSBuild allows us to pass input parameters which can be used to run a conditional statement and determine the drop location dynamically. When I queue a new build, I add a parameter in the MSBuild command-line arguments (See Queue New Build under TFS Explorer). This parameter has this format:
/p:DeployTo=develop   (/p = command switch)
The parameter name and value is just a key/value pair which you can reference in the build script much like you would use parameters in a XSLT file.  The /p is a MSBuild command switch that denotes properties.  The TFS build project file is actually a XSLT file, so we can leverage the use of the Choose conditional statement to determine where to copy the files.  
<Choose>  
    <When Condition=" '$(DeployTo)'=='stage' ">
      <PropertyGroup>
        <DeployPath>\\stageServer\drop</DeployPath>
      </PropertyGroup>
    </When>
    <When Condition=" '$(DeployTo)'=='production' ">
      <PropertyGroup>
        <DeployPath>\\prodServer\drop</DeployPath>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <DeployPath>\\devServer\drop</DeployPath>
      </PropertyGroup>
    </Otherwise>
  </Choose>
This script basically checks the value of the parameter DeployTo and creates a variable named DeployPath. If no parameter is provided, it defaults to the development environment. You can also add additional variables which you can use to set other properties like build quality. Do make sure to replace the path with something relevant to your environment. We can now create a Target (group of tasks) and determine where to copy the files.
<Target Name="AfterDropBuild" >
<Message Text="Copying Files to: $(DeployPath)" ></Message>
<!--Define New Deployment Files To Be Copied  USE **\* to get all subfolders-->
<ItemGroup>           
   <Content Include="$(DropLocation)\$(BuildNumber)\Release\myproduct\**\*"
</ItemGroup> 
 <!--Task: Copy all the files and subfolders to the path defined by DeployPath-->
 <Copy SourceFiles="@(Content)" DestinationFolder="$(DeployPath)\%(RecursiveDir)"       ContinueOnError="false"></Copy>
 </Target>
We can now check-in the project file and queue a new build. After the build is successful, we can take a look at the folders in the other server and validate that the files were copied successfully.  If the files are not in the new location, take a look at the build log (drop location folder) and look for the"Copying Files To:" message that we added in the new target. The message should displayed the path that was selected. This is an approach to troubleshoot and see what is taking place with the build script.
Thanks for reading.


og-bit.com

11/29/10

Resolve /temp/global.asax(1,0): error ASPPARSE: Could not load type- Team Build

I was trying to automate the build an ASP.Net MVC Web application using Team Build, and the build kept failing with this error:
/temp/global.asax(1,0): error ASPPARSE: Could not load type
After taking a look at the build log, I was able to see that the error is generated right after a command to aspnet_compiler.exe is made. I also looked at the parameters, and I noticed that the path was incorrect.  To confirm that the problem was just with Team Build, I loaded the same project with Visual Studio and did the build manually. Everything worked ok.
I decided to take a look at the web project file (.csproj), and noticed  the following settings:
<PropertyGroup>
    <MvcBuildViews>true</MvcBuildViews>
 </PropertyGroup>

<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
   <AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
 </Target>

The MvcBuildViews variable is used to indicate that the MVC views should be compiled to detect any errors with the views. Currently, errors within a view file are not detected until run time. The physical path in the AfterBuild target did work for the Desktop build, but not the Team Build. The AspNetCompiler MSBuild task expects to find the compiled dll in the bin folder of the web project, but this is not the same location on the build machine.
To solve this problem, I had to edit the MVC web application project file and edit the AfterBuild target to match the following:
<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
    <AspNetCompiler Condition="'$(IsDesktopBuild)' == 'true'" VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
    <AspNetCompiler Condition="'$(IsDesktopBuild)' == 'false'" VirtualPath="temp" PhysicalPath="$(OutDir)\_PublishedWebsites\$(ProjectName)" />
  </Target>

I had to add a condition to check if the build was being done using Visual Studio (IsDeskTopBuild=true) and use the default PhysicalPath (bin folder in the project directory). If the build was not done using Visual Studio, the physical path was modified to reflect the path from the build machine. After making the change, I checked in the web application project file, and the next automated build completed successfully.


og-bit.com

11/17/10

Change Silverlight Business Application template to handle login state events

If you have created  a Silverlight Application on Visual Studio 2010, you have probably used the Business Application template. The template already has implementation to wire the login and registration tasks for the application. The login state information however is encapsulated in the LoginStatus control, and the application does not have any wiring to handle those events.  The MainPage needs to handle those events to enable certain user interface elements that should only be available after the user has logged in successfully. The MainPage acts as the master page (ASP.net) for the application.
 To enable an application to received these events, we need to make the following changes:
Edit the LoginStatus.xaml.cs file and add the following code segment in the LoginStatus class:
public delegate void LoginHandler(object sender, AuthenticationEventArgs e);
public event LoginHandler LoginStateChange;          //event to send when status changes

Look for the authentication event handlers and add the LoginStateChange call. The code should look as follows:
private void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)
{
      this.UpdateLoginState();
      LoginStateChange(this, e);  //send event
}

private void Authentication_LoggedOut(object sender, AuthenticationEventArgs e)
{
      this.UpdateLoginState();           
      LoginStateChange(this, e); //send event
}

Now edit the MainPage.xaml.cs file and in the constructor add the LoginStateChange handler. The code should look as follows:

public MainPage()
{
       InitializeComponent();
       LoginStatus ctrl = new LoginStatus();
       ctrl.LoginStateChange += new LoginStatus.LoginHandler(LoginStateChange);
       this.loginContainer.Child = ctrl;                        
}

Add the event handler and check for the user status to make an element visible. In this example, I am using a container named menu which I added to the MainPage.xaml file. Replace the container name with something you have previously defined in your file otherwise you will get a compilation error.

void LoginStateChange(object sender, AuthenticationEventArgs e)
{
     menu.Visibility = (e != null && e.User.Identity.IsAuthenticated) ?
                        System.Windows.Visibility.Visible :
                      System.Windows.Visibility.Collapsed;           
}

I hope it helps


og-bit.com

11/9/10

Support Multiple Orientations - Windows Phone 7

If you worked on Windows Mobile before, you probably remember all the work that had to be done to support different orientations on your Windows Mobile application. You had to create different layouts/resources and detect the orientation change to load the corresponding layout. In Windows Phone7, this is much easier to handle.
By default, Silverlight applications for Windows Phone 7 run in portrait mode.  To support both portrait and landscape mode, we just need to add an attribute in the root of your main page XAML file.
SupportedOrientations="PortraitOrLandscape"

You can also restrict the orientation mode of your application by using the same attribute. For example, a game application (Silverlight) may be better viewed in landscape mode. In this case, the attribute would be set as follows:

SupportedOrientations="Landscape"

For applications that get text input, it may not be enough to just set the orientation property because the hardware keyboard will take some of the display area.  For this case,  you may need to manipulate the layout by trapping the OrientationChanged event in the code.
In the XAML, we will need to add the event attribute as:
OrientationChanged="PhoneApplicationPage_OrientationChanged"

In the code, we will write the handler as follows:

private void PhoneApplicationPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
          string orientation = e.Orientation.ToString();
          //add logic to handle your layout
          base.OnOrientationChanged(e);
}



og-bit.com

11/3/10

Get Filename from Url Path - ASP Classic

The following VBScript function can be used in an ASP legacy application to extract the file name from a request.
public function GetFileName()
    dim files, url, segments

    'get then current url from the server variables
    url = Request.ServerVariables("path_info")

    segments = split(url,"/")

    'read the  last segment
    url = segments(ubound(segments))

    GetFileName = url

end function

The function returns the last segment of the path information or url. These are some examples:


Url
File Name
http://mydomain/products/default.asp
Default.asp
http://mydomain/products/
The default page in that directory(check web server settings)
http://mydomain/default.asp
Default.asp


This is useful when there is the need of an  application rule associated to the file name. For example, a new rule may need to be created on your legacy application because robots are exploiting a security hole in your application. You may want to protect some pages that can be used to update information , and you want to add a security policy to only those pages.  This function can be used to know the target page on the request and apply the security rule if the page has been configured to be protected.

Update - Function to get the server or domain name:

This function can be used to get the domain or server name where the application is hosted. This becomes useful when there is  a need to apply dynamic settings or rules based on the domain name. For example, we may want to connect to a different database when using a development server. In the world of .NET, this is easily managed by web.config settings for a build definition that targets a particular environment like Dev, Stage, Prod.

public function GetServerName()
    dim name

    'get then current url from the server variables
    name = Request.ServerVariables("server_name")  

    GetServerName = name

end function


I hope this is useful to someone.


thanks.


og-bit.com