Wednesday, August 22, 2012

Using TFS 2012 and SCRUM to Run the Sprint


Posted by: Subodh Sohoni , on 8/17/2012, in Category Visual Studio, VSTS & TFS
Views: 1774
Abstract: In our previous article, Using TFS 2012 and SCRUM for Iteration Capacity Planning for Software Development, we had seen how to plan the sprint and balance the work to be done during the sprint. In this article, we will take a look at how to run the sprint till the finish line. We will use TFS 2012 and its task board capability to do that.
In our previous article, Using TFS 2012 and SCRUM for Iteration Capacity Planning for Software Development, we had seen how to plan the sprint and balance the work to be done during the sprint. In this article, we will take a look at how to run the sprint till the finish line. We will use TFS 2012 and its task board capability to do that.
We had created a set of PBIs and tasks to implement those PBIs. Both of these were created as work items in TFS. Those tasks were assigned to various team members. SCRUM encourages team members who can play multiple roles so a team member may get tasks that are for architecting or development or for testing.
Open the board tab that is under the Sprint 2 of WORK tab of Web Access. It shows all the PBIs of Sprint 2 with tasks for each of those PBIs.
tfs-scrum-sprint1
SCRUM implementation in TFS 2012 is with the help of work items. Each work item may pass through the states of TO DO, IN PROGRESS and DONE. These phases are represented on this task board by columns.
Although the default view of the task board is to show the split of tasks based upon the PBIs, we can also view the split based upon the team members. If you move to team members tab on the same page, you can view the tasks assigned to each team member.
tfs-scrum-sprint2
All the tasks initially are in the TO DO state. In the task board, we can even see the total efforts required to complete those tasks.
Now one by one, the team members start taking up the tasks for execution. They can move the tasks that they can take for execution to IN PROGRESS state. Let us now use drag and drop facility on this board to move the tasks that are started actively to be worked by team members, to the IN PROGRESS column.
tfs-scrum-sprint3
If we double click on a task before moving it and after moving then we can see that the State of the task gets changed when moved.
tfs-scrum-sprint4
tfs-scrum-sprint5
Many a times, it becomes necessary that during the run of the Sprint we need to add some task. It can be done easily from the contents tab of the Sprint.
Sometimes there are causes due to which the PBI, Bug or a Task may get blocked. In TFS 2012, that cause is represented by the work item of the type Impediment. Impediment can be technical or something to do with availability of team member to do a task or any other issue. Anyone can create an impediment and assign it to Scrum Master.
tfs-scrum-sprint6
tfs-scrum-sprint7
Impediment is either Open or Close. It is the responsibility of Scrum Master to remove the impediment and close it.
tfs-scrum-sprint8
Impediment is linked to the other work items with Parent – Child relationship and queried through the normal mechanism.
tfs-scrum-sprint9
tfs-scrum-sprint10
Query Designer
tfs-scrum-sprint11
Query Results
tfs-scrum-sprint12
Touchdown
Sprint output usually is a shippable product that has incremental additions as compared to earlier sprint. Once the sprint is run, we can view various reports. One of the most important is Sprint Burndown. Another report that is available is Backlog view. It provides the details of each PBI in the backlog.
Sprint Burndown Chart is where we come to know daily how much work has been done and how much is yet to be done in the sprint. It shows an ideal trend and against that, the actual trend. This chart is calculated automatically by TFS 2012 every day based upon the tasks that are set as in DONE state. This chart gives an understanding - is the sprint going to be completed with all PBIs or are some of them are needed to be shifted to the next sprint.
tfs-scrum-sprint13
Backlog Overview is more detailed view of each PBI. It tells us the story of the PBI. How much efforts on that PBI already DONE and how much more are needed, how many test cases are related to that PBI and out of those, how many have passed and failed.
tfs-scrum-sprint14
We can also set a filter on the Sprint we are interested in and view the Backlog details of the sprint.
tfs-scrum-sprint15
SCRUM is a process where the completion date is not always defined in the beginning. Sprints can continue till either one of these events occurs
  1. Product Backlog has no more PBIs
  2. Budget for the project is over
  3. Deadline given by the customer arrives.
Since SCRUM inherently encourages teams to pick most important and high priority PBIs, even if the project is stopped or if the budget is exhausted or the deadline approached, the team has ensured that the most important and high priority features that define the product, have already been implemented in the shippable products that were output of earlier sprints.
Summary: Sprinting is when the team completes the scope of that iteration. During the sprint, the team works on usually the most important and high priority PBIs. Team is a set of cross functional people who can sprint only by working collaboratively. They may pass the work between the team members to take it forward. TFS 2012 supports the team to manage the sprints by providing the tools to make the sprint run. It provides task boarding capability as well as the reports, that are considered to be the core artifacts of SCRUM process.
Make sure you also read Using TFS 2012 and SCRUM for Iteration Capacity Planning for Software Development

Tuesday, August 14, 2012

Important Areas

NOTE: This is objective type question, Please click question title for correct answer.
ViewState allows the state of objects (serializable) to be stored in a hidden field on the page. ViewState is transported to the client and back to the server, and is not stored on the server or any other external source. ViewState is used to retain the state of server-side objects between postabacks.
Use Debug class to debug builds
Use Trace class for both debug and release builds.
DataReader
===========
DataReader is like a forward only recordset. It fetches one row at a time so very less network cost compare to DataSet(Fethces all the rows at a time). DataReader is readonly so we can't do any transaction on them. DataReader will be the best choice where we need to show the data to the user which requires no transaction. As DataReader is forward only so we can't fetch data randomly. .NET Data Providers optimizes the datareader to handle huge amount of data.

