5 Features to Watch in Visual Studio 2017

The Visual Studio (VS) engineering teams have shifted into overdrive as they curate the next major release of their flagship IDE. Rather than resting on their laurels, they're making informed decisions while iterating at warp speed with a high degree of quality and innovation.

Not only does VS 2017 mark the twentieth anniversary of the product, but it is the fruit of this hard labor. Now that the product is beginning to congeal, it's a fantastic time to evaluate its new offerings before it is released to market on March 7th.

This article aims to raise awareness of some landmark features and improvements in Visual Studio 2017. The intent is to graze the surface of what's possible in this release. In no particular order of importance, the focus will be on the following topics:

  1. Live Unit Testing
  2. Code Style Configuration
  3. .NET Core Migration Tooling for project.json/XPROJ
  4. Bower & npm Package Restore Settings
  5. Refreshed JavaScript Editor

1. Live Unit Testing

For developers already using NCrunch, the new Live Unit Testing feature may not provoke much excitement. It's also fair to say that Live Unit Testing isn't nearly as fully-featured as NCrunch…at least not yet. But for those not using such an automated concurrent testing tool, this feature is groundbreaking.

Imagine the ability to gauge code coverage health, on a per-line basis, from within a C# class file via visual cues in the text editor's left margin. This has become a reality. The feature also eliminates the need to manually run the appropriate unit tests from the Test Explorer window. The visual cues, implemented as glyphs, automatically adjust as code changes to reflect the current state of affairs.

As a practical example, imagine a simple C# class representing an employee:

public class Employee
{
    public string FirstName { get; set; }

    public decimal HourlyWage { get; set; }

    public string LastName { get; set; }

    public int GetSalaryGrade()
    {
        int salaryGrade;

        if (HourlyWage <= 20.00M)
            salaryGrade = 100;
        else if (HourlyWage >= 20.01M && HourlyWage <= 35.00M)
            salaryGrade = 200;
        else
            salaryGrade = 300;

        return salaryGrade;
    }

    public override string ToString()
    {
        return $"{LastName?.Trim().ToUpper()}, {FirstName?.Trim().ToUpper()}";
    }
}

With a single unit test in-place and with Live Unit Testing enabled, the IDE reveals the following:

code coverage indicators

The blue dash glyphs in the screenshot above reveal two distinct paths for which no code coverage exists. There is an opportunity here to create two additional unit tests: one for the 100-level salary grade and one for the 300-level salary grade. The green checkmark glyph displays because the GetSalaryGrade method of the Employee class is being tested by the Salary_Grade_Is_200 method:

[TestClass]
public class EmployeeTests
{
    private Employee _employee;

    [TestInitialize]
    public void Setup()
    {
        _employee = new Employee
        {
            FirstName = "John",
            LastName = "Doe",
            HourlyWage = 24.30M
        };
    }

    [TestMethod]
    public void Salary_Grade_Is_200()
    {
        Assert.AreEqual<decimal>(200, _employee.GetSalaryGrade());
    }
}

The Live Unit Testing feature is exclusive to the Enterprise tier of VS 2017, and it fully supports C# and Visual Basic projects targeting .NET Framework. The .NET Standard and .NET Core support is absent at the time of writing but is on the roadmap for completion in the near future. In addition to the MSTest unit testing framework used in the example above, xUnit.net and NUnit are both fully supported.

2. Code Style Configuration

The EditorConfig project has existed for years, but using it in VS 2010 through 2015 required installing an extension (read more about that here). It wasn't until the release of VS 2017 RC1 that the IDE began shipping with native EditorConfig support.

In a nutshell, EditorConfig enforces uniformity in code styles. This is accomplished via a mechanism which overrides the individual developer's preferred styling settings as defined in the editor/IDE. To take it a step further, these code styling settings can be enforced across disparate editors/IDEs. Within the Microsoft developer ecosystem, this becomes extremely useful for cross-platform ASP.NET Core projects on which a team's developers are divided in terms of preferred operating system and editor/IDE. The prospect of developers mixing VS Code with OmniSharp on Linux and VS proper on Windows doesn't pose a problem.

