Run Multiple Projects in Visual Studio Code

While expanding the sample used in the Electron.NET post to include an API I hit a snag with Visual Studio Code. I have used multiple projects with Visual Studio Code before, but never two distinct applications that I need to run at the same time.  Sample code that contains the two projects, but before any modifications covered in this post can be found here.

Building Multiple Projects

The first issue to tackle was getting both projects to build since they are technically independent. As I’m sure you are aware VS Code doesn’t need a solution file like full Visual Studio does. What I learned during this process was that while a solution file isn’t required once can be used to ensure multiple projects all get built. Using VS Code’s Terminal I ran the following commands to create a new solution and add my two projects to that solution.

dotnet new sln -n ElectronTest
dotnet sln add src/ElectronTest/ElectronTest.csproj
dotnet sln add src/ContactsApi/ContactsApi.csproj

With the solution file in place, I then opened the tasks.json file found in the .vscode directory. In the build task, I removed the path to a specific csproj file and just used the workspace folder.

Before:
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
    "build",
    "${workspaceFolder}/src/ElectronTest/ElectronTest.csproj"
]

After:
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
    "build",
    "${workspaceFolder}"
]

This is just one option on how to handle the builds. I believe another way would be to have two build tasks (one for each project) and use the individual build task in your launch configurations (which we are going to talk about next).

Debugging Multiple Projects

Open up your launch.json file in the .vscode directory. By default you will see a couple of configurations more than likely you will see one named .NET Core Attach and another named .NET Core Launch (web). It is the .NET Core Launch (web) configuration that we are interested in. This configuration controls how our application is launched. If you notice the program property points to the dll created during the build process, in the sample code this is  ${workspaceFolder}/src/ElectronTest/bin/Debug/netcoreapp2.0/ElectronTest.dll. This is all fine but doesn’t give us a way to run both of our projects. Let’s tweak the env property to set the ASPNETCORE_URLS which will control the URL the application is launched with.

Before:
"env": {
    "ASPNETCORE_ENVIRONMENT": "Development"
}

After:
"env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://localhost:5001"
}

Now that the original application’s launch configuration is set we need to add a second launch configuration for our second application. The following is the full configuration section for my second application (the API).

{
    "name": ".NET Core Launch (API)",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "build",
    "program": "${workspaceRoot}/src/ContactsApi/bin/Debug/netcoreapp2.0/ContactsApi.dll",
    "args": [],
    "cwd": "${workspaceRoot}/src/ContactsApi",
    "stopAtEntry": false,
    "launchBrowser": {
        "enabled": false,
        "args": "${auto-detect-url}",
        "windows": {
            "command": "cmd.exe",
            "args": "/C start ${auto-detect-url}"
        },
        "osx": {
            "command": "open"
        },
        "linux": {
            "command": "xdg-open"
        }
    },
    "env": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_URLS": "http://localhost:5000"
    },
    "sourceFileMap": {
        "/Views": "${workspaceRoot}/Views"
    }
}

I started with a copy of the original application and just modified a couple of things. First, the name property is now .NET Core Launch (API) which will help us know which application we are launching later. In the launchBrowser section, I set enabled to false since this is an API and I don’t need to launch the browser when the application starts. The final difference is in ASPNETCORE_URLS to make sure the URLs of the two applications are different. I used http://localhost:5000 in this example.

Now that all our configurations are good to go hop over to the debug section in VS Code.

On the debug you will notice that both of our launch configurations are listed. If you select the API one and hit the green play button it will start the API. With the API running you can then select the web configuration and hit the green play button and you will have both of your applications running in debug mode.

Wrapping Up

While it is not obvious at all how to get multiple applications to run in a single instance of VS Code the process isn’t hard after you do it the first time. I think my Visual Studio experience made figuring this out harder than it should have been. My key to learning how to get this going was on this GitHub issue.

Hopefully, this saved you some of the struggles I went through. Please leave a comment if there are different ways to accomplish this. The code with the final configuration can be found here.

Refit Basics

A few weeks ago I was watching this episode of the ASP.NET Community Standup and they had Ryan Nowak on to talk about the new HttpClientFactory coming in the 2.1 release and a question came up about compatibility with Refit. I had been meaning to check out Refit but had honestly forgotten about it. This post is going to be a very basic introduction to Refit.