DataSet
=======
DataSet is an in memory representation of a collection of Database objects including tables of a relational database schemas.
DataSet is always a bulky object that requires a lot of memory space compare to DataReader. We can say that the DataSet is a small database because it stores the schema and data in the application memory area. DataSet fetches all data from the datasource at a time to its memory area. So we can traverse through the object to get the required data like querying database.
Custom tag allows you to create your own tag and specify key value pair for it.
NOTE: This is objective type question, Please click question title for correct answer.
NOTE: This is objective type question, Please click question title for correct answer.
1. Application level config = Web.config.
2. Machine level config = Machine.config.
System.Environment.UserName
NOTE: This is objective type question, Please click question title for correct answer.
HttpHanlder is the low level Request and Response API to service incoming Http requests. All handlers implement the IHttpHandler interface. There is no need to use any extra namespace to use it as it contains in the System.Web namespace. Handlers are somewhat analogous to Internet Server Application Programming Interface (ISAPI) extensions.


Each incoming HTTP request received by ASP.NET is ultimately processed by a specific instance of a class that implements IHTTPHanlder. IHttpHanlderFactory provides the infrastructure that handles the actual resolution of URL requests to IHttpHanlder instances. In addition to the default IHttpHandlerFactory classes provided by ASP.NET, developers can optionally create and register factories to support rich request resolution.
When EnableViewStateMAC is true for a page, the encoded and encrypted viewstate is checked to verify that it has not been tempered with on the client machine.

A EnableViewStateMAC is encoded version of the hidden variable that a page's viewstate is persisted to when sent to the browser.
Item stored in ViewState exist for the life of the current page. This includes postbacks (to the same page).
use Page.IsValid method, this revalidate your data server side against each Validation control used.

Item stored in ViewState exist for the life of the current page. This includes postbacks (to the same page).
use Page.IsValid method, this revalidate your data server side against each Validation control used.

Monday, August 6, 2012

LINQ

What is Language Integrated Query (LINQ)?
LINQ is a programming model that is the composition of general-purpose standard query operators that allow you to work with data, regardless of the data source in any .NET based programming language. It is the name given to a set of technologies based on the integration of query capabilities into any .NET language.
2. What are LINQ query expressions?
A LINQ query, also known as a query expression, consists of a combination of query clauses that identify the data sources for the query. It includes instructions for sorting, filtering, grouping, or joining to apply to the source data. The LINQ query expressions syntax is similar to the SQL syntax. It specifies what information should be retrieved from the data source.
3. Write the basic steps to execute a LINQ query.
The following are the three basic steps to execute a LINQ query:

  • Obtain the data source (The data source can be either an SQL database or an XML file)
  • Create a query
  • Execute the query
4. Write the basic syntax of a LINQ query in Visual Basic as well as in C#.
In Visual Basic, the basic syntax of a LINQ query starts with the From clause and ends with the Select or Group By clause. In addition, you can use the Where, Order By, and Order By Descending clauses to perform additional functions, such as filtering data and generating the data in a specific order.

In C#, the basic syntax of a LINQ query starts with the From clause and ends with the Select or group by clause. In addition, you can use the where, orderby, and Orderby descending clauses to perform additional functions, such as filtering data and generating the data in a specific order.
5. In which statement the LINQ query is executed?
A LINQ query is executed in the For Each statement in Visual Basic and in the foreach statement in C#.
6. In LINQ, lambda expressions underlie many of the standard query operators. Is it True or False?
It is true.
7. What is PLINQ?
PLINQ stands for Parallel Language Integrated Query. It is the parallel implementation of LINQ, in which a query can be executed by using multiple processors. PLINQ ensures the scalability of software on parallel processors in the execution environment. It is used where data grows rapidly, such as in telecom industry or where data is heterogeneous.

PLINQ also supports all the operators of LINQ. In addition, you can query 'collections by using PLINQ. It can also run several LINQ queries simultaneously and makes use of the processors on the system. Apart from this, PLINQ uses parallel execution, which helps in running the queries quickly. Parallel execution provides a major performance improvement to PLINQ over certain types of legacy code, which takes too much time to execute.
8. What are the different Visual Basic features that support LINQ?
Visual Basic includes the following features that support LINQ:

  • Anonymous types - Enables you to create a new type based on a query result.
  • Implicitly typed variables - Enables the compiler to infer and assign a type when you declare and initialize a variable.
  • Extension method - Enables you to extend an existing type with your own methods without modifying the type itself.
9. What is the function of the DISTINCT clause in a LINQ query?
The DISTINCT clause returns the result set without the duplicate values.
10. What is the DataContext class and how is it related to LINQ?
After you add a LINQ to SQL Classes item to a project and open the O/R Designer, the empty design surface represents an empty DataContext class ready to be configured. The DataContext class is a LINQ to SQL class that acts as a conduit between a SQL Server database and the LINQ to SQL entity classes mapped to that database. This class contains the connection string information and the methods for connecting to a database and manipulating the data in the database. It is configured with connection information provided by the first item that is dragged onto the design surface.
11. What is the difference between the Take and Skip clauses?
The Take clause returns a specified number of elements. For example, you can use the Take clause to return two values from an array of numbers. The Skip clause skips the specified number of elements in the query and returns the rest. For example, you can use the Skip clause to skip the first four strings in an array of strings and return the remaining array of string.
12. What is Object Relational Designer (0/R Designer)?
The 0/R Designer provides a visual design surface to create LINQ to SQL entity classes and associations (relationships) that are based on objects in a database.

Wednesday, August 1, 2012

Creating a Scrum Team Project in Visual Studio 2012 using Visual Studio Scrum 2.0 process

To Install and Configure Team Foundation Server 2012 see the post here at Team Foundation Server 2012 RC – Install & Configure.
In this post, we will walk through the steps to achieve the following tasks
  • Connect to Team Foundation Server – TFS 2012
  • Creating a Team Project using Visual Studio Scrum 2.0 process
  • Windows Azure TFS Service