This tool is leveraged in VS 2017 through the creation of an INI-based .editorconfig file. Prior to Microsoft's advocacy of the tool, the set of styling options available in the .editorconfig file was largely inadequate to draw a significant uptake. Though inadequate in terms of options, the simplicity and community backing of the tool made it that much more appealing. As explained by Visual Studio and .NET Program Manager, Kasey Uhlenhuth, in a blog post, "The simplicity and universality of EditorConfig make it an attractive choice for team-based code style settings in Visual Studio (and beyond!)."

Microsoft engaged the EditorConfig community to extend the options suite to better support the .NET ecosystem's needs. The result is a more comprehensive, enterprise-grade suite of .NET-centric code style settings. If C# syntax simplification is of interest to your team, for instance, below is an example of enforcing C# 6 expression-bodied members at the method level.

# top-most EditorConfig file
root = true

[*.cs]
csharp_style_expression_bodied_methods = true : error

What follows the colon in the rule definition is an indication of how to treat the specific rule's violations. Because the string "error" is used, violations are piped to the Error List window's Error panel. The result of this .editorconfig file is the following:

EditorConfig enforcing expression-bodied members

For a more convincing .editorconfig example, see the file used in the Roslyn language compiler's GitHub repository.

As for limitations in the current iteration, they are:

  • Continuous Integration (CI) build support isn't available, so enforcement of code styles via EditorConfig is isolated to the development environment.
  • Rule violations resulting in errors don't fail the local VS build.
  • Rules only apply to new code; there's no mechanism for updating existing code.

Fortunately, these are boulders the team is likely to dislodge. At last, the heated debates of code formatting can be put to rest:

XKCD tabs vs. spaces cartoon
Photo credit: XKCD

3. .NET Core Migration Tooling for project.json/XPROJ

Development teams all over the world own ASP.NET Core applications targeting .NET Core which are running in production environments. At the time of writing, the vast majority of those applications were created in VS 2015 with the Preview 2 tooling.

The .NET Core RTM tooling will land in VS 2017 RTM, and it involves a shift from project.json and *.xproj files to a *.csproj file based on the new MSBuild system. What's the migration story?

VS 2017 effortlessly migrates those older .NET Core applications to meet the new project system requirements. Of course, this assumes the installation of the .NET Core workload. The workload contains the updated .NET Core SDK, and it's this updated SDK that provides the dotnet migrate command in the CLI (supported in versions 1.0.0-preview3 and newer).

Upon opening an older VS 2015 solution in VS 2017, the following dialog appears:

.NET Core project upgrade dialog

A Backup folder will be created alongside the solution file. It contains any obsolete and upgraded files, such as global.json, project.json, the *.xproj file, and the solution file. In terms of additions, a new *.csproj file will be created for each upgraded project.

For those who've been following along with the release candidates, RC3 would've included a runtimeconfig.template.json file. This JSON file has been removed from the web templates in favor of an MSBuild ServerGarbageCollection property. The upshot is one less file to manage. Additionally, a migration log file named UpgradeLog.htm is created alongside the solution file for your reference. The noteworthy portion of the migration log looks like this:

VS 2017 migration report

We see that the project.json file was indeed backed up. What the migration report doesn't explicitly call out is that the project.json file's contents were mapped to the appropriate locations in the newly-created *.csproj file. Nate McMaster, of the .NET team in Redmond, has a couple very detailed blog posts here and here that help navigate these changes.

When compared to its unnerving predecessor, the modernized *.csproj file format lacks density. As shown below, the project file has graduated to a class of artifact that developers may actually want to modify by hand. Gone are the puzzling GUIDs and exhaustive inventories of included files. Oh, and did I mention that changes to this project file update VS in real-time?

For a comprehensive overview of enhancements and changes introduced with the new *.csproj file format, read David Kean's list on the MSBuild GitHub repository.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp1.0</TargetFramework>
    <PreserveCompilationContext>true</PreserveCompilationContext>
    <AssemblyName>WebApplication1</AssemblyName>
    <OutputType>Exe</OutputType>
    <PackageId>WebApplication1</PackageId>
    <RuntimeFrameworkVersion>1.0.3</RuntimeFrameworkVersion>
    <PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
  </PropertyGroup>

  <ItemGroup>
    <Content Update="wwwroot\**\*;**\*.cshtml;appsettings.json;web.config">
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.0.1" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.0.1" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink.Loader" Version="14.0.1" />
    <PackageReference Include="Autofac" Version="4.0.0" />
    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.0.0" />
  </ItemGroup>

  <Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
    <Exec Command="bower install" />
    <Exec Command="dotnet bundle" />
  </Target>

  <ItemGroup>
    <DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.2.301" />
  </ItemGroup>
