Learn About Projects and Solutions: Visual Studio Downloads
Learn About Projects and Solutions: Visual Studio Downloads
view=vs-2017
Note
Create a solution
We'll start our exploration by creating an empty solution. After you get
to know Visual Studio, you probably won't find yourself creating empty
solutions very often. When you create a new project, Visual Studio
automatically creates a solution to house the project if there's not a
solution already open.
1. Open Visual Studio.
2. On the top menu bar, choose File > New > Project.
Add a project
Now let's add our first project to the solution. We'll start with an empty
project and add the items we need to the project.
Note
If you don't see Visual C# in the left pane of the dialog box, you
must install the .NET desktop development Visual Studio
workload. Visual Studio uses workload-based installation to install
only the components you need for the type of development you
do. An easy way to install a new workload is to choose the Open
Visual Studio Installer link in the bottom left corner of the Add
New Project dialog box. After Visual Studio Installer launches,
choose the .NET desktop development workload and then the
Modify button.
C#
using System;
namespace QuickDate
{
internal class Calendar
{
static void Main(string[] args)
{
DateTime now = GetCurrentDate();
Console.WriteLine($"Today's date is {now}");
Console.ReadLine();
}
You don't need to understand what the code does, but if you
want, you can run the program by pressing Ctrl+F5 and see that
it prints today's date to the console (or standard output) window.
It is common for solutions to contain more than one project, and often
these projects reference each other. Some projects in a solution might
be class libraries, some executable applications, and some might be unit
test projects or websites.
Let's add a unit test project to our solution. This time we'll start from a
project template so we don't have to add an additional code file to the
project.
1. From the right-click or context menu of Solution 'QuickSolution'
in Solution Explorer, choose Add > New Project.
We're going to use the new unit test project to test our method in the
QuickDate project, so we need to add a reference to that project. This
creates a build dependency between the two projects, meaning that
when you build the solution, QuickDate is built before QuickTest.
1. Now we'll add test code to the C# test code file. Replace the
contents of UnitTest1.cs with the following code:
C#
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace QuickTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestGetCurrentDate()
{
Assert.AreEqual(DateTime.Now.Date, QuickDate.Calendar.GetCurrentDate());
}
}
}
You'll see a red squiggle under some of the code. We'll fix this error by
making the test project a friend assembly to the QuickDate project.
Back in the QuickDate project, open the Calendar.cs file if it's not
already open. Add the following using statement and
InternalsVisibleToAttribute attribute to the top of the file to resolve the
error in the test project.
C#
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("QuickTest")]
Project properties
The property pages for the project open on the Application tab.
The property pages contain various settings for the project. Notice
that the assembly name of the QuickTest project is indeed
"QuickTest". If you wanted to change it, this is where you'd do
that. Then, when you build the test project, the name of the
resulting binary file would change from QuickTest.dll to whatever
you chose.
Next steps
If you want to check that your unit test is working, choose Test > Run
> All Tests from the menu bar. A window called Test Explorer opens,
and you should see that the TestGetCurrentDate test passes.
Tip
Note
This article applies to Visual Studio 2015. If you're looking for the latest
Visual Studio documentation, use the version selector at the top left. We
recommend upgrading to Visual Studio 2019. Download it here
Projects are the logical containers for everything that's needed to build
your application. When you create a project by choosing File | New |
Project from the main menu, Visual Studio creates a solution to contain
it. You can then add more new or existing projects to the solution if
necessary. You can create projects from existing code files and you can
create temporary projects (.NET only) that will be deleted when you are
done with them.
Note
The descriptions in this topic are based on the Visual Studio Community
edition. The dialog boxes and menu commands you see might differ
from those described here, depending on your settings or Visual Studio
edition. To change your settings, choose Import and Export Settings
on the Tools menu. For more information, see Customizing
Development Settings in Visual Studio.
File | New | Project from the main menu to bring up the New Project
dialog. In the left pane under Intalled | Templates chose the
programming language and platform or technology, then choose from
the available templates in the middle pane.
In the New Project dialog, the Solution drop-down gives you the
option to create the new project in a new or existing solution, or in a
new instance of Visual Studio.
Create a project from existing code files
If you have a collection of loose source files, you can easily create a
project that contains them. Choose File | New |Project From
Existing Code to start the Create Project from Existing Code Files
Wizard and follow the prompts.
Tip
By working with temporary projects, you can create and experiment with
a .NET project without specifying a disk location. When you create a
project, you just select a project type and template and specify a name
in the New Project dialog box. At any time while you are working with
the temporary project, you can save it, or you can discard it.
You must have .NET Framework 3.5 installed on your system to access
framework versions earlier than 4.0.
You can use Visual Studio to download and install sample solutions from
the MSDN Code Gallery.
Sometimes you might have a file that multiple projects refer to, or that
contains text or miscellaneous data that logically belongs at the solution
level rather than under a specific project. To add a single item to a
solution:
1. On the File menu, click New and then click New Project.
4. Set the Name and Location values for your solution, then click
OK.
After you create an empty solution, you can add new or existing
projects or items to it by clicking Add New Item or Add
Existing Item on the Project menu.
Deleting Solutions
You can delete a solution permanently, but not by using Visual Studio.
Before you delete a solution, move any projects that you might want to
use again in another solution. Then use File Explorer to delete the
directory that contains the .sln and .suo solution files.
Note
The .suo file is a hidden file that is not displayed under default File
Explorer settings.
To delete a solution
See Also
Introduction
Skill Levels
This guide is best suited to those with the following skill levels, by topic:
ASP.NET Intermediate
C# Intermediate
MVC Intermediate
Scope
This guide focuses on .NET Framework solutions and does not include
information on .NET Core solutions.
Structure
Prerequisites
Overview of Concepts
The repository pattern of design separates the logic that stores and
retrieves data from the business logic that acts on the data. This
provides a number of benefits, including eliminating duplication of code
and facilitating testing. In web applications, using the repository pattern
facilitates the writing of short, well-defined Controller actions (business
logic) that act on strongly typed data objects.
If all the code for the above was incorporated into one project, the
project directory would include folders and files for:
controllers
views
view models
entity models
repositories
the data context
EF migrations
Plus THE files for application startup, configuration, fonts, routing,
scripts, style sheets (CSS), and utility classes. That's a lot, and whenever
a change is made in one area the code for the whole has to be
redeployed.
Project Components
More complex solutions might also have separate projects for logging,
financial transactions, and other functions. To facilitate work on
solutions with many projects, Visual Studio provides selective loading of
projects in solutions with more than 30 projects, by default.
Each of the projects identified above might also be paralleled with a unit
test project.
1. Name: give the project the name you would like to assign to the
entire solution. This will typically be a name that describes the
whole application. You will change the name of the web project in
a forthcoming step.
2. Check Create directory for solution. This creates the proper
directory structure for a solution containing multiple projects.
Click Ok.
You will be taken to a modal dialog box where you can choose the type
of project:
3. Leave Add Unit Tests unchecked (you can add tests later, and
you're going to change the name of the associated project in the
next step).
Rename the New Project
However, the folder structure has not changed to reflect the new
naming convention. To correct that:
Before: /BlipProjects/BlipProjects
After: /BlipProjects/Blip.Web
5. Save the file, open Visual Studio, and reopen the solution.
Once you have set up your solution and the initial project correctly, it's
easy to add additional projects to the solution. The folder structure for
the projects and the solution file (.sln) will accurately reflect the names
of the new projects.
When you are building a multi-project web application, you only use the
MVC Web Application template for the first project. You should create
subsequent projects with the Empty template.
The process looks like this as we add the Blip.Entities project to the
BlipProjects example solution:
You will be taken to a modal dialog box where you can choose the type
of project:
Do the following:
2. Name: Blip.Entities.
3. Click Ok.
You will be taken to the modal dialog box:
Do the following:
Blip.Data
Blip.Common
When you are finished, the Solution Explorer should look like this if you
have added test projects along with the empty .NET Framework
projects:
Visual Studio Solution Explorer
Note that the project icons are different for empty .NET Framework
projects.
App_Data
Entities
The revised folder structure for Blip.Web should look like this:
Visual Studio Solution Explorer: Blip.Web folder structure
While working with multiple projects adds some complexity on the front
end, it makes it easier to focus on the relevant code when developing
each tier of the application; folders for other tiers are hidden when the
Solution Explorer tree is rolled up for those projects.
Once the projects are step up, it's a good time to update the NuGet
packages that are part of each project template. At this point, we can
also add the specific NuGet packages needed by the projects that
provide the functionality for each level of our web application.
To see the package manager for the solution, be sure to highlight the
solution in the Solution Explorer, then right-click and select Manage
NuGet packages for solution... or select Tools / NuGet Package
Manager / Manage NuGet Packages for Solution...
You can also see the package manager for just a project by highlighting
the project in the Solution Explorer and following either of those steps.
If there are new releases of any of the packages in your solution, the
count of updated packages will appear next to the Updates tab and the
list will appear below. You can choose to update some or all of the
packages in your solution.
The NuGet package manager will only update the instances of package
associated with projects in which the package is already installed, so you
don't have to worry about it installing updates in projects where the
packages don't belong.
Add NuGet Packages
1. Open the NuGet Package Manager and select the Browse tab.
Entity Framework should appear near the top of the list of
packages. If it doesn't, be sure Package source is set to "All" or
"nuget.org".
4. Click Install.
The package manager will chug through the installation process and
present you with a couple of confirmation dialog boxes to accept.
Although Blip.Data provides the data context for our solution and does
the work of transferring data to the database through the repository
classes we're going to set up later, Blip.Web still needs the EF libraries
so that the state of data objects can be preserved between projects.
Blip.Data
Blip.Entities
Blip.Web
That takes care of the package management. You can close the NuGet
Package Manager window.
Blip.Entities Project
Having taken care of adding and updating the libraries we're going to
use, we can begin putting together the code for our solution. Since the
demonstration project is to convert a single project solution to a multi-
project solution, we'll look at the differences between the two as we go
along.
Let's start with the Blip.Entities projects, which contains the POCO's
(plain-old class objects) that define the data entities for our solution.
Entity Folders
Customer.ViewModels
Geographies
Note that you can use dots (".") as separators in folder names. This
follows the convention that Visual Studio uses by default to name unit
test projects.
Keep in mind that the folder structure and naming convention effect the
default/automatic namespace structure for the classes. For example:
Folder Namespace
Blip.Entities\Customers Blip.Entities.Customer
Blip.Entities\Customer.ViewModels Blip.Entities.Customer.ViewModels
Blip.Entities\Customer.ViewModels\P Blip.Entities.Customer.ViewModels.P
Folder Namespace
artial artial
Note that in the second example above the dot structure of the folder
name provides a fourth tier of the namespace structure while keeping
the folder structure flat under the project. Using a well-designed folder
and namespace structure helps implement separation of concerns and
limits the number and scope of classes referenced by using statements
elsewhere in the solution. We'll see that in action as we move along.
Entity Classes
3. Name the class "Customer". Visual Studio will add the ".cs"
extension whether or not you include it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Blip.Entities.Customer
{
class Customer
{
}
}
csharp
Note the namespace and default using statements.
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Blip.Entities.Geographies;
namespace Blip.Entities.Customer
{
public class Customer
{
[Key]
[Column(Order = 1)]
public Guid CustomerID { get; set; }
[Required]
[MaxLength(128)]
public string CustomerName { get; set; }
[Required]
[MaxLength(3)]
public string CountryIso3 { get; set; }
[MaxLength(3)]
public string RegionCode { get; set; }
Note that the using statements have changed and the namespace
reflects the folder structure we defined previously.
When you look at the using statements, you'll see that one of them
references another namespace within the Blip.Entities project:
using Blip.Entities.Geographies;
csharp
Folders and folder names formed with dot (".") separators form separate
namespaces. Like the .NET Framework, higher tiers of the namespace
structure are not supersets of the namespace; they do not contain the
classes from the namespaces below.
In the single project solution version of this project, the Customer class
looks like this:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BlipDrop.Models
{
public class Customer
{
[Key]
[Column(Order = 1)]
public Guid CustomerID { get; set; }
[Required]
[MaxLength(128)]
public string CustomerName { get; set; }
[Required]
[MaxLength(3)]
public string CountryIso3 { get; set; }
[MaxLength(3)]
public string RegionCode { get; set; }
If you are following along with the Blip.Projects example solution and
comparing it to the BlipDrop solution, you'll see we have the same
entities, but they're arranged as follows:
Folder/Namespace Class
Customer Customer
Customer.ViewModels CustomersDisplayViewModel
CustomerEditViewModel
Geographies Country
Region
You can see that we're grouping related classes together; view models
relating to the Customer object are in the same namespace and entities
relating to geospatial information are grouped together in the
Blip.Entities.Geographies namespace. The way you structure your
folders, classes, and namespaces will be dependent on the requirements
of your solution.
We've created our entities project, but how do we use those classes in
other projects? The using statements for each class can namespaces
from other projects, but to make the namespaces visible to a project we
need to add references to the other projects.
3. Select Add References... from the context menu. This will open
the Reference Manager modal dialog box.
6. Click Ok.
If you expand the tree view under the References node of Blip.Web,
you should see the other projects among the list of references.
Structural Considerations
"If our web project is exchanging data with the database through the
repositories implemented in Blip.Data, why do we also need to reference
Blip.Entities?
The view models are the return types for repository methods, so
Blip.Data needs to know about them, as well as the entities
themselves.
The controller actions and views are almost the same in the
Blip.Projects multi-project solution and the BlipDrop single project
solution. Rather than slog through the process of creating the individual
classes, let's look just at where they differ.
Controller Actions
Because the single project solution also used the repository pattern, the
only changes are in the references to other projects and to the
namespaces.
Each file below is identified by its full path starting at the solution level.
BlipDrop\BlipDrop\Controllers\CustomerController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using BlipDrop.Data;
using BlipDrop.ViewModels;
namespace BlipDrop.Controllers
{
public class CustomerController : Controller
{
// GET: Customer
public ActionResult Index()
{
var repo = new CustomersRepository();
var customerList = repo.GetCustomers();
return View(customerList);
}
...
}
}
csharp
BlipProjects\Blip.Web\Controllers\CustomerController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using Blip.Data;
using Blip.Entities.Customer.ViewModels;
namespace Blip.Web.Controllers
{
public class CustomerController : Controller
{
// GET: Customer
public ActionResult Index()
{
var repo = new CustomersRepository();
var customerList = repo.GetCustomers();
return View(customerList);
}
...
}
}
csharp
BlipDrop\BlipDrop\Views\Customer\Create.cshtml
@model BlipDrop.ViewModels.CustomerEditViewModel
@model Blip.Entities.Customer.ViewModels.CustomerEditViewModel
@{ ViewBag.Title = "Customer"; }
<h2>Customer</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Create a new customer by entering the customer name,
country, and region.</h4>
...
}
csharp
BlipProjects\Blip.Web\Views\Customer\Create.cshtml
@model Blip.Entities.Customer.ViewModels.CustomerEditViewModel
@{ ViewBag.Title = "Customer"; }
<h2>Customer</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Create a new customer by entering the customer name,
country, and region.</h4>
...
}
csharp
Aside from the @model directive, the code for the two views is the
same.
Blip.Data Project
We can put the physical database files in an App_Data folder under the
project folder. Alternately, if we don't specify a specific location for the
files, Visual Studio will put databases created on the local SQL Server
Express development server in the following directory:
If we use the App_Data directory, we'll need to specify the path in the
configuration files.
Note that it's not a good idea to add the physical database files to the
project because Git won't be able to open them to write changes,
leaving you unable to commit changes to your Git repository until you
remove the database files from the project, which will require removing
them from the directory in which they are located.
Connection strings are the source of much anguish for developers. While
this guide will endeavor to steer you around a number of connection
string landmines, your development environment--and therefore the
problems you encounter--may vary. The Other Resources section of this
guide includes some valuable resources devoted to the murky depths of
connection strings.
Project File
Blip.Data App.config
Blip.Web Web.config
These are XML format files. In both projects the .config files are found in
the project root, below the folders.
<connectionStrings>
<add name="ApplicationDbContext" connectionString="Data
Source=(localdb)\ProjectsV13;Initial Catalog=BlipData;Integrated
Security=SSPI;AttachDBFilename=BlipData.mdf"
providerName="System.Data.SqlClient" />
</connectionStrings>
xml
Note the following important points to identify some of the ways your
connection string might need to differ in order to work:
The data context for our solution is simple and straightforward. There
are a few setup requirements and a few portions which need to be
maintained manually as the development of the application progresses.
Blip.Data\Context\Context.cs
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using Blip.Entities.Customer;
using Blip.Entities.Geographies;
namespace Blip.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext() : base("ApplicationDbContext")
{
}
public DbSet<Country> Countries { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Region> Regions { get; set; }
}
}
}
csharp
As you add entity classes to the Entities project of your solutions, you
need to keep the list of entities in the context up to date. In the
BlipProjects solution, we have three entities included in the context:
DbSet<Country> Countries
DbSet<Customer> Customers
DbSet<Region> Regions
csharp
The type of each DbSet collection is one of the classes from our
entities project.
Enabling EF Migrations
If you don't see a PMC tab, select View / Other Windows / Package
Manager Console from the menu bar. You should see a window like
this in the lower third of your Visual Studio window:
Entity Framework should chug away for a while and then inform you
that migrations have been enabled. A sign that the process has worked
correctly is the presence of a Migrations folder under the root of the
data project in Solution Explorer and a Configuration.cs file in that
folder. We'll use that file in the next step.
Configuring EF Migrations
namespace Blip.Data.Migrations
{
// using System;
using System.Collections.Generic;
// using System.Data.Entity;
using System.Data.Entity.Migrations;
// using System.Linq;
using Blip.Entities.Geographies;
AutomaticMigrationsEnabled = false;
When automatic migrations are turned on, the EF default, EF will create
and apply a migration every time you make a change to your entities
that will affect the structure of the database. Early in the development
of an application, the entities may undergo a number of changes,
leading to a rapid proliferation of migrations. This is to be avoided.
Seed Method
Now that we've configured our EF migrations, we're ready to create the
database. That's done with an initial migration.
add-migration InitialMigration
3. Press Enter.
`201708222251192_InitialMigration.cs`
The -verbose argument will display the full output of the command as it
executes.
3. Press Enter.
If the process is executing correctly, the first few lines of output in the
PMC window will look something like this:
Note that the Startup project and NuGet project are both set to the
name of the data project: Blip.Data. The output also identifies the
database server and name. Following the configuration information is
the T-SQL DDL generated from the methods in the migration file.
Now that you have a database containing some data to test against, you
can begin developing repositories. Repository classes typically contain
methods for each of the create, update, retrieve, and delete functions
associated with databases and other permanent data stores, depending
on the type of data store.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using Blip.Entities.Customer;
using Blip.Entities.Customer.ViewModels;
namespace Blip.Data
{
public class CustomersRepository
{
public List<CustomerDisplayViewModel> GetCustomers()
{
using (var context = new ApplicationDbContext())
{
List<Customer> customers = new List<Customer>();
customers = context.Customers.AsNoTracking()
.Include(x => x.Country)
.Include(x => x.Region)
.ToList();
if (customers != null)
{
List<CustomerDisplayViewModel> customersDisplay =
new List<CustomerDisplayViewModel>();
foreach (var x in customers)
{
var customerDisplay = new
CustomerDisplayViewModel()
{
CustomerID = x.CustomerID,
CustomerName = x.CustomerName,
CountryName = x.Country.CountryNameEnglish,
RegionName = x.Region.RegionNameEnglish
};
customersDisplay.Add(customerDisplay);
}
return customersDisplay;
}
return null;
}
}
...
}
}
csharp
The entities and view models from the Blip.Entities project are
referenced in the using statements.
That's it! If you've followed along on your own to this point, you should
be ready to rumble. The BlipProjects sample solution on GitHub is
ready to run. (You may need to customize the connection string for your
local environment, but the solution will create the database on the first
run.)
Keep in mind that if you change your entity model by adding, removing,
or modifying entities (not view models), you need to do the following
before you can run and debug your solution:
The various .Test projects just exist to show the multi-project solution
structure; they don't contain any tests. Writing unit tests is beyond the
scope of this guide, but there are relevant PluralSight courses
referenced below.
Visual Studio may, for reasons of its own, convert your blank .NET
Framework projects to web template projects. When it does this, it
renames the App.config file to Web.config. It also changes the
project type GUID in the .csproj file. It does not add the additional
NuGet packages associated with web template projects. It also does not
change the contents of the App.config file, it just renames it.
This is the process for converting these projects back to basic .NET
Framework projects:
3. In a text editor, open the .csproj file associated with the project.
4. Find the ProjectTypeGuids element, which looks like this:
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};
{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
xml
5. Delete the first GUID and the semicolon after it, leaving
{fae04ec0-301f-11d3-bf4b-00c04f79efbc} as the sole project type
GUID.
6. Reopen the solution in Visual Studio. The project type icon should
be a green "C#", not a green globe.
More Information
If you want to dive deeper into the topics discussed in this guide, the
following is a selected list of resources.
Technology Course(s)
Project Repo
BlipDrop https://github.com/ajsaulsberry/BlipDrop
BlipProjects https://github.com/ajsaulsberry/BlipProjects
You can fork the projects, run the code, and experiment on your own.
Note that the sample project is not intended to be a real-life case study
or production code; it exists to illustrate the topics covered in this guide.