This post defines the SCRUM and discusses how to use SCRUM Template 2.0 to manage projects in Visual Studio 2012 Team Foundation Server. You can develop the enterprise projects using SCRUM framework which is based on Agile development methodology.
Scrum is an iterative and incremental agile software development method for managing software projects and product or application development. For Introduction to Agile Project Management Tools, see the blog post @ http://kishore1021.wordpress.com/2011/02/18/agile-project-management-tools/
image
Figure: Pictorial representation of the Scrum development.
For Scrum Process for Software Development using Microsoft Visual Studio Scrum 1.0 Process Template , see the blog post @ http://kishore1021.wordpress.com/2010/08/02/scrum-process-for-software-development-using-microsoft-visual-studio-scrum-1-0-process-template/
At this point you can go one of two directions.  You can install TFS 2012 or use Windows Azure based Visual Studio Team Foundation Service Preview. Team Foundation Service Preview enables everyone on your team to collaborate more effectively, be more agile, and deliver better quality software. You can sign up at http://tfspreview.com/en-us/ and start using TFS if you want to use the Azure version.
In this post, we will discuss about installing and configuring TFS with VS 2012.

Connect to Team Foundation Server:

All team projects are stored and managed on a Team Foundation Server. To start working on a team project, you must first connect to the appropriate Team Foundation Server. Following are the steps to connect to an instance of Team Foundation Server for the first time.
1. On the menu bar, choose VIEW menu and click Team Explorer as shown in Fig 1.

Figure 1: Selecting Team Explorer in Visual Studio 2012
2. Team Explorer is highlighted up as shown in Fig 2
image
Figure 2: Visual Studio 2012 with Team Explorer
3. On the Team Explorer shown in Fig 3, click Connect to Team Foundation Server.
image
Figure 3: Click the link Connect to Team Foundation Server.
3.1 Step 3 can also be achieved by clicking on the TEAM menu shown in Fig 3.1 and click on Connect to Team Foundation Server.
image
Figure 3.1 Connecting to Team Foundation Server from the TEAM menu
4. In the Connect to Team Foundation Server dialog box, select a Team Foundation Server from the drop-down list as shown in Fig 4.

Figure 4: Connecting to Team Foundation Server
Note: If the drop-down list is empty, click the Servers button to manually enter the Team Foundation settings as shown in Fig 4.1. Contact your Team Foundation Server administrator or team project administrator for the correct Team Foundation Server connection settings.
image
Figure 4.1: Manually enter the Team Foundation settings
  1. In the Connect to Team Project dialog box, choose the Servers button.
  2. In the Add/Remove Team Foundation Server dialog box, choose the Add button.
  3. In the Add Team Foundation Server dialog box, type the name or URL for the server.
  4. When you type a server name, the Preview field automatically displays the URL format, for example:
    http:// ServerName:Port/tfs
    ServerName is the name of the server that hosts Team Foundation Server.
    Port is the port that Team Foundation Server uses; the default value is 8080. If your server uses a different port number, you must specify it in the Port number box.
    tfs is the default path to the project collections that are stored on the server. If your team uses a different path, type it in the Path box.
  5. Verify that the URL is correct, choose the OK button twice, and then choose the Close button.
5. In the Connect to Team Project dialog box, under Team Project Collections, select the team project collection that hosts the team projects that you want to connect to. Then, under Team Projects, select the check box for each team project that you want to access, and then choose the Connect button as shown in Fig 5. Team projects with a check mark next to them will display in Team Explorer.
image
Figure 5: Connect to Team Project and choose the Team Projects
6. Click OK.
Team Explorer displays the team projects under the selected Team Foundation Server as shown in Fig 6. Note: The contents displayed in Fig 6 might vary depending on the versions of VS.

image

Fig 6: Details of Team Explorer
Note: Team projects are created on a Team Foundation Server, therefore, you must connect to a Team Foundation Server as described in the above 6 steps before perform Creating a Team Project. After you have connected, you can create a team project.

Creating a Team Project

Software projects in Team Foundation are called team projects and are very different from the software projects (.csprj or .vbproj) in Visual Studio. The team project is the central concept that holds together the team endeavor of creating a specific software technology or product. When you create a team project, the New Team Project Wizard creates a number of focal points by which to centralize the team efforts. A team project Web site is created containing document templates, and predefined reports. A work item database is created for tracking all effort on the project. A process template is installed that determines rules, policies, security groups, and queries for all work effort. A source code branch is created for source control.
image
Figure 7: A project is where you store all your source code, as well as tasks and builds.
1. Team project can be created by using any of the following methods (a, b or c).
a. On the File menu, point to New, and then click Team Project as shown in Fig 8
image
Figure 8: Creating a Team Project from File Menu
b. In Team Explorer click Create a New Team Project as shown in Fig 9
image
Figure 9: Creating a New Team Project from Team Explorer links
c. In Team Explorer click Drop Down Icon, select Project and My Teams, Click on New Team Project as shown in Fig 10
image
Figure 10: 3 steps to create a team project using Home drop down options.
2. The New Team Project wizard appears. On the Specify the Team Project Settings page, type your project name in the What is the name of the team project? box as shown in Fig 11. click Next.
image
Figure 11: Specify the Team Project Settings
Note: Going ahead, I will be using iCITE as the project name.
3. On the Select a Process Template page as shown in Fig 12, in the Which process template should be used to create the team project? drop-down list, select Microsoft Visual Studio Scrum 2.0 as shown in Fig 13.
image
Figure 12: Selecting process template for the New team Project
Team Explorer includes process templates based on the Microsoft Solutions Framework (MSF). Some of the process templates are available by default: Microsoft Visual Studio Scrum 2.0, MSF for Agile Software Development – v6.0, and MSF for CMMI Process Improvement – v6.0. Your team or organization may provide additional process templates or may remove the MSF templates. Here’s some help about what process template to choose:
Microsoft Visual Studio Scrum 2.0 (default) is built for teams practicing the Scrum methodology, and want to use the Scrum terms, such as "Product Backlog Item."
MSF for Agile Software Development 6.0 supports iterative and incremental software development. MSF for Agile can also be used to implement Scrum, however it is adaptive for more general use.
MSF for CMMI Process Improvement 6.0 supports an approach whose goal is to help organizations improve their performance. Development with CMMI emphasizes traceability and auditability.
image
Figure 13: Selecting Microsoft Visual Studio Scrum 2.0 for the New team Project
4. On the Specify the Source Control Settings page shown in fig 14, keep the default values and click Next.
image
Figure 14: Source Control folder path
5. On the Confirm the Team Project Settings page shown in Fig 15 , click Finish.
image
Figure 15: Confirming the settings for the New Team Project
6. The New Team Project wizard creates your new team project as shown in Fig 16.
NoteNote: It may take several minutes for the wizard to finish.
image
Figure 16: Status showing the Team Project Creation.
7. On the Team Project Created page shown in Fig 17, click Close.
image
Figure 17: Successful creation of Team Project.
Because the check box for Launch the process guidance for more information about running the team project was selected by default, the wizard opens the overview page for the process guidance for MSF Agile for Software Development.
The iCITE Innovation team project displays in Team Explorer. There are several top-level nodes:
  • My Work: This node provides access to the tasks assigned to me.
  • Pending Changes: This node provides access to the team project source control management hierarchy.
  • Work Items: This node provides access to add work items and to create and view queries against the work item database.
  • Builds   This node provides access to the builds of your team project.
  • Web Access: Click on Web Access opens up the web page for the project.
  • Settings:  Clicking on settings brings up the Project Settings page in team Explorer as shown in Figure 18.
