The true power with ASP.NET Core is in its flexibility. With a powerful CLI, I can run an app on any platform with just a basic text editor, if I so desire. And speaking of desire: when I write code, I prefer Visual Studio Code. It's fast, responsive, and gives me exactly what I need and nothing more. Of course, I rely on Visual Studio 2017 from time-to-time for advanced debugging and profiling. But when I'm in a code-debug-code workflow, nothing is better.

Visual Studio Code is perfect for ASP.NET Core - allowing me to write front-end and back-end code in one great lightweight environment. Unfortunately, when I talk to ASP.NET Core developers many times Visual Studio Code isn't a consideration. Often, I hear that while Code is a great tool, it only is for those slinging front-end code. And after decades of the full-fledged Visual Studio being the only option to write .NET code, who can blame them?

Let's get started and walk through how you can debug C# code, query databases, and more with .NET Core using Visual Studio Code.

Note: All screenshots and keyboard shortcuts are using Windows 10, but they should be very similar on other operating systems like a Mac.

Contents

Prerequisites

Before we get started, you'll need to make sure you have the following installed on your platform of choice.

Now that we have everything set up, let's start by creating a new ASP.NET Core web application from the ASP.NET Core command-line interface (CLI).

(back to contents)

Create a new Core project

We're going to create a new Core project first, using the ASP.NET Core CLI.

First, from a terminal window let's get a list of all available templates by adding the -l flag to our dotnet new command:

dotnet new -l

You can see that we can create a new project based on many different project template types. For our purposes, any application will do - let's go ahead and create a new ASP.NET Core MVC App by including the Short Name in the command in your terminal window.

dotnet new mvc

Now that we successfully created our project, we can open our new solution in Visual Studio Code. From the terminal, navigate one folder down to the project directory and enter code . from your terminal window.

(back to contents)

Working with your Core solution in Code

When you first open the C# project, you'll get a warning that Code needs to build required assets to the project.

After you click Yes, you'll see a .vscode folder added to your project, which includes the following:

  • launch.json - where Code keeps debugging configuration information
  • tasks.json - where you can define any tasks that you need run - for example, dotnet build or dotnet run.

For example, here's what my tasks.json looks like out of the box.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "command": "dotnet build",
            "type": "shell",
            "group": "build",
            "presentation": {
                "reveal": "silent"
            },
            "problemMatcher": "$msCompile"
        }
    ]
}

This allows me to build my solution without needing to keep entering dotnet build from the command line. In Windows, I can hit Ctrl + Shift + B much like I can in regular Visual Studio. Try it out!

(back to contents)

Debugging C# Code

Now, let's take a look at how C# debugging works in Visual Studio Code. First, set a breakpoint anywhere in the application much like you would do in any other IDE (in a gutter next to the line number). In my case, I'm creating a breakpoint inside the About controller action in Controllers/HomeController.cs.

Now, navigate to the Debug tab in Code and click the Debug button.

Code will launch your application using http://localhost:5000.

Next, trigger your breakpoint - for me, that means clicking the About link in the top menu so that I can enter the About action in the Home controller.

If you go back to the Debug section in Code, you'll see all the debugging options at your disposal: accessing your variables, call stack, all your breakpoints, and even the ability to watch variables or objects.

Now, let's add a watch statement for the ViewData message.

It's null now because I haven't executed that line of code yet. Once I step into the method, you'll see the value of ViewData["Message"].

Now that we've been able to debug C#, let's query a database!

Working with Databases in Code

So, I've created a new C# project and can debug it in Visual Studio Code. Pretty cool, right? But with any server-side application, you'll want to work with a database. You can easily do this in Code - for our purposes, we'll be working with SQL Server.

(Note: If you are working with SQL Server on a Mac, a setup guide is beyond the scope of this post. This piece should be able to assist you.)

Before we get started, we'll need to install Microsoft's own SQL Server extension from Visual Studio Code. Just search for mssql in the Extensions panel and you should have no problem finding it.

Now that you have the extension installed, you'll need to create a connection to your database server (this can be locally or even something hosted in Azure). To start connecting to the database, access the Command menu (Ctrl + Shift + P) and search for sql. You'll see all the commands the extension has provided for you.

Select MS SQL : Connect from the command menu and follow the prompts.

  • Server name - since I am connecting locally, I just entered (localhost)\MSSQLLocalDB. You can do this, or if hosted in Azure the mydb.database.windows.net address, or even a remote IP address.
  • Database name - enter the database you want to use (this is optional).
  • Integrated/SQL Server authentication - pick your authentication method. If you're using a local DB, Integrated will do. For something like Azure, you'll need SQL authentication.
  • Profile name - you can optionally enter a name for the profile.