What is it?

In the author’s (Paul Betts) words, Refit is the automatic type-safe REST library for .NET Core, Xamarin and .NET. Cool, but what does that mean? Basically, Refit allows you to define an interface for an API that your application wants to call and using that is hides way all the HTTP and JSON serialization/deserialization bits.

Sample project creation

To test Refit out I created a very simple .NET Core console application. To do the same open a command prompt in the directory you want the project using the following command.

dotnet new console

For this project, I am using Visual Studio Code as my editor. Since VS Code doesn’t have a NuGet UI build in (maybe there is an extension?) I used the following command to add Refit to the project.

dotnet add package Refit

Or if you prefer you can add the following to your csproj file.

<ItemGroup>
  <PackageReference Include="Refit" Version="4.3.0" />
</ItemGroup>

The API

Instead of creating an API I searched the internet for a free one I could use. I ended up using CountryAPI. The following is a sample of what a response from the API looks like.

{  
   "IsSuccess":true,
   "UserMessage":null,
   "TechnicalMessage":null,
   "TotalCount":1,
   "Response":[  
      {  
         "Name":"Afghanistan",
         "Alpha2Code":"AF",
         "Alpha3Code":"AFG",
         "NativeName":"افغانستان",
         "Region":"Asia",
         "SubRegion":"Southern Asia",
         "Latitude":"33",
         "Longitude":"65",
         "Area":652230,
         "NumericCode":4,
         "NativeLanguage":"pus",
         "CurrencyCode":"AFN",
         "CurrencyName":"Afghan afghani",
         "CurrencySymbol":"؋",
         "Flag":"https://api.backendless.com/2F26DFBF-433C-51CC-FF56-830CEA93BF00/473FB5A9-D20E-8D3E-FF01-E93D9D780A00/files/CountryFlags/afg.svg",
         "FlagPng":"https://api.backendless.com/2F26DFBF-433C-51CC-FF56-830CEA93BF00/473FB5A9-D20E-8D3E-FF01-E93D9D780A00/files/CountryFlagsPng/afg.png"
      }]
}

Classes

Now that we know what the API response looks like classes can be created to match its structure. In this case, I have two classes one for response and one for the actual country data.

public class ApiResponse<T>
{
    public bool IsSuccess {get; set;}
    public string UserMessage {get; set;}
    public string TechnicalMessage {get; set;}
    public int TotalCount {get; set;}
    public List<T> Response {get; set;}
}

public class Country
{
    public string Name { get; set; }
    public string Alpha2Code { get; set; }
    public string Alpha3Code { get; set; }
    public string NativeName { get; set; }
    public string Region { get; set; }
    public string SubRegion { get; set; }
}

API Interface for Refit

With the classes for the response setup, we can now define the interface that will be used by Refit when calling the API. The following interface defines a function to get all countries and another function that gets countries that speak a specific native language.

public interface ICountryApi
{
    [Get("/v1/Country/getCountries")]
    Task<ApiResponse<Country>> GetCountries();
    
    [Get("/v1/Country/getCountries")]
    Task<ApiResponse<Country>> GetCountriesByLanguage([AliasAs("pNativeLanguage")]string language);
}

The attributes on the functions are part of the magic of Refit. In the cases above both of the calls are HTTP Get requests which is why they are using the Get attribute. Refit does support other verbs as well.

The other thing of note here is the AliasAs on the parameter of the second function call. This attribute can be used to control what gets put in the query string and keeps cryptic names from the API from spreading to other places in your code.

Calling the API

The following is the full code from my Program class that shows the usage of Refit with both of the API calls defined above.

public static async Task Main(string[] args)
{
    var api = RestService.For<ICountryApi>(" http://countryapi.gear.host");
    
    var countries = await api.GetCountries();
    OutputCountires(countries.Response);

    Console.WriteLine("Enter a language to filter by:");    
    var language = Console.ReadLine();
    var filteredCountries = await api.GetCountriesByLanguage(language);
    OutputCountires(filteredCountries.Response);

    Console.ReadLine();
}

private static void OutputCountires(List<Country> countries)
{
   countries.ForEach(c => Console.WriteLine($"{c.Name} - {c.Region} - {c.SubRegion}"));
}

The following line is defining a call to a rest API for a specific interface.