</Project>

One final migration tidbit to note is that the global.json file, if present, will be removed from the solution. Unless there's a need to target a particular version of the SDK, the global.json file becomes unnecessary.

4. Bower & npm Package Restore Settings

As of VS 2015, the product began shipping with Bower and npm (and the requisite Node.js components to support them). The integration with these third party package managers was superb, but there was one garish design flaw — modifying anything in npm's package.json file and then saving the file triggered the execution of the npm install command. There was no concept of a dirty flag flipping to true when the package.json file's devDependencies or dependencies properties were altered. Likewise, modifying anything in Bower's bower.json file and then saving the file triggered the execution of the bower install command.

For enterprise-grade applications, the consequence was minutes of lost productivity while waiting for the package installations to complete. What an opportune time for a coffee run, or perhaps some competitive office chair spinning with your colleagues!

chair spinning cartoon
Photo credit: XKCD

VS 2017 addresses the aforementioned pain point by introducing a couple useful settings:

  1. Restore On Project Open
  2. Restore On Save

In the IDE, navigate to Tools –> Options –> Projects and Solutions –> Web Package Management –> Package Restore:

Package Restore settings

Since the settings depicted above are all set to True, VS 2015's behavior is effectively mirrored. By changing either of the two Restore On Save values to False, the associated "install" command isn't executed upon file save.

The downside of such a change is that the responsibility of installing the dependencies is left to the developer. Fortunately, this problem is easily resolved by running either npm install or bower install from the appropriate directory within the Package Manager Console window. In the case of npm, a slightly more elegant solution is to install the NPM Task Runner extension and to run the "install" task from the Task Runner Explorer window.

The Restore On Project Open setting suppresses the automatic installation of Bower and/or npm dependencies upon opening the project in VS. When changing this value to False, the benefit is improved performance. One benefit of keeping this option enabled is that the onboarding of new developers to the project team is eased, since the installation of dependencies is fully automated. Package managers other than NuGet may feel foreign to a large populace of the .NET community, so choose wisely.

5. Refreshed JavaScript Editor

Remember trying to do ECMAScript 6/2015 (ES6) development in VS 2015? Or what about React with JSX? If you've tried either of these, you recall it was a less than desirable experience – you probably abandoned VS proper for VS Code or a similar editor. Building a Single Page Application (SPA) with the latest technologies was an arduous endeavor because of the legacy JavaScript editor's antiquated, execution-based model.

In particular, VS 2015 lacked full support for ES6 features such as module imports and exports. Developers were greeted with red squiggles, poor syntax highlighting, and broken IntelliSense when using the ES6 module system and/or JSX:

red squiggles

Riding on the heels of VS Code's success, VS 2017 adopts the same "Salsa" JavaScript language service to provide ample support for technologies such as ES6 through ES7, JSX, Node, and JsDoc! In short, VS proper is much closer to achieving feature parity with VS Code in regards to its modern JavaScript development experience. Additional details on this enhancement can be found in this post from the VS Blog.

After scaffolding a new React SPA using Facebook's create-react-app tool, open the resulting folder in VS 2017 to take a look for yourself. Wait a minute…open a folder?!? That's right. The IDE now understands how to open a folder-based project. No more need for a project or solution file, which better suits most SPA development.

open folder menu option

Equally as impressive is that VS finally provides IntelliSense for named exports within an ES6 import statement. Here's an example of the IDE telling us what has been exported from the react-dom module:

react-dom module named export IntelliSense

Conclusion

The VS 2017 RTM product release is poised to make its predecessors look like child's play through an assortment of impressive features, bug fixes, and enhancements. This iteration seems particularly focused on boosting productivity and resolving performance issues with large, enterprise-grade solutions. Thankfully, these things make the upgrade more easily justifiable.

Other features generating a lot of buzz, but out-of-scope for this article, include Live Architecture Dependency Validation, C# 7 language features, and Lightweight Solution Load.

Related resources:

Comments