Friday, February 13, 2009

Team Foundation System and ALM

SDLC in a Box

Overview
Jonathan and I recently attended a presentation on application lifecycle management and Visual Studio Team System (VSTS). The meeting was lead by Brian Prince from Microsoft, Jeff Hunsaker from Cardinal Solutions Group, and Alexei Govorine from Quick Solutions Group on behalf of the Central Ohio Application Lifecycle Management (ALM) Group (http://www.coalmg.org/). The meeting was sponsored by Microsoft, Cardinal Solutions Group, and Quick Solutions Group.

The presentation focused on using VSTS as a framework within which you could manage your application development process from start to finish and showed how VSTS could be used as a flexible framework that can integrate with existing systems and be customized to fit almost any process. We will try to provide a summary of the topics covered in the presentation below.

Components that make up Visual Studio Team System
There are a number of pieces that make up the Visual Studio Team System. All clients within Team System tie into the Source Control system within Team Foundation Server (TFS).
  • Team Foundation
    • Visual Studio Team Explorer
      • Provides access to projects, work items, build management and detailed reports showing bug trends in TFS and integrates into Visual Studio
      • Must be installed separately
    • Team Project Portal
      • Site hosted in SharePoint that is created for each team project
      • Useful store for documents, reports and other features common within SharePoint
    • Team Foundation Server
      • Provides a common area for collaborating on projects across all teams
      • Houses projects, work items, version control, build services, and security for accessing various pieces of each project
      • Allows users to subscribe to alerts
      • Integrates with Microsoft Project and Excel
    • Team Foundation Server Power Tools
      • This set of tools is highly recommended
      • Features Include:
        • Build notification
        • Process Template Editor
        • Check-In Policy Pack
        • Best Practices Analyzer
        • Work Item Templates
        • TFS Server Manager
        • Alert Editor
        • …lots more
  • Architecture Edition
    • Features Include:
      • Application and infrastructure modeling
      • Verification of software flow and deployment
  • Development Edition
    • Features Include:
      • Code analysis, profiling, and metrics
      • Code development
      • Unit testing
  • Test Edition
    • Features Include:
      • Load testing
      • Manual testing
      • Recording of tests for playback by developer
      • Management of test cases
  • Database Edition
    • Features Include:
      • Deployment of changes
      • Change management (for schemas)
      • Data generation
      • Unit testing for stored procedures
  • Team Web Access
    • Features Include:
      • Create, view, query for, and track work items
      • Manage project document libraries
      • Manage the software build process by running builds and reviewing results
      • Generate reports to track status and trend information over time for a team project
      • Benefits of Implementing TFS
      • Enable software development teams to work, communicate, and collaborate more productively
      • Enhance predictability and quality throughout the application lifecycle
      • Allow project managers/leads to make informed, better decisions through real-time visibility into project status and quality
      • Help development team to define, adopt and enforce consistent process in order to accelerate development cycles


Things to Think About
What should a person implementing TFS keep in mind or do to ensure that they see the proper ROI after completing the rollout. Below are the lists of frequently asked questions based on the different types of the product.

Things to Watch Out For
Please use the following link and keep track of any latest issues or bugs related to the VSTS. http://msmvps.com/blogs/vstsblog/archive/tags/Issues+and+Hotfixes/default.aspx

References

Contributors:
Brian Cordell - Application Architect
Jonathan Li - Application Architect

Wednesday, June 4, 2008

Visual studio content installer and code snippets

I was recently asked to supply code snippets for use by a client's development team so that it would be easier for them to adhere to the development standards. Code snippets are also very good for use in speeding up repeated code. For instance, we use a standard block of code to access databases (see below).

:: begin code snippet ::
'setup the SQL statement
Dim sql As String = "select * from MyTable"
Using con As New SqlClient.SqlConnection(connectionString)
  con.Open()
  Using cmd As SqlClient.SqlCommand = con.CreateCommand()
     With cmd
        'setup the command
        .CommandType = CommandType.Text
        .CommandText = sql
        'add the parameters
        'execute the command
        Using adapter As New SqlClient.SqlDataAdapter(cmd)
           ds = New DataSet()
           adapter.Fill(ds, "Data")
        End Using
     End With
  End Using
End Using
:: end code snippet ::

This format is standard across most of our data access methods (with some small exceptions) and the developers tend to copy/paste from another class/method or type it out themselves.

Code snippets are pretty easy to create. To do so, you first need to create a snippet file (*.snippet) which is an XML file containing the code block and other information (which I will go over below) and then import that into Visual Studio using the Code Snippets Manager. You can also create a VSI (Visual Studio content Installer) along with the corresponding content file. The VSI file is simply a zip file with the extension changed to ".vsi" that also contains a “*.vscontent” file. A content file is an XML file that lists out the contents of the VSI file along with additional information about the various snippet files. As you can see below, the XML provides information about the location and contents of the snippet file as well as what version of Visual Studio supports it. If the “ContentVersion” is “1.0”, then it will be supported by both VS 2005 and 2008. If it is “2.0”, then the snippet file will be supported by VS 2008. If the VSI file contains more than one snippet, you simply need to provide multiple content blocks (one for each file that needs installed).

:: begin standard content file ::
<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
  <Content>
     <FileName>database\bwcDbSqlQuery.snippet</FileName>
     <DisplayName>bwcDbSqlQuery</DisplayName>
     <Description>Standard SQL Server query (select) for database class.</Description>
     <FileContentType>Code Snippet</FileContentType>
     <ContentVersion>1.0</ContentVersion>
     <Attributes>
        <Attribute name="lang" value="vb" />
     </Attributes>
  </Content>
</VSContent>
:: end standard content file ::

A snippet file can contain more than one code snippet as well. The “CodeSnippet” element consists of two main elements, the header and the snippet. In the header, you will supply general information that will appear in the code snippet manager as well as the shortcut for inserting the snippet. In the snippet section, you will supply the code to be imported along with other optional sections. These optional sections can be used to check for references needed by your code, to import necessary namespaces if they don’t already exist, and to specify replaceable parameters. Replaceable parameters will show up as highlighted in your code and make it easy to change the parameter name in one place and have it cascade to all other instances within the snippet.

:: begin snippet file ::
<?xml version="1.0"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Standard SQL Server query</Title>
      <Author>Brian Cordell</Author>
      <Description>Standard SQL Server query (select) for database class.</Description>
      <Shortcut>bwcDbSqlQuery</Shortcut>
    </Header>
    <Snippet>
      <References>
        <Reference>
          <Assembly>System.dll</Assembly>
        </Reference>
        <Reference>
          <Assembly>System.Data.dll</Assembly>
        </Reference>
        <Reference>
          <Assembly>System.Xml.dll</Assembly>
        </Reference>
      </References>
      <Imports>
        <Import>
          <Namespace>System</Namespace>
        </Import>
        <Import>
          <Namespace>System.Data</Namespace>
        </Import>
      </Imports>
      <Declarations>
        <Object>
          <ID>ds</ID>
          <Type>DataSet</Type>
          <ToolTip>Replace with desired variable name.</ToolTip>
          <Default>ds</Default>
        </Object>
        <Object>
          <ID>sql</ID>
          <Type>String</Type>
          <ToolTip>Replace with desired variable name.</ToolTip>
          <Default>sql</Default>
        </Object>
        <Object>
          <ID>con</ID>
          <Type>SqlClient.SqlConnection</Type>
          <ToolTip>Replace with desired variable name.</ToolTip>
          <Default>con</Default>
        </Object>
        <Object>
          <ID>connectionString</ID>
          <Type>String</Type>
          <ToolTip>Replace with desired variable name.</ToolTip>
          <Default>connectionString</Default>
        </Object>
        <Object>
          <ID>cmd</ID>
          <Type>SqlClient.SqlCommand</Type>
          <ToolTip>Replace with desired variable name.</ToolTip>
          <Default>cmd</Default>
        </Object>
        <Object>
          <ID>adapter</ID>
          <Type>SqlClient.SqlDataAdapter</Type>
          <ToolTip>Replace with desired variable name.</ToolTip>
          <Default>adapter</Default>
        </Object>
      </Declarations>
      <Code Language="VB" Kind="method body">
        <![CDATA[
'setup the SQL statement
Dim $sql$ As String = ""
Using $con$ As New SqlClient.SqlConnection($connectionString$)
  $con$.Open()
  Using $cmd$ As SqlClient.SqlCommand = $con$.CreateCommand()
     With $cmd$
        'setup the command
        .CommandType = CommandType.Text
        .CommandText = $sql$
        'add the parameters
        'execute the command
        Using $adapter$ As New SqlClient.SqlDataAdapter($cmd$)
           $ds$ = New DataSet()
           $adapter$.Fill($ds$, "Data")
        End Using
     End With
  End Using
End Using
    ]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>
:: end snippet file ::

Remember that code snippets are a great way to speed up the development process and ease the adoption of standards by providing “ready made” blocks of code.

Monday, March 31, 2008

Working with SharePoint list web services...pt. 2

Now that you have the internal list name, you can perform the standard CRUD (create, read, update, and delete) items in the list & attachments for an item. You will also be able to manage a document library remotely (i.e. check in, check out, and undo check out). In the example below, I will just return all of the items in the list. To do this, I will use the “GetListItems” method. The method will return an XML fragment that contains information about the items in the list. You can parse the XML to get the attributes for each row. Note that the attribute, “ows_ID”, is the list item ID and will be needed when referring to a specific list item (i.e. when updating, deleting, adding attachments, etc…). Once you have the list of items, you can read that XML into a DataSet, use the XML directly, or deserialize the XML into a collection. I personally prefer the later because most developers are used to working with objects/collections and you get the benefit of strongly typed data. I'll discuss this more in my next blog.

:: begin code sample ::

//setup the service
_service = new wsLists.Lists();
_service.Url = _siteUrl + "/_vti_bin/Lists.asmx";
_service.CookieContainer = _cookieJar;
XmlNode listItemsXml = _service.GetListItems(listName, null, null, null, null, null, null);

:: end code sample ::

:: begin XML sample ::

<listitems xmlns:s=“uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882” xmlns:dt=“uuid:C2F41010-65B3-11d1-A29F-00AA00C14882” xmlns:rs=“urn:schemas-microsoft-com:rowset” xmlns:z=“#RowsetSchema” xmlns=“http://schemas.microsoft.com/sharepoint/soap/”>
<rs:data ItemCount=“5”>
<z:row ows_Title=“Item 1” ows_ID=“1” ows_owshiddenversion=“2” ows_Created=“2008-03-19 18:03:47” />
<z:row ows_Title=“Item 2” ows_ID=“2” ows_owshiddenversion=“2” ows_Created=“2008-03-19 18:03:47” />
<z:row ows_Title=“Item 3” ows_ID=“3” ows_owshiddenversion=“2” ows_Created=“2008-03-19 18:03:47” />
<z:row ows_Title=“Item 4” ows_ID=“4” ows_owshiddenversion=“2” ows_Created=“2008-03-19 18:03:47” />
<z:row ows_Title=“Item 5” ows_ID=“5” ows_owshiddenversion=“2” ows_Created=“2008-03-19 18:03:47” />
</rs:data>
</listitems>

:: end XML sample ::

Future blog(s):
  1. Using data returned from a SharePoint list.

Saturday, March 29, 2008

Working with SharePoint list web services

SharePoint lists can be very useful to increase collaboration from within applications across the enterprise. SharePoint exposes web services that can be used to get information from the lists and add/update information in the lists. I have provided a sample of code below that gets the collection of lists on a site. You can then use this to find the internal name of a list. This value will be needed to interact with other services pertaining to the specific list.

In the sample below, I compared the title of the list to the “Title” attribute which is the generic display name. The “Name” attribute is the internal name of the list.

:: begin code sample ::
:: listName will be equal to the GUID of the list corresponding to the specified name ::

string listName;
XmlNode col = null;
XmlDocument doc = null;
XmlElement elem = null;

//setup the service
_service = new wsLists.Lists();
_service.Url = _siteUrl + "/_vti_bin/Lists.asmx";

//get the collection of lists from the service
col = _service.GetListCollection();
//load the collection into a document
doc = new XmlDocument();
doc.LoadXml(col.OuterXml.Trim());
//the document will have one child node that will contain all children
if ((doc.HasChildNodes) && (doc.ChildNodes.Item(0).HasChildNodes))
{
//look through the children to find the desired node
foreach (XmlNode node in doc.ChildNodes.Item(0).ChildNodes)
{
if (node.NodeType == XmlNodeType.Element)
{
elem = (XmlElement)node;
if (elem.GetAttribute("Title") == "My List Name")
{
listName = elem.GetAttribute("Name");
break;
}
}
}
}

:: end code sample ::

Future blog(s):
  1. Part 2 of this entry discussing how to get a collection of the items in the list.

Friday, March 21, 2008

Authenticate via a web service in SharePoint

I thought that I would spend a little time talking about authenticating against SharePoint via a web service. This is something that most people will encounter if they wish to access information from a SharePoint list where SharePoint is secured via forms authentication.

Steps to authenticate (see example code below):

  1. Create a web reference to one of the authentication web service instances within SharePoint. It does not matter which one, so I usually just use the base one. In my example, I called the web reference “wsAuth”. The URL will follow the format “http(s)://mysharepointsite/_vti_bin/Authentication.asmx”.
  2. Create an instance of a cookie container and set it in the service. It will be used to maintain stateful communication with the web service and will be populated with the authentication information when you call the “Login” method.
  3. Set the URL if you wish to login to a specific section of the site. Note that the URL of the web reference will always default back to the base instance (regardless of where you create the web reference to).
  4. Call the “Login” method and get the result.

If successful, the cookie container will now have all of the necessary information for future calls to services that require authentication. You will need to set this object to the cookie container of the instance of a service (similar to below) and you should be able to successfully call the services on your SharePoint site. Note that the access to the site will be limited to that of the username/password being authenticated.

::begin code sample::

internal const string URL_AUTHENTICATION = "/_vti_bin/Authentication.asmx";

internal static CookieContainer Authenticate(string siteUrl, string username, string password)
{
CookieContainer cookieJar = new CookieContainer();
wsAuth.Authentication service = null;
wsAuth.LoginResult result;
try
{
service = new wsAuth.Authentication();
service.Url = siteUrl + URL_AUTHENTICATION;
service.CookieContainer = cookieJar;
result = service.Login(username, password);
switch (result.ErrorCode)
{
case wsAuth.LoginErrorCode.NoError:
//authentication was a success
break;
case wsAuth.LoginErrorCode.NotInFormsAuthenticationMode:
//there was an error with the authentication mode
break;
case wsAuth.LoginErrorCode.PasswordNotMatch:
//the username and/or password were invalid
break;
}
}
finally
{
if (service != null)
service.Dispose();
}
return cookieJar;
}

::end code sample::