var api = RestService.For<ICountryApi>(" http://countryapi.gear.host");

Now that we have a reference to the API it can be called asynchronously to get the data from the API.

var countries = await api.GetCountries();

The rest of the app is more of the same just using the other API call.

Gotchas

In order to use async Task Main a change is needed to the project file to set the LangVersion. I just set it to latest, but I believe the minimum for this feature is 7.1.

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>netcoreapp2.0</TargetFramework>
  <LangVersion>latest</LangVersion>
</PropertyGroup>

If you are using VS Code and are using Console.ReadLine() like I am above then a change will be needed for the launch.json file found in the .vscode directory. Look for the console property and set the value to either integratedTerminal or externalTerminal otherwise, the app will be connected to the debug console which will show the output of the application, but doesn’t allow for input.

Wrapping up

Using Refit to makes using APIs super simple. There is a level of magic that I would like to dig into more. I would also like to see how it handles issues and what sort of hooks are provided to address those issue. Based on the Github page Paul has addressed a wide range of the challenges faced with dealing with an API.

As part of writing this posts, I came across two other posts on Refit that might be helpful, one by Jerrie Pelser and the other from Scott Hanselman.

 

Setting Up Visual Studio Code for Debugging ASP.NET Core

Visual Studio Code is a cross-platform source code editor from Microsoft. Out of the box, Code’s language support includes JavaScript, TypeScript, and Node. Using Codes extension system support is available for almost any language you want to use including C#, F#, Elixir, SQL, Swift, and Java. As of this writing, there are 734 language extensions available.

I have been using VS Code since it was first released after Build in 2015, but I have only been using it as an editor never taking advantage of the Debugging capabilities it has available. In this post, I am going to walk through everything that is needed to get a new ASP.NET Core with an Angular front end to run via VS Code’s debugger.

Test application

The first thing I did was to create a new application using JavaScriptServices specifically for this post. For instructions on how to use JavaScriptServices to generate an application check out this post.

On Windows, after the application has been generated and you are in the application directory you can use the following command to open the directory in VS Code.

code .

I am sure there is something similar on Linux and Mac, but I don’t have the environments to try on.

VS Code Overview

When VS Code opens you will see a view close to the following.

The icons down the left side of the screen are for Explorer (shows currently open directory and files), Seach, Source Control (git support is built in), Debug, and Extensions.

Debug

The Debug tab will be our focus so click on it which will take you to the following view.

Using the gear with red circle select .NET Core as the environment for the project.

If you don’t see .NET Core listed click More… and click install for the C# option.

After selecting an environment VS Code will add a launch.json file to the project. This file defines what happens when the start button is clicked in the debugger. At this point clicking the start button to run the application using the debugger will result in an error that Could not find the preLaunchTask ‘build’.

Next, click the Configure Task Runner option and select .NET Core.

This will add a task.json file with a build command that the launch.json is looking for. At this point, I had to restart VS Code to get it to properly pick up the new files. This seems to be an issue that will be fixed with the next release of VS Code and can be tracked using this issue.

After restart and trying to run the debugger again I ran into the error Run ‘Debug: Download .NET Core Debugger’ in the Command Palette or open a .NET project directory to download the .NET Core Debugger.

I ended up having to uninstall and reinstall the C# extension and then opening a C# file to get the debugger to download. If you are having this problem make sure and open a C# file before going as far as reinstalling the C# extension.

Hitting run in the debugger now give the error launch: launch.json must be configured. Change ‘program’ to the path to the executable file that you would like to debug.

To fix this issue click Open launch.json and you will find two places with the following.

"program": "${workspaceRoot}/bin/Debug/<target-framework>/<project-name.dll>"

Change both places to point to the dll your application builds. In the case of my project named DebugTest the final version ended up being the following.

"program": "${workspaceRoot}/bin/Debug/netcoreapp1.1/DebugTest.dll"

Wrapping up

Debugging now works! Based on this post it would seem like debugging in VS Code is a big pain, but really after you get it set up once it just works. For new projects, you just have to let it add the

For new projects, you just have to let it add the launch.json and tasks.json and then set the path to your project’s assembly in launch.json. After that, you are ready to go.

I wait too long to figure this process out. I hope this helps you get started with debugging in VS Code.