Team Development with Visual Studio .NET and Visual SourceSafe
Structuring Solutions and Projects
Summary: This chapter explains how you should organize and structure Visual Studio .NET solutions and projects, and it presents the tradeoffs associated with single-solution and multi-solution development models. It also recommends folder structures that you can use to store projects locally and within Visual SourceSafe (VSS).
This is Chapter 3 of the Team Development with Visual Studio® .NET and Visual SourceSafe guide. Start here to get the full picture.
To ensure that your development and build processes work effectively in a team environment, it's essential to start with a correct project structure that is consistent across all of your development workstations and build server.
This chapter presents guidelines on:
- Partitioning Microsoft® Visual Studio® .NET solutions and projects.
- Managing the local file system and Microsoft Visual SourceSafe (VSS) folder structure.
- Applying naming conventions for projects, assemblies and namespaces.
Visual Studio .NET Solutions and Projects
Before discussing how to organize Visual Studio .NET solutions and projects, it's very important that you understand their basic mechanics and how they are managed locally and by your source control providertypically VSS.
If you are already familiar with Visual Studio .NET solutions and projects and you understand the various file types comprising a project, you can skip this section and jump to Always Use Visual Studio .NET for Source Control Operations.
Visual Studio .NET Projects
Visual Studio .NET uses project files as containers for all of the build and configuration settings required to generate a .NET assembly. Project files have either a .csproj or .vbproj file extension, depending upon the project's language. There are many different project types [and associated Rapid Application Development (RAD) templates] although these can be divided broadly into two categories. The two categories of project types are:
- Web projects. A Web project is one that is created with a Hypertext Transfer Protocol (HTTP) location (for example, http://localhost/MyWebProject). Web projects include ASP.NET Web Applications used to deliver content to Web browsers and ASP.NET Web Services used primarily for data integration over the Internet.
- Non-Web or local projects. Non-Web or local projects are created with a file system location (for example, C:\Projects\MySystem\MySolution\ MyWinProject).
The most common local project types are Windows applications and class libraries, although there are many others, including services, console applications, database projects, and so on.
Visual Studio .NET Solutions
Solution files (with the .sln file extension) are used to group related projects together and are primarily used to control the build process. You can use solutions to control build dependency issues and control the precise order in which contained projects are built.
Important A project can be part of one or more solutions but solutions can't be included within other solutions.
Figure 3.1 illustrates the relationship between projects and solutions and indicates the file types used by VSS to maintain solution and project level settings:
Figure 3.1. Visual Studio .NET Projects and Solutions
Solutions and Build Dependencies
The solution file also contains project dependency information used by the build process. For example, in the preceding diagram, the dependency information indicates that Project A depends upon Project B and Project B depends upon Project C. As a result, the build order must be Project C, then Project B, and then Project A. When project references are used within a single solution, Visual Studio .NET ensures the correct build order.
Important There are two basic types of referencesproject references and file references. You set both types by using the Add References dialog box in Visual Studio .NET. Because project references also establish build order dependencies, you should use them whenever possible. For more information, see Referencing Assemblies in Chapter 4, "Managing Dependencies."
Files Subject to Source Control
The following list identifies the key file types that are automatically added to VSS when a solution is added to source control by using the Visual Studio .NET integrated development environment (IDE):
- Solution files (*.sln). The key items maintained within these files include a list of constituent projects, dependency information, build configuration details, and source control provider details.
- Project files (*.csproj or *.vbproj). The key items maintained within these files include assembly build settings, referenced assemblies (by name and path), and a file inventory.
- Application configuration files. These are configuration files based on Extensible Markup Language (XML) used to control various aspects of your project's run time behavior.
Note For Web applications, the source controlled configuration file is called Web.config. For non-Web applications, the source controlled file is called app.config and is contained within the project folder. At run time, the Visual Studio .NET build system copies App.config to the Bin folder and renames it to Yourappname.exe.config.
For non-Web applications, a configuration file is not automatically added to a new project. If you require one, add it manually. Make sure you call it App.config and locate it within the project folder.
- Source files (*.cs, *.vb, *.aspx, *.asax, *.resx, *.vsdisco, *.css, and so on). All project source files are subject to source control.
Files Not Subject to Source Control
The following files are not added to source control because they are developer specific:
- Solution user option files (*.suo). These contain personalized customizations made to the IDE by an individual developer.
- Project user option files (*.csproj.user or *.vbproj.user). These contain developer specific project options and an optional reference path that is used by the IDE to locate referenced assemblies. The section Referencing Assemblies in Chapter 4, "Managing Dependencies," explains how assembly references should be managed in a team environment.
- WebInfo files (*.csproj.webinfo or *.vbproj.webinfo). This file keeps track of a project's virtual root location. This is not added to source control to allow individual developers to specify different virtual roots for their own working copy of the project. While this capability exists, you and all team members are recommended to use a consistent (local) virtual root location when you develop Web applications. The recommended structure for Web and non-Web applications is discussed in Use a Consistent Folder Structure for Solutions and Projects.
- Build outputs that include assembly dynamic-link libraries (DLLs), Interop assembly DLLs and executable files. However, note that you are advised to add outer-system assemblies that are not built as part of your system build process (such as third-party controls and libraries) to VSS within the projects that reference them. For details, see Include Outer System Assemblies within Projects in Chapter 4, "Managing Dependencies."
All project creation and manipulation within VSS should be performed by using the integrated VSS support menus within Visual Studio .NETdon't use VSS Explorer for this. The Visual Studio .NET functionality guarantees that:
- Only the appropriate files are added to source control.
- Your Visual Studio .NET project and solution files are updated with appropriate VSS-specific details. For example, the VSS functionality within Visual Studio .NET updates solution (.sln) files with (among other items):
- A count of projects in the solution that are under source control (this count includes the solution itself)
- The VSS server for each project
- The location on the server for each project
- The name of each project's source-control provider
- Each project's location relative to the solution file
Other files, including the solution user file (.suo) and project file (.csproj or .vbproj), are also updated.
Important Always interact with VSS through the Visual Studio .NET interface instead of through the VSS Explorer. The tight integration of the products ensures that files are managed correctly in a team environment.
Partitioning Solutions and Projects
The way you partition solutions and projects has a big impact on the way your development efforts and build process function in a team environment.
There are three main models to consider for partitioning solutions and projects. In order of preference, these are:
- Single solution
- Partitioned single solution
Important Unless you have very good reasons to use a multi-solution model, you should avoid this and adopt either a single solution model, or in larger systems, a partitioned single solution model. These are simpler to work with and offer a number of significant advantages over the multi-solution model, which are discussed in the following sections.
Use a Single Solution Model Whenever Possible
With the single solution model, you create a single Visual Studio .NET solution and use it as a container for all of the projects defined by your application. Note the following when you use a single solution model:
- If one project needs to reference an assembly generated by another, use project references.
- File references should be used only to reference outer-system assemblies (such as .NET Framework assemblies and third-party assemblies) that are not built with the rest of your system.
Figure 3.2. The Single Solution Model
Use a single solution model whenever possible because it offers a number of significant advantages.
The single solution model offers the following advantages:
- When you need to reference another assembly generated by a separate project, you can use a project reference. Project references are the preferred way to establish references to other assemblies and they are guaranteed to work on all development workstations in a team environment. The many advantages of project references and guidance on when to use file references are discussed in Referencing Assemblies in Chapter 4, "Managing Dependencies."
- Assembly versioning issues are avoided, because Visual Studio .NET detects when a client of a referenced assembly needs to be rebuilt.
- Project references are sensitive to changes in the configuration of the referenced project. This means that you can automatically switch from Debug and Release builds across projects without having to reset references.
- The system build process and build script is much simpler.
You are advised to adopt the single solution model whenever possible. However:
- The model scales only so far. If you want to work on a single project within the solution, you are forced to acquire all of the source code for all projects within the solution.
- Even minor (nonbreaking) changes to a single source file within a single project can result in a rebuild of many projects within the solution, due to project dependencies. If an assembly's interface changes within a referenced project, you want the client project to be rebuilt. However, unnecessary rebuilds can be very time consuming, especially for solutions containing many projects.
Consider a Partitioned Single Solution Model for Larger Systems
For larger systems where you want to reduce the number of projects and source files required on each development workstation, consider grouping related sets of projects together within separate subsolution files.
This allows you and fellow developers to work on separate and smaller subsystems within the inner-system boundary.
Note A single project file can be included within one or more solution files. Solutions cannot be included within other solutions.
Figure 3.3 illustrates the partitioned single solution model. Notice how separate solution files are used to allow you to work on smaller subsystems contained within the inner system boundary. Also note how this results in projects being contained within more than one solution file. For example, Projects D and H are in a total of three solution files including the master solution.
Figure 3.3. The Partitioned Single Solution Model
In the partitioned single solution model:
- All projects are contained within a master solution file. This is used by the system build process to rebuild the entire system. If you need to work on the top level project file, you also work with the master solution.
- Project references are used between individual projects.
- Separate solution files are introduced for selected project files. If you want, you can introduce a solution file for each project within your system. Each solution file contains the main project file, together with any downstream project it depends on and any further projects it depends on, and so on down the dependency chain.
- Separate solution files allow you to work on smaller subsystems within your overall system but retain the key benefits of project references. Within each subsolution file, project references are used between constituent projects.
Note You should not separate two projects that reference one another into separate solutions, because this would necessitate the use of a file reference which should be avoided wherever possible. For more information, see Referencing Assemblies in Chapter 4, "Managing Dependencies".
The partitioned single solution model offers the following advantages:
- You can work on small subsystems. You do not require the source code and project files for the entire system on every development workstation. As a result, the Solution Explorer within Visual Studio .NET remains less cluttered and easier to work with.
- You can use project references within each solution.
- The master solution allows you to easily rebuild the entire system. The master solution file is used by the build process on the build server.
The partitioned single solution model suffers from the following disadvantages:
- When new projects are added, you must potentially add them and update any project references in multiple solution files; for example, the master solution file and one or more others.
- You are limited in the way you can partition your system. This is driven by project dependency relationships. As a result, if you work on the top level project, for example, an ASP.NET Web application within your presentation tier, you are forced to copy all dependent projects to your development workstation. This is likely to include projects from your business and data tiers. On the other hand, if you work on the development of a class library or data access component with few if any further dependencies, you require only those individual projects.
Use a Multi-Solution Model Only if Absolutely Necessary
The multi-solution model is similar to the partitioned single solution model except:
- There is no master solution file.
- File references are used between projects in separate solutions (although project references are still used between projects within an individual solution).
- The system build script that runs on the build server builds each solution in turn, based on known dependency relationships. The build script places output assemblies in a fixed location on the build server.
The multi-solution model is illustrated in Figure 3.4:
Figure 3.4. The Multi-Solution Model
The multi-solution model offers the following advantages over the partitioned single solution model:
- Each project is contained only within a single solution. This means that adding and removing projects to and from your system is easier.
- You are able to subdivide your system into multiple solutions based on logical boundaries and you are not driven by project dependencies. For example, the division that you choose may be based on areas of business functionality.
The multi-solution model suffers from the following disadvantages:
- You are forced to use file references when you need to reference an assembly generated by a project in a separate solution. These (unlike project references) do not automatically set up build dependencies. This means that you must address the issue of solution build order within the system build script. While this can be managed, it adds extra complexity to the build process.
- You are also forced to reference a specific configuration build of a DLL (for example, the Release or Debug version). Project references automatically manage this and reference the currently active configuration in Visual Studio .NET.
- When you work with single solutions, you can get the latest code (perhaps in other projects) developed by other team members to perform local integration testing. You can confirm that nothing breaks before you check your code back into VSS ready for the next system build. In a multi-solution system this is much harder to do, because you can test your solution against other solutions only by using the results of the previous system build.
You should easily be able to work with a single solution that contains 10, 20, or even 30, projects. The maximum number of workable projects within a solution is difficult to precisely define, because it depends on the specification of your build server and development workstations and the size and number of source files associated with individual projects.
Considerations for Grouping Projects into Solutions
The best way to approach the problem of grouping projects into solutions is to consider the overall architecture of your application. For example:
- Start by identifying the constituent assemblies (or components) within your system; this defines the individual projects that your system requires. Remember that each assembly is generated by a separate Visual Studio .NET project.
- Aim for a single solution model. If you want to break up your projects to provide a greater level of isolation and control, you can use the partitioned single solution model. Think about which groups of projects you want to work on in isolation, for example a set of middle-tier business components, and create separate solutions accordingly.
If you break your projects into multiple solutions and cannot do so using the partitioned single solution model, carefully consider your cross solution dependencies, and the nature of the interfaces that separate the two dependent assemblies. When you organize projects into separate solutions you should:
- Identify the external interfaces that separate the constituent parts of your system. Try to identify those that are least likely to change. If an interface that is exposed by one project changes frequently, any dependent projects should ideally be placed in the same solution.
- If you are using Web services to connect some components of your system together, the Web service interface provides a good dividing line. For example, the projects on the client side of the Web service interface and the projects on the server side are good candidates for separate solutions.
Life in a team development environment is a whole lot easier if you use a common structure for storing Visual Studio .NET solutions and projects. To keep things symmetrical (and as a result, simpler), set up a folder structure within VSS that matches your local file system structure.
Define a Common Root Folder
Define a common root folderfor example, C:\Projects on your file system and $/Projects within VSS. This acts as a container for all of your development systems.
Beneath the common root folder, create a system root folder for each of your systemsfor example, C:\Projects\MyCompanysInsuranceApp and $/Projects/MyCompanysInsuranceApp respectively.
Adopt a Parent-Child Folder Structure for Solutions and Projects
You should adopt a parent-child folder structure for solutions and projects. To do this:
- Create a subfolder beneath the system root folder for your Visual Studio .NET solution.
- Create additional child subfolders beneath the solution folder for each constituent project.
- Adopt this common structure for Web and non-Web applications
- Do not add the Bin folder or its contents to VSS.
Note If you use the partitioned single solution model, place project folders beneath the folder that contains the main solution file. For any subsolutions that you create, include project files directly from this location.
Figure 3.5 demonstrates the recommended structure:
Figure 3.5. Visual Studio .NET and VSS Folder Structure
The following subsections describe how to use the Visual Studio .NET IDE to create the appropriate structures for Web and non-Web applications.
How to Create a New ASP.NET Web Project
By default, when you create a new ASP.NET Web application, the project file is located in a nominated virtual root beneath your default Web site (usually \inetpub\wwwroot) and the associated solution file is located beneath \My Documents\Visual Studio Projects. This default arrangement is not ideal in a team development environment because it breaks the symmetrical structure between your VSS projects and local files.
The following steps guide you through the creation of a new ASP.NET Web application in accordance with the recommended solution and project folder structure. The steps assume a solution called MyWebAppSolution and a project call MyWebApp.
If you include the word Solution (or Soln) in the solution name, this helps to clearly differentiate it from the project file name.
The desired structure is shown in Figure 3.6. Note that the project folder and Microsoft Internet Information Server (IIS) virtual root are one and the same.
Figure 3.6. Recommended Web Application Structure
To create a new Web application with this structure
- Open Visual Studio .NET, and on the File menu, point to New, and then click Blank Solution.
- Enter MyWebAppSolution as the solution name, and then set its location to \Projects\MySystem.
- Click OK. Visual Studio .NET creates the MyWebAppSolution folder beneath the specified root folder location.
- Use Windows Explorer to create a new subfolder called MyWebApp beneath the \Projects\MySystem\MyWebAppSolution folder.
- Use either Windows Explorer or Internet Services Manager to establish the MyWebApp folder as an IIS virtual root.
Note Visual Studio .NET requires that the virtual root name match the project folder name.
- Return to Visual Studio .NET, and on the File menu, point to Add Project, and then click New Project.
- Add an ASP.Net Web Application project.
- In the Location field, enter http://localhost/MyWebApp, and then click OK. The project and Web source files will be created in the specified virtual root.
How to Split a Web Application into Multiple Projects
In some cases, you may want to divide your Web application into multiple projects within the same solution to facilitate team development. For example, if you have separate teams responsible for different aspects of the same Web application, it is useful to be able to divide the application into multiple projects. This capability is not supported natively by Visual Studio .NET, but it can be achieved with some manual manipulation of virtual roots.
Important A single project approach is simpler, so split a Web application up into multiple projects only if absolutely necessarytypically for very large Web applications.
For more detailed information about creating sub Web projects, see article Q307467, "HOWTO: Compose a Visual Studio 7 .NET ASP.NET application out of multiple project to facilitate team development," in the Microsoft Knowledge Base (available soon).
Working with ASP.NET Code-Behind Files
You should be aware that when you check out a Web form (aspx) file or a code-behind file (aspx.cs or aspx.vb), Visual Studio .NET automatically checks out both files. This is by design because user interface changes generally involve an aspect of coding within the code-behind file.
For example, if you add a new control to a Web form, Visual Studio .NET automatically creates a member variable for the control in the code-behind file.
How to Create a New Non-Web Project
The following steps guide you through the creation of a new non-Web project type such as a Windows-based or console application, a class library or a service. The steps assume a solution called MyWinAppSolution and a project call MyWinApp. The desired structure is shown in Figure 3.7:
Figure 3.7. Recommended Project Structure for non-Web Projects
To create a new non-Web application with this structure
- Open Visual Studio .NET, and on the File menu, point to New, and then click Project.
- Select the appropriate project type and template.
- Enter the project name in the Name field (in this example MyWinApp).
- Set the Location field to \Projects\MySystem.
- Click the More button.
- Select the Create directory for solution check box. This causes the project file to be created in a project sub folder beneath the solution folder.
- Enter MyWinAppSolution into the New Solution Name field.
- Click OK to complete the project and solution creation process.
For information about how to add the newly created project and solution to Visual SourceSafe, see How to Check In a New Solution to VSS in Chapter 6, "Working with Visual SourceSafe."
Give careful and early consideration to the way you name projects, assemblies, folders, and namespaces. While it is possible to rename these items later on in the development cycle, you should avoid this if possible. For information about how to rename a project, see Renaming a Project in Chapter 6, "Working with Visual SourceSafe."
You should also aim for a consistent set of names because this can greatly simplify project organization.
Use Common Names for Projects and Assemblies
Your output assembly name should always match the project name from which it is generated. For example, you should be able to assume that an assembly called MyCompany.Utilities.Data.dll is generated by a project called MyCompany.Utilities.Data.
If you change the name of an output assembly, consider changing the project name to match, and vice-versa.
Use a Common Root Namespace Name
The root namespace into which you place your types (structures, classes, interfaces, and so on) should match the project and assembly name.
For example, use MyCompany.Utilities.Data as the root namespace within the MyCompany.Utilities.Data.dll assembly.
While .NET does not require this alignment, it makes sense to synchronize names because it then becomes easy to tell which types live in which assemblies.
Note Microsoft Visual Basic® .NET projects expose the root namespace via project properties. By default, any type created within the Visual Basic project will be placed inside this namespace. If you use explicit namespace statements in your Visual Basic .NET project, delete the root namespace entry, otherwise the explicit namespace name is appended to the root namespace name.
C# projects expose a default namespace property via project properties. This is again used to determine the namespace into which new types added to the project are placed. However, unlike Visual Basic .NET projects, the root namespace is explicitly stated via namespace statements within your source files.
Use Common Names for VSS and Local Folders
As mentioned earlier, keep your local solution and project folder names in synchronization with their equivalent VSS folder names.
This is Chapter 3 of the Team Development with Visual Studio .NET and Visual SourceSafe guide. To read the next chapter, please see Chapter 4, "Managing Dependencies."