Now that we're set up and connected, let's set up a database with horror movies. To do this, first create a .sql file in your project.

Create sample database

In your SQL file, create a database. Feel free to copy and paste this syntax, then execute (Ctrl + Shift + E).

CREATE DATABASE MyHorrorDB

Now, if you execute the following you should be able to see the name of your new database.

SELECT Name FROM sys.Databases

Now that we have proof of our created databases, execute the following SQL to inject your database with sample data. You should notice IntelliSense-like completion!

USE MyHorrorDB
CREATE TABLE Movies (MovieId INT, Name NVARCHAR(255), Year INT)
INSERT INTO Movies VALUES (1, 'Halloween', 1978);
INSERT INTO Movies VALUES (2, 'Psycho', 1960);
INSERT INTO Movies VALUES (3, 'The Texas Chainsaw Massacre', 1974);
INSERT INTO Movies VALUES (4, 'The Exorcist', 1973);
INSERT INTO Movies VALUES (5, 'Night of the Living Dead', 1968);
GO

Now, do a blanket SELECT to get all your movies (obviously, a blanket SELECT is not recommended for performance reasons but this is pretty harmless since we're querying five movies).

SELECT * FROM Movies

Now you should your sample data in action!

You can do much more than query databases, though! Take a look at this document for more details.

(back to contents)

Wrapping up

While this is just scratching the surface, I hope this convinces you to give Visual Studio Code a try for developing in ASP.NET Core when you don't need a full-fledged IDE. And, of course, leave me a comment if you want to keep this conversation going!

Thanks to Scott Addie of Microsoft and Chris DeMars for performing a technical review of this post.


This is a short post that might help you.

I wasn't sure how to use anchor links in Markdown. These come in handy when you have a long post and want to link to different sections of a document for easy navigation.

The nice thing about Markdown is that it plays so well with straight HTML - so I was pleased to get it working on the first try.

First, add an anchor as regular HTML in your Markdown element. Here, it is right at a heading.

### <a id="MyHeading"></a>My Heading ###

Now, I can link it using a standard Markdown link.

Where is my [heading](#MyHeading)?


For years, if we wanted to store local data we would look to using HTTP cookies—a convenient way to persist small amounts of data on a user's machine. And we do mean small: cookies are limited to about 4KB apiece. Also, the cookie is passed along with every request and response even if it isn't used, making for heavy HTTP messaging.

Enter the web storage APIs, a way for you to store key/value paired string values on a user's machine. Local storage provides much more space—modern browsers support a minimum of 5MB, much more than the 4KB for cookies.

When using web storage, we can implement either localStorage or sessionStorage from the Storage object. Let's work through how to accomplish this.

Checking web storage compatibility

Although most browsers support web storage, you still should be a good developer and verify that a user isn't using an unusually old browser like IE6:

function isWebStorageSupported() {
 return 'localStorage' in window;
}

When to use localStorage or sessionStorage

The localStorage and sessionStorage APIs are nearly identical with one key exception: persistence.

The sessionStorage object is only available for the duration of the browser session, and is deleted automatically when the window is closed. However, it does stick around for page reloads.

Meanwhile, localStorage is persisted until it is explicitly by the site or the user. All changes made to localStorage are available for all current and future visits to the site.

Whatever the case, both localStorage and sessionStorage work well when working with non-sensitive data needed within an application since data stored in localStorage and sessionStorage can easily be read or updated from a user's browser.

Web storage API methods and properties

The following is a list of methods and properties available on global Storage object's localStorage and sessionStorage variables.

  • key(index) - finds a key at a given index. As with finding indexes in other code you write, you should check the length before finding an index to avoid any null or out-of-range exceptions.
  • getItem(key) - retrieve a value by using the associated key.
  • setItem(key, value) - stores a value by using the associated key. Whether you are setting a new value or updating an existing value, the syntax is the same.
  • removeItem(key) - removes a value from local storage.
  • clear() - removes all items from storage.
  • length - a read-only property that gets the number of entries being stored.

Can I store objects?

While you can only have string values in web storage, you can store arrays or even JavaScript objects using the JSON notation and the available utility methods, like in the following example.

var player = { firstName: 'Kris', lastName: 'Bryant' };
localStorage.setItem('kris', JSON.stringify(player));

You can then use the parse() method to deserialize the kris object.

var player = JSON.parse(localStorage.getItem('kris'));

Keeping web storage synchronized

How do you keep everything in sync when a user has multiple tabs or browser instances of your site open concurrently? To solve this problem, the web storage APIs have a storage event that is raised whenever an entry is updated (add/update/removed). Subscribing to this event can provide notifications when something has changed. This works for both localStorage and sessionStorage.

Subscribers receive a StorageEvent object that contains data about what changed. The following properties are accessible from the StorageEvent object. The storage event cannot be canceled from a callback. The event is merely a notification mechanism: it informs subscribers when a change happens.

  • key - gets the key. The key will be null if the event was triggered by clear()
  • oldValue - gets the initial value if the entry was updated or removed. Again, the value will be null if an old value did not previously exist, or if clear() is invoked.
  • newValue - gets the new value for new and updated entries. The value is null if the event was triggered by the removeItem() or clear() methods.
  • url - gets the URL of the page on the storage action
  • storageArea - gets a reference to either the localStorage or sessionStorage object

To begin listening for event notifications, you can add an event handler to the storage event as follows.

function respondToEvent(event) {
 alert(event.newValue);
}

window.addEventListener('storage', respondToChange, false);

To trigger this event, perform an operation like the following in a new tab from the same site.

localStorage.setItem('player', 'Kris');

The fine print

While web storage offers many benefits over cookies, it is not the end-all, be-all solution, and comes with serious drawbacks as others have noted.

  • Web storage is synchronous - because web storage runs synchronously, it can block the DOM from rendering while I/O is occurring.
  • No indexing or transactional features - web storage does not have indexing, which may incur performance bottlenecks on large data queries. If a user is modifying the same storage data in multiple browser tabs, one tab could potentially overwrite the value in another.
  • Web storage does I/O on your hard drive - because web storage writes to your hard drive, it can be an expensive operation depending on what your system is currently doing (virus scanning, indexing data, etc.) While you can store a lot more data in local storage, you'll need to be cognizant of performance.
  • Persistence - if a user no longer uses a site and storage is not explicitly deleted, storage is still loaded when you start the browser session
  • First request memory loading - because browsers load data into memory, it could use a lot of memory if many tabs are utilizing web storage mechanisms.

Web storage does offer you a simple, direct way to store user data, with caveats that it can degrade performance if you do not use it wisely thanks to its synchronous, I/O nature. Much like anything else you code, understand its utility and its nuances before using it, and don't be greedy. It very well can bite you in the rear end if you expect too much.


Building location-aware applications is a snap with the HTML5 Geolocation APIs. These APIs allows you to retrieve a user’s location – with the user’s permission – as a one-time request or over a period of time. This post will walk you through how to implement the Geolocation API.

Checking for support

When implementing the Geolocation APIs, the first thing you’ll want to do is see if geolocation is supported by a user’s browser. (You’ll notice that the geolocation functionality is supported by virtually all browsers, but you know what they say about assuming.) You can check by writing code that uses the in operator; this returns true if the geolocation property exists in the window’s navigator object.

function supportsGeolocation() {
    return 'geolocation' in navigator;
}

Now that you can use the Geolocation API, you can use the position.coords property to retrieve some of the following values:

  • The latitude and longitude attributes are geographic coordinates specified in decimal degrees.
  • The altitude attribute denotes the height of the position, specified in meters. If the implementation cannot provide altitude information,
    the value of this attribute must be null.
  • The accuracy attribute denotes the accuracy level of the latitude and longitude coordinates. It is specified in meters and must be supported
    by all implementations.
  • The altitudeAccuracy attribute is specified in meters. If the implementation cannot provide altitude information, the value of this attribute
    must be null.
  • The heading attribute denotes the direction of travel of the hosting device and is specified in degrees, where 0° ≤ heading < 360°, counting
    clockwise relative to the true north. If the implementation cannot provide heading information, the value of this attribute must be null. If the
    hosting device is stationary, then the value of the heading attribute must be NaN.
  • The speed attribute denotes the magnitude of the horizontal component of the hosting device’s current velocity and is specified in meters per second.
    If the implementation cannot provide speed information, the value of this attribute must be null.

From here, you can decide if you want to get a user’s current location (one-time event), or watch a current position (specified time).

Getting current location

There are many scenarios where you just want to get a user’s location once – like, where is a user’s closest movie theater or grocery store? If this is what you’re after, then you can call the getCurrentLocation method from the navigator.geolocation object.

This method sends an async request to detect the user’s position. When the position is determined, a callback function is executed. You can optionally provide a second callback function to be executed if an error occurs and also a third parameter as an options object. In the options object, you can set the following attributes:

  • enableHighAccuracy – get the best possible result, even if it takes longer (default is false)
  • timeout – timeout, in milliseconds, that the browser will wait for a response (the default is -1, meaning there is no timeout)
  • maximumAge – specifies that a cached location is acceptable, so long as it isn’t longer than the milliseconds that you specify
    (the default is 0, meaning a cached location is not used)

In the following example, I’ll build a simple page that asks a user to click a button that will execute a function to find a user’s location. The information will be displayed in the location div.

<html>
    <head>
        <script src="js/current-location.js"></script>
    </head>
    <body>
        <p><button onclick="findMe()">Show my location</button></p>
        <div id="location"></div>
    </body>
</html>

The following example retrieves the latitude, longitude, and accuracy of the current user, given that they give permission to access their location. After getting this information, we’ll display a Google Maps image of their location, easily accessible by using the Google Maps API, which takes latitude and longitude as parameters.

function findMe() {
  var output = document.getElementById("location");

  function success(position) {
    var latitude  = position.coords.latitude;
    var longitude = position.coords.longitude;
    var accuracy = position.coords.accuracy;

    output.innerHTML = '<ul> \
                        <li>Latitude: ' + latitude + ' degrees</li> \
                        <li>Longitude: ' + longitude + ' degrees</li> \
                        <li>Accuracy: ' + accuracy + 'm</li> \
                        </ul>';

    var img = new Image();
    img.src = "https://maps.googleapis.com/maps/api/staticmap?center=" + latitude + "," + longitude + "&zoom=13&size=300x300&sensor=false";
    output.appendChild(img);
  };

  function error() {
    output.innerHTML = "Unable to retrieve your location!";
  };

  output.innerHTML = "Getting location ...";

  var options = {
      enableHighAccuracy: true,
      timeout: 3000,
      maximumAge: 20000
  };

  navigator.geolocation.getCurrentPosition(success, error, options);
}

Monitoring current location

If you see a user’s position changing frequently, like with turn-by-turn directions, you can set up a callback function that you call with the updated position information. You can accomplish this by using the watchPosition function. This function
has the same parameters as getCurrentPosition.

The watchPosition method returns an ID that can be used to identify who or what is watching the position. Then, when you wish to stop watching a user’s location, you can use the clearWatch method which takes the ID as a parameter.

In this case, we have another simple page that has buttons to start and stop watching a user’s position. The position information will display in the message div.

<html>
    <head>
        <script src="js/jquery-3.1.0.min.js"></script>
        <script src="js/watch-position.js"></script>
    </head>
    <body>
        <div id="message"></div>
        <button id="startLocation">Start</button>
        <button id="stopLocation">Stop</button>
    </body>
</html>

In the JavaScript file, we’ll start by initializing a watchId, using jQuery to initiate click events (more on that in a second), checking to see if the API is supported, and then writing a utility method that will show the information in the message div.

var watchId = 0;

$(document).ready(function() {
    $('#startLocation').on('click', getLocation);
    $('#stopLocation').on('click', endWatch);
})

function supportsGeolocation() {
    return 'geolocation' in navigator;
}

function showMessage(message) {
    $('#message').html(message);
}

And now, here’s where the magic happens: if a user’s browser supports geolocation, we call watchPosition, which in this case take a success callback, an optional error callback, and an optional options parameter.

function getLocation() {
    if (supportsGeolocation()) {
        var options = {
            enableHighAccuracy: true
        };
        watchId = navigator.geolocation.watchPosition(showPosition, showError, options);
    }
    else {
        showMessage("Geolocation is not supported by this browser.");
    }
}

function showPosition(position) {
    var datetime = new Date(position.timestamp).toLocaleString();
    showMessage("Latitude: " + position.coords.latitude + "<br />"
              + "Longitude: " + position.coords.longitude + "<br />"
              + "Timestamp: " + datetime);
}

function showError(error) {
    switch (error.code) {
        case error.PERMISSION_DENIED:
            showMessge("User denied Geolocation access request.");
            break;
        case error.POSITION_UNAVAILABLE:
            showMessage("Location information unavailable.");
            break;
        case error.TIMEOUT:
            showMessage("Get user location request timed out.");
            break;
        case error.UNKNOWN_ERROR:
            showMessage("An unknown error occurred.");
            break;
    }
}

Finally, when the user clicks the Stop button, the clearWatch method is called and the browser stops tracking the user’s location.

function endWatch() {
    if (watchId != 0) {
        navigator.geolocation.clearWatch(watchId);
        watchId = 0;
        showMessage("Monitoring complete.");
    }
}


The Application Cache (AppCache) API allows offline access to your application by providing the following benefits:

  • Performance - Specified site resources come right from disk, avoiding any network trips
  • Availability - Users can navigate to your site when they are offline
  • Resilience - If a server breaks or something bombs and your site is inaccessible online, users can still use your offline site.

In this post, we’ll explore how to implement AppCache and investigate its benefits and many drawbacks. If you would like to play along at home, feel free to reference an AppCache repo I created on GitHub.

Creating a manifest file

First, you’ll need to create a manifest file, a static text file that tells the browser which assets to cache for offline availability. A manifest can have three different sections (the order and frequency of these sections do not matter):

  • Cache - This default section lists the site assets that will be cached after an initial download.
  • Network - Files listed here can come from the network if they aren’t cached. Otherwise, the network isn’t used even if the user is online.
  • Fallback - A section, which is optional, that specifies pages to use if a resource is not available. The first URI is the resource, and the second is what is used if the network request fails or errors.

Keep in mind the following “gotchas” with the manifest: if the manifest file cannot be found, the cache is deleted. Also, if the manifest or a resource specified in the manifest cannot be found and downloaded, the entire offline caching process fails and the browser will keep using the old application cache.

So, when will the browser use a new cache? This occurs when a user clears their cache, a manifest file is modified, or programmatically (we’ll get to that later).

Pay special attention to the second item. A common misconception is that when any resources listed within the manifest change, they will be re-cached. That is wrong. The manifest file itself needs to change. To facilitate this, it is a common practice to leave a timestamp comment at the top of the file that you can update whenever the manifest changes, such as in my example below.

# v5 2016-08-15
index.html
css/main.css
scripts/script.js
images/hanna.jpg
images/emma.jpg

NETWORK:
*

FALLBACK:
offline.html

Referencing the manifest file

Now that you’ve created the manifest file, you then need to reference it in your web page(s). To do this, you’ll need to append the manifest attribute to the opening tag of any page you want cached:

<html manifest="manifest.appcache">
...
</html>

This bears repeating: the attribute must be included on every page that you want cached. The browser will not cache a page if the manifest attribute is not included on the specific page.

Using the AppCache APIs

Now that you have created the manifest file and decided which pages you want to be cached, you can now talk to the AppCache programmatically from the global JavaScript window.applicationCache object. From this object, you can call the following methods:

  • abort - kills the cache download process
  • addEventListener - registers an event handler for a specific event type
  • dispatchEvent - sends an event to a current element
  • removeEventListener - removes a handler previously registered by addEventListener
  • swapCache - swaps an old cache for a new cache
  • update - triggers an update of the existing cache only if updates are available

From the MDN documentation on AppCache, here’s how you would see if your application has an updated manifest file.

function onUpdateReady() {
  console.log('I found a new version!');
}

window.applicationCache.addEventListener('updateready', onUpdateReady);

if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
  onUpdateReady();
}

Note, as specified in the MDN documentation, that “…since a cache manifest file may have been updated before a script attaches event listeners to test for updates, scripts should always test applicationCache.status.”

The fine print

In Application Cache is a Douchebag, Jake Archibald brilliantly lays out the many limitations of the AppCache API. You should read that piece for the full details, but here are a few gotchas that I haven’t mentioned yet:

  • Files come from the cache if you’re online – you’ll first get a version of the site from your cache. After rendering, the browser then finds updates to the manifest. As noted in the article, it means the browser doesn’t have to wait for timing out connections, but it’s somewhat annoying.
  • Non-cached resources don’t load on a cached page – if you cache, for example, a web page but not an image on it, the image will not display on the page even when you are online. Really. You would get around this by adding the * to the Network section in your manifest. (However, these connections will fail anyway if you are offline.)
  • You can’t access a cached file by its parameters – accessing index.html by parameters such as index.html?parameter=value will not retrieve the cached page. It will fetch the page over the network.

As you can imagine, AppCache’s limitations have spurned a “let’s not use AppCache” movement across the Web. It has been removed from the Web standards in favor of service workers. When describing service workers, the MDN documentation summed up AppCache’s rise and fall nicely:

The previous attempt — AppCache — seemed to be a good idea because it allowed you to specify assets to cache really easily. However, it made many assumptions about what you were trying to do and then broke horribly when your app didn’t follow those assumptions exactly.

So to answer my initial question, it is not worth it; don’t use AppCache. Unless you are completely aware of the limitations and able to live with them, AppCache’s drawbacks outweigh its benefits. The community has spoken, and using local storage or service workers is the preferred approach.