image
Figure 18: Team Project Settings displayed in Team Explorer.
From here on, you can either use the Team Explorer in Visual Studio 2012 or using the browser for planning and tracking projects.
a. Using Visual Studio 2012 and team Explorer to manage the Team Project
image
b. You can also use Web Browser to manage the Team Project. In the Team Explorer home navigation menu for the team project, choose Web Access to launch the browser as shown in Fig 19. A browser window will open to the home page for that team project with the URL http://ServerName:Port/tfs/CollectionName/ProjectName For ex, http://kishore1021:8080/tfs/DefaultCollection/iCITE%20Innovation/
image
Figure 20: Managing the Team Project through browser.
FYI: Click on the settings (gear) icon located on the top upper right hand corner in the browser window shown in figure 20 brings up the browser window shown in Figure 21.image
Figure 21: Control Panel of the Team Project
Fig 22 shows the Control Panel of the Team Project as seen from the browserimage
Figure 22: Control Panel of the Team Project

Windows Azure TFS Service:

Microsoft announced the availability of Windows Azure based Visual Studio Team Foundation Service. To get started, signup for TFS at http://tfspreview.com/.
I created similar project on windows Azure TFS Service and following is the screenshot. https://kishore1021.tfspreview.com/DefaultCollection/iCITE%20Project
image
Figure 23: Team collaboration and agile planning in Windows Azure TFS Service.
To get started on using TFS on Windows Azure, see the blog http://blogs.msdn.com/b/bharry/archive/2011/09/14/team-foundation-server-on-windows-azure.aspx

C# and VB.NET Code Searcher - Using Roslyn

Introduction

This article is about a tool using Roslyn that can search through a large codebase in 4 ways:
  1. Search text in methods
  2. Search calls to certain methods  
  3. Search for methods with certain names
  4. Search for properties with certain names 

Screenshot

Here's a screenshot of C# and VB.NET Code Searcher in action.

The problem 

Recently I had an assignment that required a lot of searching through the source code of a large legacy codebase (61 solutions, C# code). A field had to be moved from one table to another table. It was a change that would impact some parts of the codebase. To find out I had to find methods in the data layer where Stored procedures were called. Then I had to go bottom-up through the codebase to see where these methods were called, and what the impact was on the code.
At first I used the freeware tool "TextCrawler 2" for that (http://www.digitalvolcano.co.uk/content/index.php). This is quite a fast text search utility. But the problem is, it doesn't "know" anything about the C# language. For example, if you search for method calls to a certain method, TextCrawler will happily find files for you that have the method calls commented out. Another problem was, it wasn't fast enough (searching through 61 solutions can take some time..). I also used the Microsoft Desktop Search tool, this was fast but also not "intelligent" with the source code.
Since I read about Roslyn I thought of ways I could make it useful for this purpose.

Background about Roslyn

Roslyn is Microsoft’s project to open up the VB and C# compilers through APIs, and provide easy access to the information it gathers during the different stages of the compilation process.
To get started on what Roslyn is about, you can read about it here:
Or if you'd prefer to take a deeper dive into Roslyn, here's a whitepaper from Microsoft:
This article is not meant to give you an introduction to Roslyn, there are a couple of good CodeProject articles that do that:
I also found out that after installing the Microsoft Roslyn CTP - June 2012 there were lots of sample projects installed in my Documents folder.

The solution

So I thought I'd give Roslyn a try to see if I could create a tool that could search through code faster. I think I succeeded in this. I use it all the time now! I created a Windows Forms application that has 4 ways of searching through C# and VB.NET code:
  1. Search text in methods 
  2. Search calls to certain methods
  3. Search for methods with certain names
  4. Search for properties with certain names 
I decided to share this with the world so everyone can enjoy it. By posting this article, I hope that:
  • People will find this useful too.
  • I get valuable feedback so the tool can be improved.
  • People will extend / adapt the tool or parts of it in ways I haven't thought of yet. 

How fast is it

The first time you use it the tool will be slower, because it has to build a list of IWorkspace objects in memory (604 MB of memory in my situation). With a couple of solutions you will hardly notice. But to give you an indication: On my computer it took about half a minute searching 61 solutions the first time. If you keep the tool open and search a second time it will be considerably faster (a few seconds for searching through 61 solutions) because it already has the list of IWorkspace objects in memory. After I started using the .NET 4 Parallel.ForEach keyword the performance has increased significantly (with a factor depending on the number of cores in the processor of your computer, Dual Core, Quad Core, etc.).

How to use it   

Prerequisites

Make sure you have the following software installed in this order, otherwise the solution will not build:
.NET Framework 4.0
- Visual Studio 2010 (not Express edition)
   \ The C# language feature if you want to be able to search in C# source code
   \ The VB.NET language feature if you want to be able to search in VB.NET source code
Visual Studio 2010 SP1
Visual Studio 2010 SP1 SDK
Microsoft "Roslyn" CTP
Next: solutions.txt file
You have to provide the tool with a list of solutions to search through.
There are 2 ways you can do this:
  1. With a text file "solutions.txt" placed in the directory of the executable (or \bin\debug after you build the solution). The tool will read this on startup if it exists. This text file should contain full paths to the solutions. Each on it's own line.
  2. If the solutions.txt file doesn't exist yet, click on [Browse ...] and in the File dialog select a directory. Next click on [Update solution List]. The tool will then walk recursively down the directory structure, starting at the selected directory, looking for solution (.sln) files.
The result will be stored in the "solutions.txt" file in the directory of the executable. The existing "solutions.txt" file will be overwritten.

Next: search 

  • Type the text you want to search for in the textbox.
  • Select one of the ways to search with by clicking one of the radio buttons.
  • Click [Search].
The solutions from solutions.txt, all underlying projects, and all underlying source files will be searched through.
The result of the search consists of:
  1. The path to the source files containing the found methods.
  2. The body of the methods.
You can also specify words in the textboxes on the right that say:

"Do not include files containing words in filename. Separate by comma."
or
"Only include files containing word in filename. Separate by comma."
  • "Do not include" means, the tool will not search in code files that have any of the words in the path. 
  • "Only include" means, the tool will only search in code files that have any of the words in the path.  
These text boxes are mutually exlusive, they can not be used at the same time, " Do not include" takes precedence over "include". 

Syntax Highlighting with Fast Colored TextBox

To present the results of the code search I needed a text editor that could do Syntax Highlighting. I researched a couple of those, and decided to use the great "Fast Colored TextBox" from Pavel Torgashov in my project (also on CodeProject): Fast Colored TextBox for Syntax Highlighting. It also supports searching in the textbox with Ctrl-F.

Multiple Searches using KRBTabControl

To be able to start multiple searches using a tabbed interface, I gladly used the excellent "KRBTabControl" from Burak299 in my project (also on CodeProject): KRBTabControl. This gave me the possibility to provide tabs that can be closed just like browser tabs. When there are too many tabs too display you will see two tiny arrows on the right so you can switch between tabs with the mouse.  

The implementation  

The code below is not entirely the same as the sourcecode itself, but this is meant to show you the basics of how the tool works.

When the Search button is clicked, a search is started using the selected Search method (the radio buttons).
public enum SearchType
{
    SearchTextInMethod,
    SearchCallers,
    SearchMethods,
    SearchProperties
} 
private SearchType _searchType = new SearchType();
 
        /// <summary>
        /// - Do some checks to see if the input is correct and the solutions.txt file exists
        /// - Update the text of the tab to the text that is being searched
        /// - Show a hourglass icon on the tab during the search
        /// - Start a new worker that will do the search
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSearch_Click(object sender, EventArgs e)
        {
            string searchText = txtTextToSearch.Text;
            //Remove leading and trailing spaces
            searchText = searchText.Trim();

            if (searchText.Contains("(") || searchText.Contains(")"))
            {
                MessageBox.Show("Please specify searchtext without parentheses or parameters.");
                return;
            }

            if (!File.Exists(Constants.BaseDirectorySolutionsTxtPath))
            {
                MessageBox.Show("There is no solutions.txt file in the directory where the .exe resides. Please click the [Browse] button to select a starting direcctory. Then click [Update solution List]");
            }
            else
            {
                SearchType searchType = new SearchType();

                if (!String.IsNullOrEmpty(searchText))
                {
                    TabController.UpdateSearchTextOnTab(searchText);
                    TabController.ShowHourGlass();

                    if (rbSearchTextInMethod.Checked)
                    {
                        searchType = SearchType.SearchTextInMethod;
                    }
                    else if (rbSearchCallers.Checked)
                    {
                        searchType = SearchType.SearchCallers;
                    }
                    else if (rbSearchMethods.Checked)
                    {
                        searchType = SearchType.SearchMethods;
                    }
                    else if (rbSearchProperties.Checked)
                    {
                        searchType = SearchType.SearchProperties;
                    }

                    //Create and start a new worker that will do the searching for us.
                    WorkerFactory.Start(searchType, searchText, txtExclude.Text, txtInclude.Text, TabController.SelectedTab.Guid);
                }
                else
                {
                    MessageBox.Show("Please enter text to search");
                }
            }
        } 
The WorkerFactory.Start method creates a new Worker object every time you do a search.
public static class WorkerFactory
{
    private static List<Worker> _workerList = new List<Worker>();

    public static void Start(SearchType searchType, string searchText, 
           string filter, string include, Guid guid)
    {
        Worker worker;

        worker = new Worker(searchType, searchText, filter, include, guid);
        _workerList.Add(worker);

        worker.Start();
    }

    /// <summary>
    /// Select a worker from the workerlist with a certain Guid.
    /// </summary>
    /// <param name="guid"></param>
    /// <returns></returns>
    private static Worker SelectWorker(Guid guid)
    {
        var selectWorker = from worker in _workerList
                           where worker.Guid == guid
                           select worker;

        if (selectWorker != null && selectWorker.Count() == 1)
        {
            return (Worker)selectWorker.First();
        }

        return null;
    }

    /// <summary>
    /// If a tab is deleted the accompanying worker must be cancelled.
    /// It won't be killed, but the results will not be written to a tab anymore.
    /// If it's not needed anymore, doesn't matter because they will be cleaned up once the program quits.
    /// </summary>
    /// <param name="guid">The unique identifier of the worker</param>
    public static void Delete(Guid guid)
    {
        Worker selectWorker = SelectWorker(guid);

        //Does the worker exist in the workerlist?
        //Because, if a tab is deleted, but a worker was not started for that tab,
        //there is no worker to delete.
        if (selectWorker != null)
        {
            selectWorker.Cancel();
            _workerList.Remove(selectWorker);
        }
    }
} 
This Worker uses a BackgroundWorker to start a thread that starts a codesearch using Roslyn.
public class Worker
{
    private CodeSearcher _searcher;
    BackgroundWorker _worker;
    private string _result;
    private Guid _guid;
    private bool _cancel;
 
    public Worker(SearchType searchType, string searchText, string filter, string include, Guid guid)
    {
        _guid = guid;
        _searcher = new CodeSearcher(searchType, searchText, filter, include);

        _worker = new BackgroundWorker();
        _worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    }

    public Guid Guid
    {
        get { return _guid; }
        set { _guid = value; }
    }

    public void Start()
    {
        _worker.RunWorkerAsync();
    }

    /// <summary>
    /// Cancel means the backgroundworker will finish it's job,
    /// but won't write the results to the tabcontroller anymore.
    /// </summary>
    public void Cancel()
    {
        _cancel = true;
    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (!_cancel)
        {
            TabController.WriteResults(_guid, _result);
        }
    }

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
       _result = _searcher.Search();
    }
}
If the worker is started with the Start method, it calls the worker_DoWork asynchronously, which calls the CodeSearcher.Search method that searches using 1 of 4 methods, depending on the selected SearchType.
public class CodeSearcher
{
    /// <summary>
    /// Search for the provided searchtext in the sourcecode files of the solutions.
    /// Use the provided SearchType (method, callers, text in method).
    /// Return the result in a string.
    /// </summary>
    /// <returns></returns>
    public string Search()
    {
      string result = "";

      List<string> excludes = CodeSearcher.GetFilters(_exclude);
      List<string> includes = CodeSearcher.GetFilters(_include);

      if (CodeRepository.Workspaces.Count() == 0)
      {
        //Get the solutions from the solutions.txt file and load them into Workspaces
        //If it doesn't exist, this will be checked at the moment user presses the [Search] button.
        CodeRepository.Solutions = CodeRepository.GetSolutions(Constants.BaseDirectorySolutionsTxtPath);

        CodeRepository.Workspaces = CodeRepository.GetWorkspaces(CodeRepository.Solutions);
      }

      if (_searchType == SearchType.SearchTextInMethod)
      {
        result = SearchMethodsForTextParallel(CodeRepository.Workspaces, _searchText, excludes, includes);
      }
      else if (_searchType == SearchType.SearchCallers)
      {
        result = SearchCallersParallel(CodeRepository.Workspaces, _searchText, excludes, includes);
      }
      else if (_searchType == SearchType.SearchMethods)
      {
        result = SearchMethodsParallel(CodeRepository.Workspaces, _searchText, excludes, includes);
      }
      else if (_searchType == SearchType.SearchProperties)
      {
        result = SearchPropertiesParallel(CodeRepository.Workspaces, _searchText, excludes, includes);
      }

      return result;
    } 
If a solutions.txt file exists in the directory where the RoslynCodeSearcher.exe resides, the paths to the solutions will be put in a List and the Workspaces with the solutions will be loaded. A workspace is an active representation of your solution as a collection of projects, each with a collection of documents. The workspace provides access to the current model of the solution. You can read more about it here.
In the CodeSearcher class I have 4 search methods. This is where the searching happens. The searching makes use of the .NET 4 keyword Parallel.ForEach to speed things up depending on the number of cores in the processor of your computer.
 /// <summary>
/// Search through the code for methods that contain the text textToSearch.
/// Return the resulting method bodies as a string.
/// excludes are used to exclude files that have paths that contain certain words.
/// includes are used to include files that have paths that contain certain words.
/// </summary>
/// <param name="workspaces"></param>
/// <param name="textToSearch"></param>
/// <param name="excludes">Projects / documents to exclude by name</param>
/// <param name="includes">Projects / documents to include by name</param>
/// <returns></returns>
public string SearchMethodsForTextParallel(List<IWorkspace> workspaces, string textToSearch, List<string> excludes, List<string> includes)
{
  StringBuilder result = new StringBuilder();
  string language = "";

  foreach (IWorkspace w in workspaces)
  {
    ISolution solution = w.CurrentSolution;

    foreach (IProject project in solution.Projects)
    {
      language = project.LanguageServices.Language;

      Parallel.ForEach(project.Documents, document =>
      {
        //Filter and include document names containing certain words
        if (!excludes.Any(s => document.FilePath.ToUpper().Contains(s)) &&
            (
              includes.Count() == 0 || includes.Any(s => document.FilePath.ToUpper().Contains(s)))
            )
        {
          if (language == LANG_CS)
          {
            result.Append(SearchMethodsForTextCSharp(document, textToSearch));
          }
        }
      });
    }
  }

  return result.ToString();
}

private string SearchMethodsForTextCSharp(IDocument document, string textToSearch)
{
  StringBuilder result = new StringBuilder();

  CommonSyntaxTree syntax = document.GetSyntaxTree();
  var root = (Roslyn.Compilers.CSharp.CompilationUnitSyntax)syntax.GetRoot();

  var syntaxNodes = from methodDeclaration in root.DescendantNodes()
                   .Where(x => x is MethodDeclarationSyntax || x is PropertyDeclarationSyntax)
                    select methodDeclaration;

  if (syntaxNodes != null && syntaxNodes.Count() > 0)
  {
    foreach (MemberDeclarationSyntax method in syntaxNodes)
    {
      if (method != null)
      {
        string methodText = method.GetFullText();
        if (methodText.ToUpper().Contains(textToSearch.ToUpper()))
        {
          result.Append(GetMethodOrPropertyTextCSharp(method, document));
        }
      }
    }
  }

  return result.ToString();
}

/// <summary>
/// Search through the code for calls of a method with name textToSearch.
/// Return the resulting method bodies as a string.
/// excludes are used to exclude files that have paths that contain certain words.
/// includes are used to include files that have paths that contain certain words.
/// </summary>
/// <param name="workspaces"></param>
/// <param name="textToSearch"></param>
/// <param name="excludes">Projects / documents to exclude by name</param>
/// <param name="includes">Projects / documents to include by name</param>
public string SearchCallersParallel(List<IWorkspace> workspaces, string textToSearch, List<string> excludes, List<string> includes)
{
  StringBuilder result = new StringBuilder();
  string language = "";

  foreach (IWorkspace w in workspaces)
  {
    ISolution solution = w.CurrentSolution;

    foreach (IProject project in solution.Projects)
    {
      language = project.LanguageServices.Language;

      Parallel.ForEach(project.Documents, document =>
      {
        //Filter and include document names containing certain words
        if (!excludes.Any(s => document.FilePath.ToUpper().Contains(s)) &&
            (
              includes.Count() == 0 || includes.Any(s => document.FilePath.ToUpper().Contains(s)))
            )
        {
          if (language == LANG_CS)
          {
            result.Append(GetSearchCallersCSharp(document, textToSearch));
          }
        }
      });
    }
  }

  return result.ToString();
}

private string GetSearchCallersCSharp(IDocument document, string textToSearch)
{
  StringBuilder result = new StringBuilder();

  CommonSyntaxTree syntax = document.GetSyntaxTree();
  var root = (Roslyn.Compilers.CSharp.CompilationUnitSyntax)syntax.GetRoot();

  var invocationAccess = from invoc in root.DescendantNodes().OfType<Roslyn.Compilers.CSharp.InvocationExpressionSyntax>()
                         select invoc;

  if (invocationAccess != null && invocationAccess.Count() > 0)
  {
    foreach (Roslyn.Compilers.CSharp.InvocationExpressionSyntax s in invocationAccess)
    {
      Roslyn.Compilers.CSharp.ExpressionSyntax expr = s.Expression;
      if (expr is Roslyn.Compilers.CSharp.IdentifierNameSyntax)
      {
        if (((Roslyn.Compilers.CSharp.IdentifierNameSyntax)expr).PlainName == textToSearch)
        {
          var method = from m in s.Ancestors().Where(x => x is MethodDeclarationSyntax)
                       select m;

          if (method != null && method.Count() > 0)
          {
            result.Append(GetMethodOrPropertyTextCSharp(method.First(), document));
          }
        }
      }
    }
  }

  var memberAccess = from memberAcc in root.DescendantNodes().Where(x => x is Roslyn.Compilers.CSharp.MemberAccessExpressionSyntax)
                     where ((Roslyn.Compilers.CSharp.MemberAccessExpressionSyntax)memberAcc).Name.GetFullText() == textToSearch
                     select memberAcc;

  if (memberAccess != null && memberAccess.Count() > 0)
  {
    foreach (Roslyn.Compilers.CSharp.MemberAccessExpressionSyntax s in memberAccess)
    {
      var method = from m in s.Ancestors().Where(x => x is MethodDeclarationSyntax || x is PropertyDeclarationSyntax)
                   select m;

      if (method != null && method.Count() > 0)
      {
        result.Append(GetMethodOrPropertyTextCSharp(method.First(), document));
      }
    }
  }

  return result.ToString();
}

/// <summary>
/// Search through the code for Method declarations that match textToSearch.
/// Return the resulting method bodies as a string.
/// excludes are used to filter out files that have paths that contain certain words.
/// includes are used to include files that have paths that contain certain words.
/// </summary>
/// <param name="workspaces"></param>
/// <param name="textToSearch"></param>
/// <param name="excludes">Projects / documents to exclude by name</param>
/// <param name="includes">Projects / documents to include by name</param>
public string SearchMethodsParallel(List<IWorkspace> workspaces, string textToSearch, List<string> excludes, List<string> includes)
{
  StringBuilder result = new StringBuilder();
  string language = "";

  foreach (IWorkspace w in workspaces)
  {
    ISolution solution = w.CurrentSolution;

    foreach (IProject project in solution.Projects)
    {
      language = project.LanguageServices.Language;

      Parallel.ForEach(project.Documents, document =>
      {
        //Filter and include document names containing certain words
        if (!excludes.Any(s => document.FilePath.ToUpper().Contains(s)) &&
            (
              includes.Count() == 0 || includes.Any(s => document.FilePath.ToUpper().Contains(s)))
            )
        {
          if (language == LANG_CS)
          {
            result.Append(GetSearchMethodsResultCSharp(document, textToSearch));
          }
        }
      });
    }
  }

  return result.ToString();
}

/// <summary>
/// 
/// </summary>
/// <param name="document"></param>
/// <param name="textToSearch"></param>
/// <returns></returns>
private string GetSearchMethodsResultCSharp(IDocument document, string textToSearch)
{
  StringBuilder result = new StringBuilder();

  CommonSyntaxTree syntax = document.GetSyntaxTree();
  var root = (Roslyn.Compilers.CSharp.CompilationUnitSyntax)syntax.GetRoot();

  var methods = from method in root.DescendantNodes().Where(x => x is MethodDeclarationSyntax)
                where ((MethodDeclarationSyntax)method).Identifier.ValueText == textToSearch
                select method;

  if (methods != null && methods.Count() > 0)
  {
    foreach (Roslyn.Compilers.CSharp.SyntaxNode m in methods)
    {
      result.Append(GetMethodOrPropertyTextCSharp(m, document));
    }
  }

  return result.ToString();
}

/// <summary>
/// Search through the code for Property declarations that match textToSearch.
/// Return the resulting property bodies as a string.
/// excludes are used to exclude files that have paths that contain certain words.
/// includes are used to include files that have paths that contain certain words.
/// </summary>
/// <param name="workspaces"></param>
/// <param name="textToSearch"></param>
/// <param name="excludes">Projects / documents to exclude by name</param>
/// <param name="includes">Projects / documents to include by name</param>
public string SearchPropertiesParallel(List<IWorkspace> workspaces, string textToSearch, List<string> excludes, List<string> includes)
{
  StringBuilder result = new StringBuilder();
  string language = "";

  foreach (IWorkspace w in workspaces)
  {
    ISolution solution = w.CurrentSolution;

    foreach (IProject project in solution.Projects)
    {
      language = project.LanguageServices.Language;

      Parallel.ForEach(project.Documents, document =>
      {
        //Filter and include document names containing certain words
        if (!excludes.Any(s => document.FilePath.ToUpper().Contains(s)) &&
            (
              includes.Count() == 0 || includes.Any(s => document.FilePath.ToUpper().Contains(s)))
            )
        {
          if (language == LANG_CS)
          {
            result.Append(GetSearchPropertiesCSharp(document, textToSearch));
          }
        }
      });
    }
  }

  return result.ToString();
}

private string GetSearchPropertiesCSharp(IDocument document, string textToSearch)
{
  StringBuilder result = new StringBuilder();

  CommonSyntaxTree syntax = document.GetSyntaxTree();
  var root = (Roslyn.Compilers.CSharp.CompilationUnitSyntax)syntax.GetRoot();

  var methods = from method in root.DescendantNodes().Where(x => x is PropertyDeclarationSyntax)
                where ((PropertyDeclarationSyntax)method).Identifier.ValueText == textToSearch
                select method;

  if (methods != null && methods.Count() > 0)
  {
    foreach (Roslyn.Compilers.CSharp.SyntaxNode m in methods)
    {
      result.Append(GetMethodOrPropertyTextCSharp(m, document));
    }
  }

  return result.ToString();
} 

When the text or call or method or property is found, the method GetMethodOrPropertyText is called to get the body of the method / property in which the searched item is found. The full text of the method /property will be returned, including the path to the .cs file.
/// <summary>
/// Get the full text of the method or property body.
/// </summary>
/// <param name="node"></param>
/// <param name="document"></param>
/// <returns></returns>
private string GetMethodOrPropertyTextCSharp(Roslyn.Compilers.CSharp.SyntaxNode node, IDocument document)
{
  StringBuilder resultStringBuilder = new StringBuilder();

  string methodText = node.GetFullText();
  bool isMethod = node is Roslyn.Compilers.CSharp.MethodDeclarationSyntax;
  string methodOrPropertyDefinition = isMethod ? "Method: " : "Property: ";

  object methodName = isMethod ? ((Roslyn.Compilers.CSharp.MethodDeclarationSyntax)node).Identifier.Value : ((Roslyn.Compilers.CSharp.PropertyDeclarationSyntax)node).Identifier.Value;
  resultStringBuilder.AppendLine("//=====================================================================================");
  resultStringBuilder.AppendLine(document.FilePath);
  resultStringBuilder.AppendLine(methodOrPropertyDefinition + (string)methodName);
  resultStringBuilder.AppendLine(methodText);

  return resultStringBuilder.ToString();
} 
Jumping back to the Worker object above, when the worker is finished and has the results, the TabController.WriteResults method will be called to update the FastColoredTextBox with the results.
public static class TabController
{
  private static List<FastColoredTextBoxNS.FastColoredTextBox>
     _fastColoredTextBoxes = new List<FastColoredTextBoxNS.FastColoredTextBox>();

  /// <summary>
  /// The results of the search will be written to the tab specified with the guid
  /// </summary>
  /// <param name="guid"></param>
  /// <param name="text"></param>
  public static void WriteResults(Guid guid, string text)
  {
    //If another thread comes here, block it temporarily until this thread is finished.
    lock (_lockobj)
    {
      var selectFastColoredTextBox = from fctb in _fastColoredTextBoxes
                       where fctb.Guid == guid
                       select fctb;

      if (selectFastColoredTextBox != null && selectFastColoredTextBox.Count()==1)
      {
        FastColoredTextBox currentTextBox = (FastColoredTextBox)selectFastColoredTextBox.First();

        currentTextBox.Text = text;

        if (text == "") currentTextBox.Text = "Nothing found.";

        //move caret to start text
        currentTextBox.Selection.Start = Place.Empty;
        currentTextBox.DoCaretVisible();
      }
    }
  }
} 
As you will see in the source code, there is much more to it then I have shown in this article. For example, it is possible to start multiple searches independently at the same time from different tabs. This uses some threading and proper handling / locking. Also, the tool itself can search in both C# and VB.NET sourcecode.

About the Source Code

The projects attached will open up and build in Visual Studio 2010 SP1. In paragraph "How to use it" I explain the prerequisites that are necessary to use the tool.

Future of this project

Some thoughts about the direction this project might go in the future:

Visual Studio Extension 

This could be reworked as a Visual Studio extension. That way it could make use of the C# code editor and other parts of Visual Studio. That could make it even more powerful and accessible to more people.

Advanced stuff 

To make refactoring source code through multiple solutions friendlier, it would be nice if you could do some type of "queries" on your sourcecode, just like LINQ. Maybe this CodeProject article "C# as a Scripting Language in Your .NET Applications Using Roslyn" can be a starting point for this. Of course, that would need intellisense.. Maybe the new Roslyn "C# Interactive window" could be of use for this.

Output  

Let the user define what the output should contain, for example:
  • The whole code file
  • A graphical view of connections between methods / classes / solutions etc.

History   

01-08-2012   

  • Used Parallel.ForEach for searching. +/- 2x as fast with 2 processors, 4 processors not able to test, but probably 4x as fast.

28-07-2012     

  • More unittests (TabController)  
  • Use .Any() instead of Count() > 0 
  • Unittest to test performance of  @"A".ToUpper().Contains(@"B".ToUpper()) versus @"A".IndexOf(@"B", StringComparison.OrdinalIgnoreCase)  
24-07-2012   
  • Added unittests   
  • Able to search in VB.NET code also  
18-07-2012 
  • Added property search ability
  • Input check on search textbox
  • Remove leading / trailing spaces on text from search textbox when click [Search]
  • Show "Method:" or "Property:" depending on which searchtype is selected

16-07-2012
  • Fixed ability to Copy (Ctrl-C) from the FastColoredTextBox 
  • Show hourglass icon on tabs when threads are running 
  • If you click button [New tab] the program automatically jumps to the next tab
  • Separator lines between tabs 
  • Changed text of include / exclude text fields to better describe what they mean 
15-07-2012 

  • Fixed issue causing error with parentheses in search text
  • Added extra comments to source
  • Tested if different types of method definitions can be searched
  • Some refactoring: regions etc.
  • Added Messagebox for button "Update solution List".