Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
138 views76 pages

CODE 5-2020 Web

Uploaded by

Kim Long
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
138 views76 pages

CODE 5-2020 Web

Uploaded by

Kim Long
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 76

Blazor, ASP.

NET Core, Maquette, VS Code

SEP
OCT
2020
codemag.com - THE LEADING INDEPENDENT DEVELOPER MAGAZINE - US $ 8.95 Can $ 11.95

Full Steam
Ahead

Localize Your The History Prototyping


ASP.NET Core of Open Source with Microsoft
Applications at Microsoft Maquette
TABLE OF CONTENTS

Features
8 Progressive Web Apps (PWAs) 54  Deep Dive into ASP.NET Core
A
Somewhere between the simplicity of a website and the technological
complexity of a native app lives the progressive Web application.
Localization
PWAs are built with browser-based technologies but can act just like Joydip shows you how to take advantage of internationalization
a native app. Sahil shows you how. in ASP.NET Core to enable a broader reach for your applications.
Sahil Malik Joydip Kanjilal

14  se the MVVM Design Pattern


U 66 I nteractive Unit Testing with
in MVC Core, Part 3 .NET Core and VS Code
Paul shows you how to build a product detail page for adding and editing data. John continues exploring unit testing using .NET Core and VS Code.
Paul D. Sheriff John Petersen

22  hen Open Source Came


W
to Microsoft
After years of resistance, Microsoft has joined the open source movement.
Richard tells us the history and whys and wherefores of this change,
Columns
and how far it’s come.
Richard Campbell 60 T alk to an RD:
Dr. Neil Roodyn and Markus Egger
28 S tages of Data: A Playbook Dr. Neil and Markus talk about life during COVID-19.
for Analytic Reporting Using Markus Egger

COVID-19 Data, Part 2 74 CODA: There Are No New Problems


Kevin continues using Power BI to track a constantly changing array
It’s not déjà vu, it’s just that this is the same problem as before
of data about the pandemic.
from a new angle. John explores learning from the past.
Kevin S. Goff
John V. Petersen

 38 En Route to Full-Stack .NET


Development with Client-Side Blazor
After a long race chasing after JavaScript, C# is coming
into the final curve holding its own using Blazor.
Departments
Otto shows you how to use Blazor on the client-side.
Otto Dobretsberger 6 The Joy of Being a Beginner
46  rototyping with Microsoft
P 25 Advertisers Index
Maquette: A New Virtual Reality Tool
Vassili and Stefan take a step away from reality using Maquette 73 Code Compilers
and show you how to start your escapist journey too.
Stefan Landvogt and Vassili Kaplan

US subscriptions are US $29.99 for one year. Subscriptions outside the US pay $49.99 USD. Payments should be made in US dollars drawn on a US bank. American Express,
MasterCard, Visa, and Discover credit cards are accepted. Bill Me option is available only for US subscriptions. Back issues are available. For subscription information,
send e-mail to [email protected] or contact Customer Service at 832-717-4445 ext. 9.
Subscribe online at www.codemag.com
CODE Component Developer Magazine (ISSN # 1547-5166) is published bimonthly by EPS Software Corporation, 6605 Cypresswood Drive, Suite 425, Spring, TX 77379 U.S.A.
POSTMASTER: Send address changes to CODE Component Developer Magazine, 6605 Cypresswood Drive, Suite 425, Spring, TX 77379 U.S.A.

4 Table of Contents codemag.com


EDITORIAL

The Joy of Being a Beginner


As I write this editorial, most of the world is under various stay-at-home orders dealing with the
COVID-19 epidemic. As many of you are abundantly aware, social gatherings are unwise and, in many
cases prohibited, which includes software developer conferences. One of the conferences that has

been rescheduled is Prairie DevCon in Winnipeg. Over the last 30 years, I’ve used the following Python: Python is probably the most challenging
The cancellation of this conference has a real tools first as a beginner then in many (but not and interesting of the tools I’m exploring. Python
sting for me, as this conference is where I would all) cases as a master: dBase, FoxBase+, FoxPro, is firmly entrenched in the Data Science world.
have given my first keynote speech. Visual Basic, Visual FoxPro, SQL Server, HTML/ If you plan on working in this world, you’ll need
CSS/JavaScript, Ruby, Ruby on Rails, Visual Basic at least a minimum exposure to Python. I recom-
My keynote was entitled “Just Your Every Day .NET, C#, and my newest addition: Python. These mend checking out the Python content at CODE
Ordinary Programmer” (title borrowed from Joe are just the languages, there are numerous add- Magazine (www.codemag.com) that we’ve devel-
Walsh, Thanks Joe!). In this keynote I would ons, IDEs, frameworks, and operating systems oped over the years. Many of these articles are
have talked about 30+ years as a developer and that I also throw into the mix. targeted at people leveraging knowledge of one
regaled with many stories. I’d have stressed how, technology to learn some new technology.
even after 30 years, there’s still new stuff to While we are under work-from-home orders and
learn—it took this long to get to my first keynote, required to limit our participation in social gath- These are just a few items I’ve been spending
for instance—and that’s my favorite part of being erings, it might be a good time to consider be- time with (including ample time to perfect that
a developer: The Joy of Being a Beginner. coming a “beginner” in a new technology. cancelled keynote, LOL). I know that these times
are rough for people mentally and you may not
I listen to several podcasts and one of my favor- I’ve been working at home for a bit over two de- have the bandwidth or desire to learn new tech-
ites is called ScriptNotes. ScriptNotes is hosted cades, so that part’s not new. What is new is the nologies. But if you are so inclined, let’s commu-
by John August and Craig Mazin, two fabulous lack of social events that I participate in to for a nicate about what we’re learning. I love hearing
working screenwriters. On Episode 439, “How to “change of scenery.” No Dungeons & Dragons (we from readers, so if you’d like to share I’m @rod-
Grow Old as a Writer,” John and Craig discussed play online now), no concerts, no movies out, no paddock on Twitter, I hope to hear your experi-
extensively about how each medium you write for poker games, no eating out at restaurants. Just ences being a beginner again.
has its own unique qualities. John (who write the like you, I now have a lot of free time on my
film Big Fish) made the following comment: hands. I’m taking this time to add new technolo-
gies to my mix. Here are some of the technologies  Rod Paddock
I am exploring. 

“Big Fish (the musical) gave me .NET Core (and ASP.NET Core): I came to .NET
Core a bit late in my development cycle. My com-
a chance to be a beginner again. pany has many websites built in the 4.x frame-
To be someone who is brand work, so I’ve had little time to use ASP.NET Core
new to things and be curious previously. Now, I’ve started a new business ven-
ture and we’ve built out our new platform using
and eager to explore and ASP.NET Core. Also, I’m a bit fascinated by the
willing to make mistakes as I’m idea of building single-file executables.
figuring out this new art form. Docker: Docker is a newfangled way of deploy-
When you have mastery over ing and running applications. I’m still a neophyte
something, it’s nice, it’s helpful, when it comes to Docker, which makes it all the
more fun. The idea of building a single-file ap-
things are easier for you, plication using .NET Core and deploying it using
but they’re also less exciting.” Docker is a fun area I am exploring.

Vue: I’ve been a long-time proponent of jQuery,


which has served me well. jQuery is a great tool
BOOM! This comment struck me like a bolt of light- but there are other tools worthy of exploration
ning. This IS EXACTLY why after 30+ years I still and one of these is Vue. One tool I am using is
love being a software developer. It’s a real blast to from an article Shawn Wildermuth wrote for CODE
learn new languages and tools. Yes, you also make Magazine last year, called “Moving from jQuery
new mistakes but it’s for sure not boring. to Vue” (https://www.codemag.com/Article/
1909051/Moving-from-jQuery-to-Vue). I love ar-
Good software developers realize that their ca- ticles that help bridge knowledge from one tech-
reers are in a constant state of being a beginner. nology to another.

6 Editorial codemag.com
NEVRON OPEN VISION (NOV)
The future of .NET UI development is here

NOV lets you build applications


for Windows, Mac and soon Blazor
using a single codebase.

The suite features industry-leading components


including Chart, Diagram, Gauge, Grid, Scheduler,
Rich Text Editor, Ribbon, and others.

Nevron provides advanced solutions for:

codemag.com Learn more at: www.nevron.com today


ONLINE QUICK ID 2009021

Progressive Web Apps (PWAs)


When was the last time you opened the App Store on your phone? No, I don’t mean a website that took you to the App
Store, I mean that you unlocked your phone, specifically went to the App Store, and looked for new interesting apps to install.
I’ll speak for myself; it has been so long, I don’t even remember. Let’s be honest, the novelty of the App Store has worn off.

Whenever Apple releases a new iOS version, with a signifi- proxy written in JavaScript between your app and the out-
cant new capability that’s locked to new kinds of applica- side world. It can give you a lot of fine control on your net-
tions, I may look at it out of a curiosity perspective. But, work requests. This means that you can control the cach-
the process of downloading, installing, and then cleaning ing behavior of individual components of your Web page or
applications is just tedious. I think the most commonly used website. This gives you a lot of control on what gets loaded
app on my phone is the browser. and what doesn’t. For example, you can decide that large
JavaScript files are loaded only on demand, and they are
However, browser-based applications, or let’s call them cached. This means that when the user revisits your Web
websites, have significant limitations. On the other hand, page or goes from page to page within the same website,
Sahil Malik native apps that get installed on your device are a hassle to the files are not constantly reloaded. This is not so great for
www.winsmarts.com install, manage etc. Progressive Web applications are some- complex pages that you want to load faster.
@sahilmalik where in the middle. They are built using browser-based
technologies, but they can act and be installed as a native
Sahil Malik is a Microsoft
app. They can be installed directly from your browser, or
MVP, INETA speaker,
a .NET author, consultant,
even distributed through the App Store. Realistically speak- A service worker is a client-side
ing, although they can work offline, do interesting things
and trainer.
like offline storage, work with your camera, do geolocation,
programmable proxy written in
Sahil loves interacting with and typically do things that are reserved for a native ap- JavaScript between your app and
fellow geeks in real time. plication, a native app will still win when it comes to those the outside world.
His talks and trainings are capabilities. Progressive Web apps give you the incredible
full of humor and practical convenience of being installable right through the browser
nuggets. or App Store, if you choose, and they are built using stan-
dard Web-based technologies that we’re already familiar Additionally, service workers also enable you to handle push
His areas of expertise are with. And although progressive Web applications may not messaging. A service worker is nothing but a type of a Web
cross-platform Mobile app scale to the capabilities of a native application, a vast num- worker. It’s a JavaScript file that runs separately from the
development, Microsoft ber of applications don’t need those capabilities. main browser thread. It intercepts network requests, caches
anything, and security or retrieves resources from the cache, and can deliver push
and identity. In fact, you’d be surprised to know how many applica- messages to you.
tions in app stores today are actually progressive Web apps
(PWAs). In this article, I intend to explain what PWAs are, The key here is that the service worker runs separately from
their capabilities, and their limitations. the main thread. This means that service workers are inde-
pendent of the application they are associated with. This
Before I get started, let me let you in on a little secret: Even means that they’re a lot more powerful than the traditional
if you never intend to write a mobile application, the con- caching techniques that you’ve used in the applications
cepts presented in this article are applicable to you as a Web you’ve used so far. Another consequence of the service
developer. The amazing thing about PWAs is that they sim- worker running separately from the main thread is that it
ply use Web-based technologies that you’re already familiar can receive push notifications even when the app isn’t run-
with. For example, one of the core technologies of PWAs ning in the browser.
is service worker. Service worker, when applied to any Web
application, can greatly improve the load times of your Web Service worker APIs are also designed to be fully asynchro-
pages. Research after research has shown that users prefer nous; this means that synchronous APIs, such as localStor-
to use fast loading Web pages. In fact, if your Web page age, are inaccessible from service workers directly. Addi-
takes more than three seconds to load, that might be a lost tionally, they must work only on HTTPS. A service worker
customer. That’s just one of the things a PWA can do for you. can’t access the DOM directly, but must work through the
I was amazed to open my developer tools on my browser and postMessage method to send data and a message to an
see how many applications already use these technologies. event listener to receive data.

Without further ado, let’s get familiarized with core building As you can imagine, service workers, given their nature of
block of PWAs, the service worker. being able to run independent of the application itself,
open doors to some amazing capabilities. Some of these ca-
pabilities are offline; first application pattern and leverag-
Service Worker ing caching, a Notifications API that allows your application
Sometimes, it’s hard to be a worker in the IT industry. For to interact with the operating system’s notification system,
example, if I were to use the words “service worker” at a a push API that lets your app subscribe to a push service
party, what would people think? So, before anyone casts of any kind, a background sync API that lets the user defer
any aspersions, let’s go ahead and define what a service actions until better network connectivity is available, and a
worker is. A service worker is a client-side programmable channel messaging API that service workers communicate

8 Progressive Web Apps (PWAs) codemag.com


with the main application, so the user can be prompted
for actions when new and interesting information becomes
available.

Let’s understand each one of these one by one.

Notifications API
The Notifications API allows your Web application to push
notifications to the user by taking advantage of the operat- Figure 1: The Notifications API requesting permission.
ing system’s notifications capabilities. You may have noticed
that on any modern operating system, applications can re-
quest to send you notifications when something of interest serviceWorkerRegistration
appears for you. These notifications can then be centrally .pushManager
managed in control panel, system preferences, settings etc. .subscribe(options).then(
Wouldn’t it be nice if a Web application could send such no- function(pushSubscription) {
tifications as well? Of course, this needs the user’s permis- // use the subscription
sion. That’s exactly the job of the Notifications API. You’ll });
be pleased to know that using the Notifications API is really
simple. Here is how you use the Notifications API. Background Sync API
The background sync API allows you to defer actions until
let promise = the user has stable connectivity. As you can imagine, this is SPONSORED SIDEBAR:
Notification.requestPermission(); critical for occasionally online applications. Remember that Are Your Legacy Apps
in a lot of places in the world, there isn’t reliable or good Stuck in the Past?
Notice that a lot of these APIs rely on a promise-based Internet connectivity. This isn’t a binary one or zero, as in
mechanism. This is because these APIs are designed to be being online or offline. Sometimes when you have poor con- Need FREE advice on
asynchronous. For example, the Notifications API returns a nectivity, you want to pull the phone out of your pocket, migrating yesterday’s
promise, which then waits for the user to either allow or dis- take a picture, and put it back in your pocket. Later when legacy applications
allow sending notifications. This dialog asking for permis- you have better connectivity, you can perhaps upload that to a today’s modern
sion is specific to the operating system and browser you’re picture. This is exactly what the background sync API lets platforms? Get answers
on, but on Mac and Safari, it looks like Figure 1. you do. by taking advantage of
CODE Consulting’s years
The Notifications API comes with a rich set of methods and Just like many other APIs, the background sync API also relies of experience migrating
properties that let you do the various things you’d expect on service workers. It uses a service worker as an event tar- legacy applications by
from a first-class notification infrastructure. For example, it get, which allows it to work in a background mode when the contacting us today to
lets you check whether the user has granted a permission Web page isn’t open. You use it in two steps: First you register schedule your free hour of
CODE consulting call with
or not. It lets you craft up a notification and it lets you your service worker and listen for sync events from a page,
our expert consultants.
show the notification. It then gives you the various events and second you listen to the event in your service worker.
No strings. No commitment.
necessary to handle the notification, for example whether For more information,
it’s visible, whether it has been dismissed etc. In scenarios To register, you use this code: visit www.codemag.com/
where the user hasn’t granted permissions for notifications, consulting or email us at
you can re ask the user to grant such a permission, or simply navigator.serviceWorker [email protected].
show an alert box or some other mechanism for communi- .register('/mysw.js');
cating with the user.
Once registered, you can then request for a sync:
Push API
The push API allows your application to receive messages navigator.serviceWorker.ready.then(
pushed to it from the Web server. Note that this is differ- function(registration) {
ent from your standard Ajax calls. The push API allows your return registration.sync.register(
application to receive messages when the application isn’t syncEvent');
even loaded. The push API works hand-in-hand with the });
Notifications API; for example, a pushed message can be
shown as a notification. Later, when there’s a sync event matching the phrase “sync
Event”, you can act upon it in your mysw.js file as shown
For your application to receive push messages, it must have here:
an active service worker. When the service worker is active,
you simply subscribe to push notifications using the below self.addEventListener('sync',
code snippet: function(event) {
if (event.tag == 'syncEvent') {
let promise = PushManager.subscribe(); event.waitUntil(doStuff());
}
This gives you a PushSubscription object, which has all });
the information, such as the subscription URLs endpoint,
expiration time of the push subscription, etc. Realistically The doStuff method returns a promise and that’s where you
speaking, to use the push manager, you’ll always need a can work on the actual task. If the promise fails, another
service worker. So once you have a service worker registra- sync is automatically scheduled for you. Retry sync waits for
tion, your code will look like this: connectivity and also uses exponential back off.

codemag.com Progressive Web Apps (PWAs) 9


Now, don’t let the name background sync fool you; this is Installation
actually a very useful feature that can be used for more than If the service worker registration succeeds, you can attempt
just syncs. For example, imagine that you were writing a to install it next. This would generally be dumb if you’re
chat program. You could use background sync to alert the attempting to install a new service worker at a scope that
user of a new chat message. Think of it this way: Anytime didn’t previously have a registered service worker, or you
you have a very small bit of information that you want to are installing a new service worker on a previously installed
push from the server back to the client, even when the user scope.
has navigated away from the page, that’s where background
sync is your friend. Installing a service worker simply means that you need to
listen to the “install” event. This means that you listen to
the “install” event listener, in the service worker, perform
Channel Messaging API some task once you are informed that the service worker
How often you have two different browsing contexts, for ex- is done installing. An example usage of this could be, hey,
ample two different iframes that need to communicate with my service worker is now installed, let me go ahead and
each other? That’s exactly the problem that the channel mes- pre cache some portions of the page so the next time the
saging API solves. The way this works is that you create a user opens this page, it reads from the cache. The next code
message channel using the MessageChannel() constructor. snippet shows you how you can listen to the installation
Once the message channel is created, the two ports of the event and act upon it. Remember, this code goes in the sw.js
channel can be accessed through port1 and port2 proper- file (or whatever service worker file you have chosen).
ties on the MessageChannel object. The app that created the
channel uses port1 and the app on the other side uses port2. self.addEventListener('install',
function (event) {
// do work
Service Worker Lifecycle });
In order to use service workers effectively, the first thing
that you need to learn is the service worker lifecycle. There Activation
are three main steps that a service worker goes through in There’s a possibility that when you install a service worker,
its lifecycle: registration, installation, and activation. you may be replacing a service worker in an existing scope.
For any given scope there can be only one service worker
Registration active at a time. When you’re replacing an existing service
This is the first step you need to do in order to use a service worker, the existing service worker needs to finish working.
worker. Registering a service worker tells the browser where The new service worker enters a waiting state. In such an
your service worker is located and to start installing it in instance, when the new service worker finally activates, an
the background. The code shown in Listing 1 shows how to activate event is triggered in the activating service order.
register a service worker. This event listener is where you can start performing a task.
Listening to this event listener is as simple as the code
The code shown in Listing 1 uses a file called sw.js where shown below.
the actual service order code lives. You can also choose to
register your service worker at a well-defined scope as can self.addEventListener('activate',
be seen here: function (event) {
// do work
navigator.serviceWorker.register('/sw.js', { });
scope: '/app/'
}); With these basic fundamentals understood, now let’s try to
solidify your understanding with an example.
By registering a service worker at a well-defined scope, as
is the case here, is on the /app/ scope. This service worker
intercepts requests to /app/* URLs. A Simple Offline Application
So far in this article, my focus has been around the service
There’s an additional setting you need to do on the server worker. Believe it or not, service worker is just one aspect
side as well. The server side needs to include a Service- of PWAs. There’s so much more to learn, and I hope to talk
Worker-Allowed header matching the scope you’ve defined. more about those in future articles, but meanwhile, let’s
solidify your understanding with what you’ve learned so far
with a simple example. In this very simple example, I’m go-
Listing 1: Service worker registration ing to write a simple application that works both offline and
if ('serviceWorker' in navigator) { online with the help of service workers.
navigator.serviceWorker.register('/sw.js')
.then(function (registration) { Although the concepts of PWAs are specific to HTML in Ja-
console.log(
'Registration successful, scope:' vaScript, you do need a Web server to understand what’s
, registration.scope); going on here. You can choose to use any Web server. I’ve
}) chosen to use NodeJS, although I assure you that the Node-
.catch(function (error) { JS parts are the simplest. Feel free to replace the NodeJS
console.log(
'Registration failed, error:' parts with any other Web server you wish.
, error);
}); To keep my demo clean, let’s get the NodeJS parts out of
} the way first. Go ahead and create a new directory on your

10 Progressive Web Apps (PWAs) codemag.com


INDUSTRY’S FASTEST DATA CONNECTORS

















 
 
 
 
 



  


  
  
  
  

 

codemag.com Title article 11


Listing 2: The simple Web server
const express = require('express');
const app = express();

app.use(express.static(__dirname));

const server = app.listen(8000, () => {


const host = server.address().address;
const port = server.address().port; Figure 2: My HTML page is working.
console.log(
'Listening at http://%s:%s', host, port);
});
disk that will hold the solution. In this directory, run the
following command to give yourself an empty package.json
Listing 3:The index.html file registering the service worker. file.

<!doctype html> npm init -y


<html>
<body>
<h1>My awesome page</h1> Now you’re going to need a simple Web server, so I’ll go
<script> ahead and take a dependency on expressjs, which is a very
if ('serviceWorker' in navigator) { popular node package used to create simple Web applica-
window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js') tions. Use the following command to install express JS.
.then(sw => {
console.log( npm i express
'Service Worker is registered', sw);
})
.catch(err => { Now go ahead and create a file called server.js, and in this
console.error( file, go ahead and place the code, as shown in Listing 2.
'Service Worker Error', err);
});
}); As can be seen in Listing 2, you’ve created a very simple
} Web server. Now any files that you put in the root of the
</script> project will be served over port 8000. This isn’t a production
</body>
</html> quality Web server, but it’ll get the job done for what you
need here.

Listing 4: Caching files after installation And here is where NodeJS-specific stuff ends. You can re-
place everything I talked about here so far with any Web
const filesToCache = [ server you wish. Now let’s focus on the PWA parts that are
'/',
'index.html' entirely client side.
];
Now go ahead and place an index.html and sw.js file in the
const staticCacheName = 'mycache';
same folder. The index.js file is where the simple application
self.addEventListener('install', event => { will live, and the sw.js file is where the service worker will
console.log('Installing service worker'); live.
event.waitUntil(
caches.open(staticCacheName)
.then(cache => { In the index.html file, go ahead and place the code, as
console.log('Caching file'); shown in Listing 3.
return cache.addAll(filesToCache);
}) if you pay close attention to Listing 3, the HTML page has
);
}); a simple message. It says, “My awesome page.” However,
there’s an interesting JavaScript snippet there that regis-
ters a service worker. The service worker is defined in the
Listing 5: Serving files from the cache sw.js file.
self.addEventListener('fetch', event => {
console.log('Fetch event for ', event.request.url); In sw.js file, you do two things. First, you listen to the in-
event.respondWith( stall event. Once the service worker has installed, you cache
caches.match(event.request) the files that are of interest to you. These files are cached
.then(response => { because later, they’ll allow the application to work offline.
if (response) {
console.log( In this case, you intend to cache only the index.html file,
'Found ', event.request.url, ' in cache'); which is the only file in your project. You can, of course,
return response; extrapolate this to other scenarios. This code can be seen
}
console.log(
in Listing 4.
'Network request for ', event.request.url);
return fetch(event.request) Now that the files are cached, the next thing you do in
}).catch(error => { the service worker is listen to the fetch event. In the fetch
console.log('Error, ', error);
}) event, if the file is available in the cache, you simply serve it
); from the cache. This saves a server roundtrip, and it allows
}); the application to work offline. The code for this can be seen
in Listing 5.

12 Progressive Web Apps (PWAs) codemag.com


Figure 3: Service worker debugging the UI.

Now go ahead and run your application using the following


node command. If you’ve chosen to use a Web server other
than node, feel free to serve the files over a URL.

node server.js

Once the server is running, open your browser and visit loc-
alhost:8000. I’ll use chrome, but really any browser that lets
you debug service workers will do. Your page should load as
shown in Figure 2

Now open the development tools of your Chrome browser


and visit the application tab. Inside the application tab, Figure 4: Emulating the offline mode.
look for service workers. You should see something like that
shown in Figure 3. This shows you that your service worker
registered. You can also use this development tool to send ing use of such concepts even if you don’t recognize them
simple push or sync messages if you wish. as applications. These are websites that you use daily. You’d
also be amazed to see how many applications in the App
Now switch over to the network tab and choose the drop- Store are already using these concepts. And that all modern
down indicated in Figure 4 to emulate offline mode. browsers today implement these concepts on all platforms.

At this point, Chrome is practically offline. Now go ahead My point is that you don’t have to go full-blown PWA to
and hit refresh on the page. What do you notice? You’ll see start taking advantage of the involved technologies. I must
that the page loads just as expected, except that the net- also confess that this article is at a very beginner level for
work call never made it to the Web server. You can verify PWAs. There’s a lot more to learn: there are concepts, such
that by setting a breakpoint in server.js, and the breakpoint as how to design your applications in a responsive manner;
won’t get hit. This is great, because now the application is there are tools available, such as lighthouse, that allow you
working offline. It’s working because the service worker has to analyze your PWAs; there are concepts around debugging
intercepted the request and is serving index.html from its your PWAs; and then there are many other APIs, such as
local cache. This is how the world’s simplest PWA is built. credential management API etc., that I didn’t even get to
touch upon.
Summary In my future articles, I hope to talk more about such addi-
Let me be honest here, PWA is a groundbreaking technol- tional aspects of PWAs. Until then, happy coding.
ogy. If you examine your Chrome developer tools, under the
application tab, under service worker, you’ll be amazed to  Sahil Malik
see how many websites you commonly visit are already mak- 

codemag.com Progressive Web Apps (PWAs) 13


ONLINE QUICK ID 2009031

Use the MVVM Design Pattern


in MVC Core, Part 3
In the articles “Use the MVVM Design Pattern in MVC Core: Part 1” and “Part 2” earlier this year in CODE Magazine, you created
an MVC Core application using the Model-View-View-Model (MVVM) design pattern. In those articles, you created a Web
application using VS Code and a few Class Library projects in which to put your entity, repository, and view model classes.

With the product table from the AdventureWorksLT data- the IProductRepository.cs interface class and add a new
base, you created a page to display product data in an HTML method stub. This method accepts a single integer value for
table. In addition, you wrote code to search for products the primary key of the product to retrieve.
based on user input.
Product Get(int id);
In this final part of this article series, you build a product
detail page to add and edit product data. You add a delete Next, open the ProductRepository.cs class and add a new
button on the list page to remove a product from the data- method to implement the Get(id) method in the interface.
Paul D. Sheriff base. You learn to validate product data and display valida- The code for this method uses the LINQ FirstOrDefault()
http://www.pdsa.com tion messages to the user. Finally, you learn to cancel out of method to locate the product ID in the product table. If
an add or edit page, bypassing validation and returning to the value is found, a Product object is returned from this
Paul has been in the IT the product list page. method. If the value is not found, a null value is returned.
industry over 33 years.
In that time, he has suc- public Product Get(int id)
cessfully assisted hundreds Create Product Detail Page {
of companies to architect
At some point, your user will want to add or edit the details return DbContext.Products.FirstOrDefault(
software applications to
of one product. Because they can’t see all the product fields p => p.ProductID == id);
solve their toughest business
problems. Paul has been on the list page, create a detail page as shown in Figure 1. }
a teacher and mentor Add a new partial page named _ProductDetail.cshtml under
through various mediums the \Views\Product folder. In this new partial page, add the Modify the View Model Classes
such as video courses, code shown in Listing 1. Two new properties need to be added to your view model
blogs, articles, and speaking classes to support adding or editing a single product object.
engagements at user To get to the new product detail page, add an Edit button in First, open the ViewModelBase.cs class and add a Boolean
groups and conferences each row of the product table. Open the _ProductList.cshtml value to tell the view to display the detail page you just
around the world. partial page and add a new table header element before the added.
Paul has 23 courses in the other <th> elements in the <thead> area.
www.pluralsight.com library public bool IsDetailVisible { get; set; }
(http://www.pluralsight. <th>
com/author/paul-sheriff) Edit Next, open the ProductViewModel.cs class and add a new
on topics ranging from </th> property to hold the instance of the product class returned
JavaScript, Angular, MVC, from the Get(id) method in the repository class.
WPF, XML, jQuery, and Within the <tbody> area, add a new table detail element before
Bootstrap. Contact Paul all the other <td> elements, as shown below. Notice the two public Product SelectedProduct { get; set; }
at [email protected]. data- attributes that are added to the anchor tag. The data-
custom-cmd attribute has a value of “edit,” which is sent to the The product ID is posted back from the view to the view
view model to place the user into edit mode. The second attri- model class using the EventArgument property. Add a new
bute, data-custom-arg, is filled in with the primary key value of method named LoadProduct() to which you pass this prod-
the product to edit. This key value is used by the Entity Frame- uct ID. This method calls the new Get(id) method in the
work to lookup the details of the product and fill in a single Repository class to retrieve the product selected by the user.
product object to which the product detail page is bound.
protected virtual void LoadProduct(int id) {
<td> if (Repository == null) {
<a href="#" throw new ApplicationException(
data-custom-cmd="edit" "Must set the Repository property.");
data-custom-arg="@item.ProductID" } else {
class="btn btn-primary"> SelectedProduct = Repository.Get(id);
Edit }
</a> }
</td>
Remember that in the controller class, the HandleRequest()
Modify Repository method is always called from the post back method. In this
To retrieve a single product object based on the primary key method, add a new case statement to check for the “edit”
value, add a new method to your repository classes. Open command passed in via the data-custom-cmd attribute. Call

14 Use the MVVM Design Pattern in MVC Core, Part 3 codemag.com


the LoadProduct() method passing in the EventArgument
property that was set from the data-custom-arg attribute.
If the SelectedProduct property is set to a non-null value,
set the IsDetailVisible property to true. The SelectedProduct
property is only set to true if a product was found in the
Get(id) method of the Repository class.

case "edit":
LoadProduct(Convert.ToInt32(EventArgument));
IsDetailVisible = (SelectedProduct != null);
break;

Modify Views to Display Detail Page


Just like you did for the other properties in the ViewMod-
elBase class, you need to store the IsDetailVisible property
in a hidden input field so it can be posted back to the view
model. Open the \Views\Shared\_StandardViewModelHid-
den.cshtml file and add the following line of code.

<input type="hidden"
asp-for="IsDetailVisible" />

Use the IsDetailVisible property on the Products.cshtml to


either show the product detail or to show the search and
product list partial pages. Open the Products.cshtml file and
modify the code within the <form> tag to look like the fol-
lowing code snippet.

<form method="post">
<partial name="~/Views/Shared/
_StandardViewModelHidden.cshtml" />
@if (Model.IsDetailVisible) {
<partial name="_ProductDetail"
for="SelectedProduct" />
}
else {
<partial name="_ProductSearch.cshtml" />
<partial name="_ProductList" />
}
</form>

In the _ProductDetail.cshtml page, set the model as an


instance of MVVMEntityLayer.Product class. Use the “for”
attribute on the <partial> element to pass in the Selected-
Product property of the product view model to the product
detail partial page. You don’t need all of the properties of
the product view model in the product detail page, so the
“for” attribute allows you to just pass what you need.

Try It Out
Now that you’ve created the detail page and added the appro-
priate properties to the view model, it’s time to see the detail
data. Run the application and click on an Edit button in your
product list. If you’ve done everything correctly, you should be
taken to the detail page and the product’s data should appear.

Figure 1: The product detail page allows you to add or edit data.
Using Data Annotations
When you ask a user to input data, you must ensure that
they enter the correct values. Typically, you want to validate When you first created the Product class, you added a few
the user’s input both on the client-side and the server-side. data annotations, such as [DataType] and [Column]. These
Use client-side validation to make the Web page responsive annotations help MVC map the data coming from the da-
and eliminate the need for a round-trip to the server just tabase table to the appropriate properties. They also tell
to check business rules. Use server-side validation to en- MVC what data type to put into the <input> element for the
sure that the user didn’t bypass the client-side validation by bound property. In addition, they help validate the values
turning off JavaScript. posted back by the user.

codemag.com Use the MVVM Design Pattern in MVC Core, Part 3 15


Add Additional Data Annotations to Product Class Look at Figure 1 and notice that the labels for the input
It’s now time to add additional annotations to help with valida- fields are simply the property names. These are not good
tion. Open the Product.cs file and make the file look like Listing labels for your user, so add the [Display] attribute and set
2. In this listing, you see additional annotations, such as the the Name property to what you want displayed to the user
[Required] attribute. This adds client- and server-side code to for each property. The [Range] attribute allows you to set a
ensure that the user has entered a value for the property this minimum and maximum range of values that the user is al-
annotation decorates. The [StringLength] attribute adds code to lowed to enter for a specific property. There are many other
ensure that the user hasn’t entered too many characters into an data annotations that you can apply to properties of your
input field. Optionally, you can add a MinimumLength property to entity classes. Do a search for data annotations on Google
ensure that they enter at least a minimum amount of characters. to get a complete list.

Listing 1: The product detail page lets your user see all of the fields to add or edit.
@model MVVMEntityLayer.Product <label asp-for="Weight" class="control-label"></label>
<input asp-for="Weight" class="form-control" />
<input asp-for="ProductID" type="hidden" /> </div>
<div class="form-group"> <div class="form-group">
<label asp-for="Name" class="control-label"></label> <label asp-for="SellStartDate" class="control-label">
<input asp-for="Name" class="form-control" /> </label>
</div> <input asp-for="SellStartDate" class="form-control" />
<div class="form-group"> </div>
<label asp-for="ProductNumber" class="control-label"> <div class="form-group">
</label> <label asp-for="SellEndDate" class="control-label">
<input asp-for="ProductNumber" class="form-control" /> </label>
</div> <input asp-for="SellEndDate" class="form-control" />
<div class="form-group"> </div>
<label asp-for="Color" class="control-label"></label> <div class="form-group">
<input asp-for="Color" class="form-control" /> <label asp-for="DiscontinuedDate"
</div> class="control-label">
<div class="form-group"> </label>
<label asp-for="StandardCost" class="control-label"> <input asp-for="DiscontinuedDate"
</label> class="form-control" />
<input asp-for="StandardCost" class="form-control" /> </div>
</div> <div class="form-group">
<div class="form-group"> <button data-custom-cmd="save"
<label asp-for="ListPrice" class="control-label"> class="btn btn-primary">
</label> Save
<input asp-for="ListPrice" class="form-control" /> </button>
</div> <button formnovalidate="formnovalidate"
<div class="form-group"> class="btn btn-info">
<label asp-for="Size" class="control-label"></label> Cancel
<input asp-for="Size" class="form-control" /> </button>
</div> </div>
<div class="form-group">

Listing 2: Add data annotations to help with labels and validation.


public partial class Product [Range(0.01, 9999)]
{ [Column(TypeName = "decimal(18, 2)")]
public int? ProductID { get; set; } public decimal ListPrice { get; set; }
[Display(Name = "Product Name")]
[Required] [StringLength(5)]
[StringLength(50, MinimumLength = 4)] public string Size { get; set; }
public string Name { get; set; }
[Column(TypeName = "decimal(8, 2)")]
[Display(Name = "Product Number")] [Range(0.5, 2000)]
[StringLength(25, MinimumLength = 3)] public decimal? Weight { get; set; }
public string ProductNumber { get; set; }
[Display(Name = "Start Selling Date")]
[StringLength(15, MinimumLength = 3)] [Required]
public string Color { get; set; } [DataType(DataType.Date)]
public DateTime SellStartDate { get; set; }
[Display(Name = "Cost")]
[Required] [Display(Name = "End Selling Date")]
[DataType(DataType.Currency)] [DataType(DataType.Date)]
[Range(0.01, 9999)] public DateTime? SellEndDate { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal StandardCost { get; set; } [Display(Name = "Date Discontinued")]
[DataType(DataType.Date)]
[Display(Name = "Price")] public DateTime? DiscontinuedDate { get; set; }
[Required] }
[DataType(DataType.Currency)]

16 Use the MVVM Design Pattern in MVC Core, Part 3 codemag.com


Display a Summary of Validation Messages
Each of the data annotations applied to the Product class
properties generates an error message if the data doesn’t
pass the checks. You need to display these messages to the
user. These messages may be displayed all in one area, as
shown in Figure 2.

Or, they may be displayed near each field that’s in error, as


shown in Figure 3.

To display the validation messages all in one location, open


the _ProductDetail.cshtml file and immediately before the
first <input> field, add the following <div> element.

<div asp-validation-summary="All"
class="text-danger">
</div>

For any client-side validation to work and for the mes-


sages be displayed, you must include the jQuery validation
script files. These files are included in the project when
you created the MVC Core project. They’re in the \Views\
Shared\_ValidationScriptsPartial.cshtml file. All you need
to do is to include them in your product page. Open the
Products.cshtml file and add a <partial> element just before
the <script> tag.

@section Scripts
{
<partial name="_ValidationScriptsPartial"/>
<script>
Figure 2: The validation error messages can be displayed all in one area if you desire.

</script>
}

Try It Out
Run the application and click on the Edit button next to one
of the products. Delete the data in the Product Name field.
Set the Cost and the Price fields to a value of minus one (-1)
and click on the Save button. You should now see the error
messages appear at the top of the page just like that shown
in Figure 2.

Display Individual Validation Messages


Instead of putting all validation messages into a single area
at the top of the page, you may place each individual message
close to the input field that is in error. Add a <span> below
the input for the Name property that looks like the following:

<div class="form-group">
<label asp-for="Name"
class="control-label"></label>
<input asp-for="Name"
class="form-control" />
<span asp-validation-for="Name"
class="text-danger" />
</div>

Following the same pattern as above, add <span> classes


for each input field on this page that has a data annota-
tion. Add the following <span> elements at the end of each
“form-group” for the appropriate input field.

<span asp-validation-for="ProductNumber"
class="text-danger" />
<span asp-validation-for="Color" Figure 3: The validation error messages can be displayed near each field in error.

codemag.com Use the MVVM Design Pattern in MVC Core, Part 3 17


class="text-danger" /> @* <partial
<span asp-validation-for="StandardCost" name="_ValidationScriptsPartial" /> *@
class="text-danger" />
<span asp-validation-for="ListPrice" Run the application and click on the Edit button next to one of
class="text-danger" /> the products. Delete the data in the Product Name field. Set the
<span asp-validation-for="Size" Cost and the Price fields to a value of minus one (-1) and click
class="text-danger" /> on the Save button. You should now see the error messages
<span asp-validation-for="Weight" appear at the top of the page just like that shown in Figure 2.
class="text-danger" />
<span asp-validation-for="SellStartDate"
class="text-danger" /> Cancel Button
<span asp-validation-for="SellEndDate" If the user clicks the Edit button on the wrong product, they need
class="text-danger" /> a way to cancel and return to the product list page. They can ei-
<span asp-validation-for="DiscontinuedDate" ther hit the Back button on the browser, or the Cancel button
class="text-danger" /> at the bottom of the product detail page. You need to add some
JavaScript code to set the EventCommand property to cancel. Set-
Try It Out ting this property informs the view model class that it needs to
Run the application and click on the Edit button next to one go back to the list page. Open the Products.cshtml file and add
of the products. Delete the data in the Product Name field the following function immediately before the close </script> tag.
and press the Tab key. You should immediately see a mes-
sage appear below the product name field. Next, enter just function cancel() {
two characters in the Product Number field and press the // Fill in command to post back
Tab key. Again, you should see a message appear immedi- $("#EventCommand").val("cancel");
ately below the field. You can continue entering bad data in $("#EventArgument").val("");
each field to see messages appear just below each field. Your
screen might look something like that shown in Figure 3. return true;
}
Checking the Data Server-Side
If the user has turned off JavaScript, or you have a hacker who Next, open the _ProductDetail.cshtml file and add an on-click
is trying to post back to your controller with bad data, you need event to the Cancel button as shown below. When the user
to ensure that you don’t let bad data get into your database. clicks on the Cancel button, the function cancel() is called
Open the ProductController.cs file and locate the Post method. and the EventCommand property is set to “cancel” and the
Wrap up all the code, except the return statement, into an if EventArgument property is set to an empty string.
statement that checks the ModelState.IsValid property.
<button formnovalidate="formnovalidate"
[HttpPost] class="btn btn-info"
public IActionResult Products( onclick="cancel();">
ProductViewModel vm) { Cancel
if (ModelState.IsValid) { </button>
vm.Pager.PageSize = 5;
vm.Repository = _repo; You now need to handle the “cancel” code in your controller and
vm.AllProducts = your view model. Open the ProductViewModel.cs file and locate
JsonConvert the HandleRequest() method. Add another case statement for
.DeserializeObject<List<Product>>( the “cancel” mode. Because you were just in the “edit” mode,
HttpContext.Session the Products collection is not filled in. Call the SortProducts()
.GetString("Products")); and the PageProducts() methods to fill in the Products collec-
vm.HandleRequest(); tion so that the list of products can be displayed. Setting the
IsDetailVisible property to a false value causes the search and
ModelState.Clear(); list partial pages to be displayed.
}
return View(vm); case "cancel":
} SortProducts();
PageProducts();
When you post the data back, MVC automatically runs code IsDetailVisible = false;
to check all the data in your bound fields’ data annotations. break;
If any of the checks fail, the IsValid property is set to false.
All you need to do is to check this value and bypass any code Open the ProductController.cs and in the Post method,
that would otherwise save the data. All the validation mes- modify the If statement you added earlier to look like the
sages are now displayed just like they were with the client- following snippet. Because you’re cancelling out of an add
side validation. or edit, you don’t want the validation rules to be checked;
instead you want the code to go right into the HandleRe-
Try It Out quest() method in the view model.
To try out the server-side validation, you need to comment
out the client-side jQuery validation code. Open the Prod- [HttpPost]
ucts.cshtml file and comment out the <partial> tag you en- public IActionResult Products(
tered earlier. ProductViewModel vm) {

18 Use the MVVM Design Pattern in MVC Core, Part 3 codemag.com


if (ModelState.IsValid || Getting the Sample Code
vm.EventCommand == "cancel") {
®
// OTHER CODE HERE You can download the sample
} code for this article by visiting
www.CODEMag.com under

Instantly Search
return View(vm); the issue and article, or by
}
visiting www.pdsa.com/
downloads. Select “Fairway/
Try It Out
Run the application and click the Edit button on any product
PDSA Articles” from the
Category drop-down.
Then select “Use the MVVM
Terabytes
in the list. Click the Cancel button and you should be taken Design Pattern in MVC Core -
back to the list of products. Part 3” from the Item drop-down.

Add a Product dtSearch’s document filters


You have written code to edit the data of an existing product. support:
Your user also needs the ability to add a new product. You use • popular file types
the same detail page for adding as you do for editing product
data. The difference between an add and an edit is that when • emails with multilevel
you click an Add button, you want the detail screen to display attachments
blank fields ready to accept a new product record. • a wide variety of databases
To create an empty instance of a Product object, create a
• web data
new method in the Repository classes that can be called
from the view model. Open the IProductRepository.cs file
and add a new interface method named CreateEmpty(). Over 25 search options
including:
Product CreateEmpty();
• efficient multithreaded search
Open the ProductRepository.cs file and implement the Cre- • easy multicolor hit-highlighting
ateEmpty() method to create a blank Product object. You • forensics options like credit
may default any of the product properties to any values you
want. In the code below, I set the StandardCost to one and
card search
the ListPrice to two. The SellStartDate property is set to the
current date and time. These default values could also be
read in from a configuration file if you want. Developers:
public virtual Product CreateEmpty() {
• SDKs for Windows, Linux,
return new Product { macOS
ProductID = null, • Cross-platform APIs for
Name = string.Empty, C++, Java and NET with.
ProductNumber = string.Empty,
Color = string.Empty,
.NET Standard / NET Core .
StandardCost = 1, • FAQs on faceted search,
ListPrice = 2, granular data classification,
Size = string.Empty, Azure and more
Weight = 0M,
SellStartDate = DateTime.Now,
SellEndDate = null,
DiscontinuedDate = null,
}; Visit dtSearch.com for
}
• hundreds of reviews and
Now that you have a method to create a blank Product ob- case studies
ject, you need to call that method from the ProductView-
Model class and set the SelectedProduct property to the
• fully-functional enterprise
empty Product object. Open the ProductViewModel.cs file and developer evaluations
and add a new method named CreateEmptyProduct(), as
shown in the following code snippet. The Smart Choice for Text
public virtual void CreateEmptyProduct() {
Retrieval® since 1991
SelectedProduct = Repository.CreateEmpty();
} 1-800-IT-FINDS
Add a new case statement in the HandleRequest() method
www.dtSearch.com
so when the “add” command is sent in the EventCommand

codemag.com Use the MVVM Design Pattern in MVC Core, Part 3 19


property, the CreateEmptyProduct() method is called and DbContext.Products.Update(entity);
the IsDetailVisible property is set to true.
// Save changes in database
case "add": DbContext.SaveChanges();
CreateEmptyProduct();
IsDetailVisible = true; return entity;
break; }

Open the _ProductSearch.cshtml file and create the Add Open the ProductViewModel.cs file and add a Save() meth-
button immediately after the Search button. od. The Save() method checks the value in the ProductID
property of the Product object to see if it’s null or not. If
<button type="button" class="btn btn-info" it isn’t null, you’re editing a product and thus you call the
data-custom-cmd="add"> Update() method on the Repository object. If the ProductID
Add New Product property is a null, then it’s a new product object and you call
</button> the Add() method on the Repository object.

Try It Out public virtual bool Save() {


Run the application and click on the Add button. You should if (SelectedProduct.ProductID.HasValue) {
now see the detail page with blank data in each input field. // Editing an existing product
The Save button does NOT yet work, but that’s what you’re Repository.Update(SelectedProduct);
going to code next. }
else {
// Adding a new product
Save the Product Data Repository.Add(SelectedProduct);
Whether the user performs an add or an edit on the product }
data, the product detail page is displayed. When the user
clicks on the Save button, the view model attempts to either return true;
insert or update the Product table with the data input by }
the user. Open the IProductRepository.cs file and add two
new interface methods to support either adding or updating Locate the HandleRequest() method and add a new case
product data. statement to call the Save() method when the user clicks on
the Save button. If the Save() is successful, set the AllProd-
Product Add(Product entity); ucts property to a null value. This ensures that all records in
Product Update(Product entity); the Product table are reread from the database and brought
back into memory for display in the HTML table.
Open the ProductRepository.cs file and add a method to
insert a Product object into the database using the Entity case "save":
Framework. if (Save()) {
// Force a refresh of the data
public virtual Product Add(Product entity) { AllProducts = null;
// Add new entity to Products DbSet SortProducts();
DbContext.Products.Add(entity); PageProducts();
IsDetailVisible = false;
// Save changes in database }
DbContext.SaveChanges(); break;

return entity; Try It Out


} Run the application, click on the Add button and add some
good data to the product detail page. Set the Product Name
The above code adds the new Product entity object to the field to the value “A New Product”. Click the Save button
Products DbSet<> collection in the AdventureWorks DbCon- and you should see the new record appear in the table. Click
text object. Calling the SaveChanges() method on the Db- on the Edit button of the new product you just added and
Context object creates an INSERT statement from the values modify some of the fields. Click on the Save button and you
in the entity parameter and sends that statement to the should see the changed values appear in the table.
database for execution.

Add another new method in the ProductRepository class to Delete a Product


update the product data. In the Update() method, a Product You’ve given the user the ability to add and edit products
object is passed to the Update() method on the Products in the table. They also need the ability to delete a prod-
DbSet<> collection. When the SaveChanges() method is uct. Open the _ProductList.cshtml file and add a new table
called on the DbContext object, an Update statement is cre- header at the end of the table columns immediately before
ated from the values in the updated Product object and that the closing </tr> and </thead> elements.
statement is sent to the database for execution.
<th>
public virtual Product Update(Product entity) { Delete
// Update entity in Products DbSet </th>

20 Use the MVVM Design Pattern in MVC Core, Part 3 codemag.com


In the <tbody> element, after all the other <td> elements, Open the ProductViewModel.cs file and add a DeleteProd-
add a new table detail, as shown in the code snippet below. uct() method to call the Delete() method in the Repository
object.
<td>
<a href="#" public virtual bool DeleteProduct(int id)
onclick="deleteProduct(@item.ProductID);" {
class="btn btn-secondary">Delete Repository.Delete(id);
</a>
</td> // Clear the EventArgument so it does
// not interfere with paging
The onclick of the anchor tag you just added calls a JavaScript EventArgument = string.Empty;
function named delete(). This function needs to be added in
the <script> tag in the Products.cshtml file. Open the Prod- return true;
ucts.cshtml file and add the Delete function, as shown in the }
following code snippet.
Add a new case statement in the HandleRequest() method
function deleteProduct(id) { to handle the “delete” command. If the DeleteProduct()
if(confirm("Delete this product?")) { method returns true, refresh all of the product data again
// Fill in command to post back to view model so the complete product list can be redisplayed. I haven’t
$("#EventCommand").val("delete"); added any exception handling in the code in this article to
$("#EventArgument").val(id); keep it simple. In a real-world application, add the appro-
priate exception handling code, and return a true or false
// Submit form with hidden values filled in from the Delete() and Save() methods as appropriate.
$("form").submit();
return true; case "delete":
} if(DeleteProduct(
else { Convert.ToInt32(EventArgument))) {
return false; // Force a refresh of the data
} AllProducts = null;
} SortProducts();
PageProducts();
The code in the JavaScript function asks the user if they }
really want to delete the current product. If they cancel the break;
dialog, a false value is returned from this function that can-
cels the delete action. If the user confirms the dialog, the Try It Out
command “delete” is added into the EventCommand prop- Run the application and click on the Delete button for the
erty and the ProductID of the product to delete is added product you added in the previous section of this article.
into the EventArgument property. The form is then submit- Clicking on the Delete button causes your browser to display
ted and the two values are mapped into the view model. a dialog to you with OK and Cancel buttons. Click on the OK
button and you should see the product disappear from your
You now need to add the functionality in your C# code to product list table.
delete a product. Open the IProductRepository.cs file and
add an interface method named Delete().
Summary
bool Delete(int id); Congratulations on finishing this three-part series on using
the MVVM design pattern in MVC Core. If you have followed
Open the ProductRepository.cs file and implement the De- along and input all of the code as you read, you have re-
lete() method. The following code passes in the ProductID ally learned a lot about how to design code that you can
to delete and the Find() method is called on the Products reuse in any other UI technology. This same set of Class Li-
DbSet<> collection to locate that product. The product ob- brary projects can be used in WPF, Web Forms, MVC, and
ject is removed from the collection and the SaveChanges() UWP. Throughout this series of articles, you learned to dis-
method is called on the DbContext object. This creates a play product data in an HTML table and search for product
Delete statement and submits that statement to the back- data. You learned to sort and page data. You then learned
end for processing. to add, edit, and delete product data. Most importantly, you
learned to do all this functionality using a view model class.
public virtual bool Delete(int id) By using a view model class, you make your code highly re-
{ usable and easy to test.
// Locate entity to delete
DbContext.Products.Remove(  Paul D. Sheriff
DbContext.Products.Find(id)); 

// Save changes in database


DbContext.SaveChanges();

return true;
}

codemag.com Use the MVVM Design Pattern in MVC Core, Part 3 21


ONLINE QUICK ID 2009041

When Open Source Came to Microsoft


Today you can see that Microsoft is an open-source company and really does build open-source software with all the essential
elements of open source, like accepting pull requests from the public. More Microsoft employees contribute to open source
projects on GitHub than any other company in the world. Building open source software via GitHub is the way a lot of

programmers at Microsoft make their living now. To some, patent-based lawsuits around software between the various
it may appear that this move to open-source development software players in the 1980s and 90s until eventually, the
started back at the Build conference in 2014. That was the cross-licensing of patents between the companies became
day that Anders Heljsberg took the stage during the keynote normal.
to publish the Roslyn compiler on GitHub as an open-source,
cross-platform tool. Or perhaps it started in the fall of 2014 Building software that ran on many microcomputers was
with the announcement of.NET Core. But those events were an early strategy at Microsoft, and it expanded when they
hardly the beginning, although you could consider those moved into the operating system business, first with MS-
events as the “end of the beginning” of a new Microsoft, A DOS and ultimately with Windows. When combined with the
Richard Campbell Microsoft that embraced open source. IBM PC architecture that allowed multiple hardware manu-
@richcampbell facturers to make compatible PCs, Microsoft grew to domi-
http://historyofdot.net/ Going back to the actual beginning of Microsoft in 1976, nate the microcomputer market by the end of the 1990s
richard@campbellassoci- when Bill Gates and Paul Allen created the company, Bill had with operating systems and development tools.
ates.ca a great insight into the future of software. In those days,
Richard Campbell wrote his software, at least software for the early microcomputers, Microsoft wasn’t the only beneficiary of its dominance; a
first line of code in 1977. was almost entirely free. It was also unique to the particular vast ecosystem of vendors and independent developers
His career has spanned microcomputer: Each new microcomputer needed new pro- made their living building software and products for Win-
the computing industry grams. The sharing of code was common. dows.
both on the hardware and
software sides, develop- In that period of rapid innovation of microcomputers, com- The dominance in the marketplace came with consequences
ment and operations. panies often competed with their own earlier products or also, ultimately leading to a court case brought by the U.S.
He was a co-founder of announced new products so early that it sabotaged existing Department of Justice around monopolistic behavior at
Strangeloop Networks, ac- sales. The lack of software was a constant problem, and de- Microsoft. By November of 1999, Judge Jackson declared
quired by Radware in 2013 velopers had to pick their favorite hardware to develop for, Microsoft a “predatory monopoly.” The judge ordered the
and was on the board of only to have the company make a new computer that the old company broken into two: One company would be an op-
directors of Telerik that was software wouldn’t run on—or go broke. erating system company and the other company would be
acquired by Progress Soft- everything else.
ware in 2014. Today he is a Bill Gates saw the need for software to be a product unto
consultant and advisor to itself, especially software that could be coded once and ex- The breakup never happened. In January 2000, Steve
several successful technol- ecuted on a variety of computers. In 1976, he wrote an open Ballmer took over as CEO of Microsoft, Bill Gates stayed on
ogy firms and is the founder
letter to the hobbyists at the Homebrew Computer Club, en- as chairman of the board and also moved into a chief ar-
and chairman of Humanitar-
couraging them to stop sharing software, specifically com- chitect role, overseeing all of the products that Microsoft
ian Toolbox (www.htbox.
org), a public charity that
mercial software. In the letter, he pointed out that if no one made. Steve’s first mission as CEO was to keep the company
builds open-source software can make a living from software, why would anyone go to together, and as we know, he succeeded, resulting in the
for disaster relief. Richard the effort to write and maintain it? consent decree with the US government in November 2001.
is also the host of two
podcasts: .NET Rocks! The idea of retail software in microcomputers was novel, and Part of that consent decree included Microsoft being more
(www.dotnetrocks.com) the it required legal protection. The protection came in the form open about its source code. Some of the complaints from
Internet Audio Talkshow for of the End User License Agreement (EULA), which changed the industry around Microsoft that led to the DOJ court
.NET developers and RunAs the definition of ownership of software: You never actually case had included concerns that Windows was hostile to
Radio (www.runasradio.com), own software, just a limited license to use it. Included in some third-party software. Although there was never any
a weekly show for IT Profes- those limits is protection for the developer for liability. Any evidence of this being true, Microsoft, as part of the con-
sionals. He also produces compensation for harm caused by the software is limited to sent decree, did agree to share part of the source code of
the DevIntersection the price of the software, typically. Windows with these third-party companies so that they
(www.devintersection.com) could better understand how Windows interacted with their
series of conferences. With software as the business and protection in the form of software.
the EULA, commercial software for microcomputers snow-
balled, and Microsoft was one of the leading companies Was this open source? Far from it. It was shared source, and
making software manufacture into their business and stay- that sharing took place under specific circumstances.
ing (for the most part) out of the hardware business.
It was into this world that .NET was born. Steve Ballmer
As a developer at Microsoft, you not only wrote proprietary announced the development of .NET the PDC conference in
closed-source software that the company sold but also filed 2000. Steve talked about .NET at the keynote as “a new plat-
patents for any innovative aspects of the software. The num- form based on Internet Standards.” He also announced the
ber of patents you secured around your code affected how publication of the C# specification and the Common Lan-
much Microsoft paid you. The creation of software patents guage Infrastructure as ECMA specifications. It’s important
is part of a broader “IP-Moat” strategy that led to numerous to remember that this was in the middle of the consent de-

22 When Open Source Came to Microsoft codemag.com


cree negotiations with the DOJ. Microsoft was very focused AJAX Control Toolkit that brought the power of asynchro-
on showing that it was a more open company than it had nous JavaScript programming to mainstream Microsoft Web
been in the past. developers .

Those ECMA specifications would inspire a young man by the Also announced at Mix06 was Internet Explorer 7 almost
name of Miguel de Icaza. Reading those specifications, he five years after Microsoft shipped Internet Explorer 6 and
decided that he liked C# and the whole concept of .NET. In WPF/E, or Windows Presentation Foundation Everywhere,
July of 2001 at an O’Reilly Conference, he announced the which would eventually be known as Silverlight. The first
development of Mono: An Open-Source implementation of CTP of Silverlight would ship in December 2006.
.NET for Linux.
In 2007, the energy around open source and Microsoft
It’s interesting to note that the month before (June 2001), technologies peaked with several different fronts opening
Steve Ballmer said in a newspaper article that “Linux is a at once. The first was ASP.NET MVC, based on the Model-
cancer.” He would, much later (2016), change his position View-Controller software design pattern. Built to address
on that statement. the challenges of ASP.NET WebForms, MVC would also work
alongside WebForms while offering a more maintainable ap-
Microsoft continued opening up its source code around .NET proach to Web development.
in 2002 with the release of the Shared Source Common Lan-
guage Infrastructure that was code-named “Rotor.” Rotor The next front in open-source at Microsoft was a movement
included the .NET Runtime, base class libraries, and a C# of open source-minded .NET developers, a group that would
compiler, but removed all the Windows-centric technolo- eventually be known as ALT.NET. At the time, a lot of devel-
gies. opers building on the Microsoft stack didn’t consider open-
source libraries or tools at all. That point of view existed
Rotor had what Microsoft called an “academic research li- within Microsoft as well: Perhaps a form of Not-Invented-
cense,” so it wasn’t open source—more like source open. Here syndrome, Microsoft tended to build tools for devel-
Anyone with academic credentials could request a copy, so opers even when there were excellent open source tools
Rotor was more broadly available than the source code for available.
Windows. Academics were only able to read the .NET code to
understand behavior. There wasn’t any way to compile the The final front comes in the fall of 2007, Scott Guthrie began
code for .NET or to use it in an application directly. recruiting folks like Scott Hanselman, Phil Haack, and Rob
Conery—all people with strong backgrounds in open source
The open-source story of .NET goes quiet for a few years technologies (some people would call them “Scott Guthrie’s
after the release of Rotor, but .NET itself evolved rapidly, Ninja Army”). On Phil Haack’s blog at the time, he men-
releasing 1.0 in February of 2002, 1.1 in April of 2003, and tioned seeing a product that inspired him to want to be part
2.0 in November of 2005. Microsoft was doing its best to be of Microsoft. That technology was the prototype of MVC.
a more open company with the advent of things like Channel
9, presenting the face of the developers at Microsoft who All of these fronts came together in October of 2007 at the
built products like .NET. Another aspect of openness was the ALT.NET Open Space Conference in Austin. Scott Guthrie had
willingness to show off the earliest bits of its next operating been invited to keynote at the conference and surprised
system, code-named Longhorn, although one could argue the attendees with the first public demonstration of ASP.
that they showed those bits off a bit too soon. NET MVC. He also announced that ASP.NET MVC would be an
open-source project published on CodePlex.
By 2006, .NET 2.0 was a relatively mature product, and de-
velopers had built large applications in ASP.NET WebForms. At the same time, Microsoft announced that the .NET Frame-
There were challenges around the maintainability of Web- work 3.5 libraries would be available under the Microsoft
Forms: The architecture of WebForms had focused on making Reference Source License (MS-RSL). The MS-RSL opened up
Web development feel like WinForms development, hiding the source code of the .NET Framework to anyone who want-
a lot of the metaphors of Web development. It was easy to ed to look at it, so in that sense, it was an expansion of the
learn and code in, but creating maintainable and testable Rotor project back in 2002 that was for academics only. Still,
Web applications was very difficult. the MS-RSL wasn’t making the .NET Framework open-source;
the primary purpose of the Reference Source was to help in
In the same time frame, David Heinemeier Hansson (DHH) debugging applications built with .NET.
was developing Ruby on Rails, first released as an open-
source project in July of 2004. Developers fell in love with The development of ASP.NET MVC did not take place directly
the Ruby on Rails approach to building Web applications, on CodePlex. The developers at Microsoft working on MVC
with lots of built-in automated testing and rapid develop- wrote code into internal source control, build, and test sys-
ment features. tems. Periodically, a version of ASP.NET MVC was published
to CodePlex for the public to explore. On the positive side, a
There was pressure for ASP.NET WebForms team to offer lot of external developers took MVC out for a spin and gave
something that could compete with Ruby on Rails. The fo- detailed feedback to Microsoft through CodePlex. On the
cus on modern Web development led to the Mix conference, negative, there wasn’t any way for those same developers to
which first ran in Las Vegas in March 2006. The subtitle of directly contribute code to the MVC project.
Mix06 was “the next Web now.” It was at the Mix06 con-
ference that Microsoft announced the creation of CodePlex But building MVC through CodePlex did make MVC better, if
as a so-called “source forge” for open-source projects. One perhaps delivering it a bit slower; the first Community Tech-
of the first successful projects created on CodePlex was the nology Preview (CTP) of MVC arrived in December of 2007, with

codemag.com When Open Source Came to Microsoft 23


version 1.0 coming in March 2009. At that time, Microsoft 2011 was the first time the public heard about Roslyn. Ros-
chose to license MVC with its own Microsoft Permissive License lyn was a new C# compiler written in C#. This was standard
(MS-PL). Microsoft eventually changed the meaning of MS-PL practice for most programming languages, almost a sign of
to the Microsoft Public License. Although quite a permissive li- maturity. You know your programming language is grown
cense, the open source community heavily criticized Microsoft up when you can build a compiler for your language in your
for creating separate licenses—they felt it unnecessary. Better language. The ability to create a compiler for C# in C# had
to use one of the existing licenses than make a new one. been possible for years, but the demands on the C# team
for new features kept pushing the compiler rewrite to the
During the development of MVC 1.0, there were significant con- back burner.
versations on CodePlex about the problems of DOM tree travers-
al. The Web browsers of 2008 didn’t make it easy to find HTML One of the things that brought Roslyn to the forefront was
elements on a Web page. Tooling for DOM tree traversal started Visual Studio. The Visual Studio editor analyzed your code
to be added to MVC, then proposed as a separate library. But as you wrote it, highlighting problems as it detected them.
the solution already existed in the form of the open source It was a powerful productivity feature, but to make the ana-
project called jQuery, created by Jon Resig back in 2006. It lyzer work, it required a separate C# language interpreter.
was community pressure on CodePlex that pushed Microsoft to As C# grew, writing every language feature twice got expen-
resist Not-Invented-Here and consider using jQuery with MVC. sive, and any inconsistencies between the Visual Studio C#
interpreter and the C# compiler were hugely confusing to
This push resulted in jQuery being included in the box for developers. Roslyn became the solution as a “compiler-as-
Visual Studio 2010—the first time any Microsoft product a-service” that could handle both roles. It also meant that
had included an open source library. And Microsoft didn’t the developers creating C# were coding in C#.
just add it in Visual Studio; they committed to making con-
tributions to the jQuery project. The MVC team and others Although the team created Roslyn using internal tools, they
needed features in jQuery to make their products work the did publish versions to CodePlex. It wasn’t an open source
way they wanted them to. The contribution process was a project, but that would change.
great educational moment for Microsoft’s developers, con-
tributing to a project they didn’t control but on which their Another significant change in the C# space was the departure
products depended. of Anders Heljsberg—not from Microsoft, but rather from the
C# team. Anders had been working on C# for more than ten
Although the inclusion of jQuery was a point of celebration years and was ready to do something new. His protégé, Mads
for fans of the open-source movement, it also created a lot of Torgersen, took the helm of C# while Anders went to explore
concern for more traditional Microsoft development custom- a new problem space and that space was JavaScript.
ers. At the time in 2010, it was common practice for enter-
prises to have a strict “no open-source” policy for all software Although Anders had focused mainly on creating statically
projects. Microsoft found itself in the remarkable position of typed languages like Delphi (which was Object Pascal) and
having to defend open source technology to its customers. C#, he did have a fascination with dynamic languages like
JavaScript. In his mind, the challenge was building sustain-
The tide, as the saying goes, was turning. able software with them. Once projects written in dynamic
languages get to a certain size, it becomes very challenging
The increasing popularity in open source libraries for .NET to maintain them.
led to installation and versioning problems. A developer
could end up using a dozen libraries from a dozen differ- In 2011, JavaScript was hot; the work happening in the vari-
ent sources, making building and maintaining an applica- ous browser teams, including Internet Explorer (Chakra),
tion very difficult. The solution to this problem is a package Chrome (V8), and FireFox (Nitro) around JavaScript had
manager, for example, the Ruby on Rails world has Ruby made the language faster, more robust and more capable.
Gems. Scott Guthrie tapped Phil Haack to lead the package NodeJS was gaining traction, letting JavaScript program-
manager project, which would eventually be named NuGet. mers write code on the server-side as well as in the browser.
NuGet shipped in October 2010. Anders was keen to make JavaScript even better. He as-
sembled a team.
In 2011, Damien Edwards and David Fowler, while working
in the ASP.NET team at Microsoft, started a side project they What came out of the team’s efforts was TypeScript, a strict
called SignalR. The SignalR library took advantage of Web syntactical superset of JavaScript with the ability to add
sockets in modern browsers to provide real-time functional- static typing to JavaScript. And it was open source from the
ity, while at the same time wrapping up the complexity of beginning, initially released on CodePlex in 2012.
solving the same problem for older browsers. They also built
SignalR as an open-source project on GitHub. The open source community loved TypeScript from the start.
Although there had always been critics of the various open
GitHub started back in 2008 as a source code repository source efforts that Microsoft had made, the principal com-
over top of the Git protocol initially developed by Linus plaint about TypeScript from the beginning is that it didn’t
Torvalds for the development of Linux. As a distributed support enough platforms or have enough declaration files
source code control solution, it worked well for open source for library support. But, being an open source project, the
projects with many contributors. GitHub was also great at community could fix those problems faster than Microsoft—
helping developers and project leaders communicate about and they did.
the projects they were working on—almost a kind of social
media space for software developers. By 2012, the ASP.NET Early on in TypeScript’s life, Palantir Technologies contribut-
team would have a repository on GitHub. ed a plug-in so that the users of the Eclipse IDE could code

24 When Open Source Came to Microsoft codemag.com


in TypeScript. And contributors to JavaScript libraries of all cross-platform and open source Microsoft. Although .NET
kinds created TypeScript declaration files. The open source may have come into the world back in 2000 as “a new plat-
community loved TypeScript and helped to make it better. form based on Internet standards,” it was also tooling to let
enterprise developers build applications for Windows. Deep
The embrace of TypeScript by the open source community in the core of .NET was Windows: the Common Language
had a massive effect on Microsoft as well. It became the Runtime was a thin wrapper over various Windows APIs.
talking point that Microsoft could be an open source com-
pany. A team formed to explore what a new .NET would look like.

The need to organize open source efforts at Microsoft led In February of 2014, Satya Nadella became CEO of Microsoft.
to the creation of MS OpenTech. MS OpenTech was created By April of 2014, he was keynoting at the Microsoft Build
as a wholly owned subsidiary of Microsoft focused on open- conference and announced that Windows would be free on
source development. The most visible part of its efforts was all devices that have a screen nine inches across or smaller.
making sure that important open source projects, like Redis And this is the same keynote mentioned at the beginning
and MemCacheD, for example, ran great on the Microsoft of this article where Anders Heljsberg published Roslyn to
stack. Microsoft developers moved to MS OpenTech to con- GitHub as a cross-platform, open source C# compiler-as-a-
tribute to these open-source projects. service. Version 1 of TypeScript also shipped at Build.

MS OpenTech was also a place where Microsoft developers The .NET Foundation was launched at Build 2014 as well;
worked to make existing closed-source software into open similar to the Apache Foundation, it serves as a manage-
source projects. Developers of the closed-source project ment point for .NET open source projects. At Build, numer-
would work with Microsoft patent lawyers to review all ous .NET projects were committed to the foundation, in-
the patents filed for the project. Making that project open cluding ASP.NET MVC, Web API, and Web Pages, some of the
source meant invalidating the patents, so a lot of time was original open source projects at Microsoft.
spent sorting good from bad, valuable from non-valuable.
By the fall of 2014, Microsoft announced .NET Core: a com-
An example of this transformation was Entity Framework, plete rewrite of .NET as a cross-platform, open source devel-
first released as a closed-source product with .NET Frame- opment framework. It will take 18 months for version 1.0 to
work 3.5 SP1 back in 2008. In 2012, Scott Guthrie announced
that Entity Framework Version 6.0 would be open-source,
and it released under the Apache 2.0 license in 2013. This
release came alongside the ASPNetWebStack that included
MVC, WebAPI, and Web Pages, an entirely open source set ADVERTISERS INDEX
of tools under the Apache 2.0 license with source code on
GitHub and distributed via NuGet.
Advertisers Index
Microsoft had exercised its open source muscles in a variety of
ways, starting projects from scratch, like MVC, as open source Amazon Web Services (AWS)
projects. They were distributing third-party open source librar- go.aws/codemag 76
ies like jQuery as part of a product offering and making con-
CData
tributions to existing open source projects. And perhaps most
www.cdata.com 11
remarkably, taking closed-source projects like Entity Framework
and turning them into open source libraries. It was a huge CODE Consulting
change at Microsoft, but bigger changes were coming: The www.codemag.com/consulting 65
Cloud. CODE Legacy
www.codemag.com/legacy 53
Back at the Microsoft Professional Developers Conference in
Commercial UAV Expo Americas
2008, chief architect Ray Ozzie announced Windows Azure
www.expouav.com 45
as Microsoft’s cloud offering. The first public offerings came
in February of 2010 with support for SQL Azure, PHP, Java, DevIntersection Advertising Sales:
and .NET. The cloud was new then, Amazon had gotten to www.devintersection.com 2 Tammy Ferguson
832-717-4445 ext 26
market a couple of years before, but existing Microsoft cus- dtSearch [email protected]
tomers were not in a hurry to move. At that time, Steve www.dtsearch.com 19
Ballmer said “For the cloud, we’re all in.”
JetBrains
www.jetbrains.com 29
By 2013, the struggles around Windows 8 and the shifts in
the marketplace had made it apparent that the future of LEAD Technologies
Microsoft lay in the cloud. For decades, Microsoft had fo- www.leadtools.com 5
cused solely on building products and tools that sold cop- Nevron Software, LLC
ies of Windows. Now Microsoft was making Azure its pri- www.nevron.com 7
mary focus. As part of the refocusing of Microsoft, Steve
PLURALSIGHT
Ballmer announced his retirement: Thirteen years as CEO
www.pluralsight.com 75 This listing is provided as a courtesy
was enough, and the search was on for his replacement. to our readers and advertisers.
The Tech Academy The publisher assumes no responsibi-
While that search was taking place, there were serious con- www.learncodinganywhere.com 37 lity for errors or omissions.
versations about what development tools made sense in a

codemag.com When Open Source Came to Microsoft 25


ship. Still, Microsoft succeeded in the goal to maintain as The idea that you could write mobile and tablet applica-
much compatibility with the original .NET Framework as pos- tions for iOS and Android using C# was hugely compelling
sible while creating a new runtime environment that sup- for developers in the .NET space, and they flocked to the
ported executing on Windows, OS/X, and Linux. toolset. Bringing Xamarin into Microsoft not only expanded
the team working on mobile and tablet development, but it
Not all of the .NET Framework came to Core: System.Web, also opened the doors for new opportunities for the Xama-
the namespace where ASP.NET WebForms lives, wasn’t re- rin folks, including Nat and Miguel.
written. Profoundly intertwined with IIS, the rewrite of Sys-
tem.Web would have taken years and provided questionable The business of open source projects changed the way that
benefit for the effort. Later, other libraries like WCF ended companies compete. An example of this new form of com-
up in a similar situation. Developing a framework for twenty petition is Kubernetes. By 2016, building applications using
years means that some parts do get old. Docker containers are becoming very popular, and there is
an array of container orchestration tools available. As cloud
In many ways, Microsoft became an open source company vendors, Amazon, Microsoft, and Google all have container or-
in 2014. On GitHub, the github.com/microsoft repository chestration offerings: Amazon has Elastic Cloud, Microsoft of-
opened, and a vast array of projects from numerous Micro- fers Azure Service Fabric, and Google uses Kubernetes. Of the
soft teams developed there. The new open source develop- three approaches, only Kubernetes is an open source project.
ment style is very transparent: Some teams stream their
team meetings, publish the notes of those meetings in the There are also third-party container orchestration engines
GitHub repositories of their projects, and interact continu- available like DC/OS and Meso. Most of the time, software
ously with internal and external developers about ideas and developers like the idea of a choice of products, but there’s
issues around the projects. also a point where it represents a fragmented market. Se-
lecting the offerings from Amazon, Microsoft, or Google
There are no secrets: Anything you want to know about an meant locking into a single cloud vendor. Going with other
upcoming version of an open source product is there for offerings meant more maintenance and effort dealing with
you to read. Better still, you can contribute to the project potential incompatibilities.
with ideas and code. The cycle time from concept to code
gets short: there is delight when you can discuss an idea Kubernetes may not have been the most sophisticated of
for a feature in a Microsoft project with the team via GitHub the orchestration engines at the time but being an open
and then see it show up as a preview build—sometimes source project meant that it wasn’t difficult for Microsoft to
in days! implement a Kubernetes service on Azure. Hiring Brendan
Burns, one of the founders of the Kubernetes project, didn’t
The Build conference in 2015 saw the release of Windows hurt either. Not long after Microsoft’s move, Amazon, too,
10: Microsoft announced it as the “last version of Windows.” had an implementation of Kubernetes available as a service.
Build 2015 also saw the release of Visual Studio Code. Based
on the Electron framework developed at GitHub, VSCode is By 2017, folks from Amazon, Microsoft, Google, and more
a cross-platform source control editor that has many of the were contributing to the project, and Kubernetes was more
favorite features of the Windows-only Visual Studio. But un- of a consensus around orchestration than a competition. All
like Visual Studio, VSCode is not an all-encompassing IDE. of the energy poured into Kubernetes made its ecosystem
Instead, it’s a brilliant code editor with excellent extensibil- snowball, creating a proliferation of tools for monitoring,
ity. Programmers flock to it, and not just .NET developers—it deploying, and securing containers—and a growing pool of
became a favored tool for all sorts of Web developers, Py- people skilled in working with Kubernetes.
thon developers, and more.
Could you consider Kubernetes a monopoly? There are still
2015 also saw the end of MS OpenTech, with all the tech- other container orchestration engines, but Kubernetes is
nology and staff moved into Microsoft. Why do you need dominating. And no one company has control over Kuber-
a separate subsidiary for open source development when netes; as an open-source project, anyone could fork the
you’re an open-source company? source and build a version of it. But there’s an advantage
to working together in the form of that growing ecosystem.
The growth of Azure brought Microsoft’s relationship with That’s one of the benefits of the open source world: the code
Linux full circle. In the Azure marketplace, where users of isn’t the asset, the service is. And the service is made more
Azure can select products and services, there are several valuable with collaboration.
flavors of Linux: SUSE, Ubuntu, and Debian, among others.
In November 2015, Microsoft completed an agreement with This collaboration and consolidation mantra spreads fur-
Red Hat to bring Red Hat Enterprise Linux to the market- ther when Microsoft commits to shutting down CodePlex,
place as well, providing direct support in Azure for all the moving almost all the projects there over to GitHub. To this
major enterprise Linux flavors. So much for Ballmer’s decla- day, CodePlex is still running, but as an archive of the old
ration of open-source being a cancer. projects. Should you visit those projects, you’ll see that, for
the most part, they say the same thing: Moved to GitHub!
Another circle was completed in 2016 when Microsoft ac-
quired Xamarin. Miguel de Icaza had embraced .NET in its Also in 2017, .NET Core 1.1 and 2.0 were shipped. Along with
earliest days, creating the Mono project as an open source the expanding surface area of the cross-platform and open
implementation of .NET for Linux, the first version shipping source version of .NET came a new maintenance strategy.
in 2004. By 2011, Miguel, with his friend Nat Friedman, For years, Microsoft built software with ten-year support
formed Xamarin to focus on building implementations of agreements; that is, once development stops on a technol-
Mono for iOS and Android. ogy, Microsoft guarantees maintenance support, like secu-

26 When Open Source Came to Microsoft codemag.com


rity patches, for ten years. This model made sense when new In 2019, .NET Core 3 shipped two versions as well, bringing
versions of products shipped every 18-24 months. But at the a new idea: platform-specific libraries. New versions of Win-
modern cadence of shipping software several times a year, Forms and WPF were released, available only for Windows
old versions need to be dropped. but made available as an SDK for .NET Core 3. By separating
off the Windows-specific parts, .NET Core kept being cross-
For .NET Core, Microsoft alternates between a short-term platform but added new capabilities to help client-side .NET
version supported for three months after its final patch and developers building apps for Windows. The new version of
a long-term support version supported for three years. The WinForms in the SDK supported high-DPI screens, a long
goal was to encourage developers to keep their applications sought after feature for WinForms developers.
evergreen, that is, to be able to build them with the new
bits and have confidence that their software would keep It’s 2020, and a new decade has arrived, and Microsoft is an
working. Static software is dead software. open-source company. Not completely, but certainly where
it matters.
Microsoft’s commitment to GitHub hit its crescendo in 2018
when it acquired the company. GitHub had never been a There’s a push to unify the .NET Frameworks. Developers
particularly profitable company on its own, but it was a vi- building applications in the older Windows-only version of
tal part of the developer ecosystem. When word leaked that .NET are concerned that they’ll be left behind at some point.
Microsoft was making the acquisition, there were some pro- Microsoft has announced that the next version of .NET is
tests on popular open source forums: That was tamped down called .NET 5. The underpinnings of .NET 5 are .NET Core,
by other members of those communities saying, “Have you preserving the cross-platform and open source aspects. On
looked at Microsoft lately? This is the best thing that could top of that, there’ll be the various Windows-specific librar-
happen for all of us!” ies that developers need. Not all of them: Some tools, like
WCF, are too costly to make the migration.
Nat Friedman, the former CEO of Xamarin, now became the
CEO of GitHub, a wholly owned subsidiary of Microsoft. For The open source community can step up to help these po-
the most part, the only change that Microsoft made at tentially stranded libraries. Microsoft has released their
GitHub was to make unlimited private repositories free. De- prototypes of an open source version of WCF on GitHub—far
velopment at GitHub accelerated as more people were able from feature-complete, it’s a start toward making an open-
to work on the products. source version of WCF. But there’s also a healthy conversa-
tion going on for moving to more modern communication
Effectively, the acquisition of GitHub let Microsoft protect approaches, like gRPC.
a vital part of the open-source world, a part that Microsoft
itself depended upon heavily. That’s the nature of open source coming to Microsoft: Choic-
es about how to move forward with any technology. And the
Microsoft’s moves around Linux continued in 2018 as well, community is better for it.
when it joined the Open Invention Network (OIN), an open
source patent group designed to protect Linux from patent  Richard Campbell
lawsuits. Microsoft’s library of 60,000 patents became open 
source and available to OIN members. The release wasn’t of
every patent that Microsoft held; lots of Windows and Azure
related patents remain held close.

Protecting Linux made a lot of sense for Microsoft; after all,


they were building their own flavor of Linux for the Azure
Sphere product. Azure Sphere is part of Microsoft’s Internet
of Things offering, with a focus on building secure devices.
The hardware, firmware, and Linux operating system main-
tain secure encryption and security, one of the key problems
in the IoT space.

And the moves to cooperate around key technologies con-


tinued in 2019 when Microsoft rebuilt its Edge browser us-
ing the open-source Chromium rendering engine—the very
same rendering engine that Google’s Chrome browser uses.
The switch to Chromium didn’t come out of the blue; Micro-
soft and Google had collaborated for years over Web stan-
dards and often demonstrated new feature ideas in their
respective browsers.

But the Edge browser never gained a large following.


Chrome had been the dominant browser for years. By using
the Chromium rendering engine, Microsoft could guarantee
that this new Edge browser would render pages the same as
Chrome, and that the competition in browsers would focus
more on the other aspects of Web browsing, not just how
the Web page looked on the screen.

codemag.com When Open Source Came to Microsoft 27


ONLINE QUICK ID 2009051

Stages of Data: A Playbook


for Analytic Reporting Using
COVID-19 Data, Part 2
In my last installment of “Stages of Data” (CODE Magazine, July/August 2020), I presented a public dashboard solution using
Microsoft SQL Server and Microsoft Power BI to report on COVID-19 data by county across the United States. The solution
covered aspects of dashboard/analytic reporting that will be common across many applications. In this issue, I’ll continue

with the project. We’ll look at some updated COVID-19 num- In late April and early May, New Jersey was reporting nearly
bers, show some new metrics, and some new visualizations. 1,800 deaths per week. Then that number declined a few hun-
dred each week, to a low of 268 the week of 6/20. Then the
following week of 6/27, the death count spiked to nearly 2,100
Stages of Data, Literally and Figuratively for that week, even though case counts had been dropping
I “retired” my Baker’s Dozen Productivity series that ran consistently for two months. As it turns out, in the week of
from 2004 through 2017 and built a new series brand with 6/27, health officials went back and reviewed medical data and
“Stages of Data.” One of the goals was emphasizing the determined that there were 1,800 “probable” COVID deaths go-
different phases of an analytic solution: identifying source ing back a few months, thus the weekly spike. Obviously, if the
Kevin S. Goff data, exploring extract strategies, organizing data into dif- major data sources are counting it, we need to count it: but we
kgoff@kevinsgoff. net ferent model layers, reporting on and presenting data, etc. also need to be able to explain any spikes of that magnitude.
www. KevinSGoff. net
@StagesOfData In addition, there’s also another dimension to analytic so- A similar situation occurred in New York at the end of June.
lutions: the points in time when certain analytic perspec- After weeks of declining death counts, New York experi-
Kevin S. Goff is Database
architect/developer/speaker/ tives take on a stronger meaning. For instance, you need a enced a spike of about 700 deaths that occurred over sev-
author, and has been period of time to even begin thinking about trend analysis. eral weeks and there was a delay in the reporting. This kind
writing for CODE Magazine Along with that, you need to identify blocks of time that of situation obviously skews any trend analysis. This is a fact
since 2004. He was a mem- hold significance. In the case of COVID-19 reporting, many of life, but we need to be prepared to explain it.
ber of the Microsoft MVP who are following this data have eschewed (rightly so) daily
program from 2005 through analysis. Over time, even weekly analysis can be problem- The moral of the story: Know Thy Data. The final response to
2019, when he spoke atic. Months or even years can go by before anyone finds the a wonky number should never be, “That’s what the data feed
frequently community temporal “sweet spot” in this kind of data, but I’m finding said.” If the number is bizarre, go find out why. Know Thy Data.
events in the Mid-Atlantic that taking a picture of numbers over the last two weeks and
region and also spoke comparing to the prior two weeks gives a better picture of
regularly for the VS Live/ how a particular geography is progressing/regressing dur-
Live 360 Conference brand ing this pandemic. Know Thy Data. The final response
from 2012 through 2015.
There’s also a corollary to stages of data: “Know Thy Data.”
to a wonky number should never be,
I’ve known some very talented and sharp programmers, but “That’s what the data feed said.” If the
unfortunately, a few of them didn’t recognize the value number is bizarre, go find out why.
of understanding the data. The old cliché is true: Context
matters. I recall years ago working with a Web developer
who built a new summary Web page that incorrectly showed
sales in the billions of dollars instead of low millions. There
were multiple reasons for the difference, but the bigger is- What’s on the Agenda for Today:
sue was that the developer knew the Web page was show- Here are the topics for today:
ing data in the billions and didn’t realize that it was or-
ders of magnitude off. Ideally, the developer should have • A new state profile page with ranking formulas
realized it. Unfortunately, the business side wondered, • New trend-based measures
“Don’t our developers know our business and sales num- • Defining custom filter groups
bers? Didn’t they realize this was way off?” They had a • A new visualization page to show death trends
point. • A recap of the Analytic playbook
• A look ahead to the next installment in this series
Similarly, when you work with data, regardless of the physi-
cal stage, you need to know the context of the data and be
able to spot any MAJOR outliers. I’m not saying you should
New State Profile Page with
be able to detect if sales went up 10% from month to month: Some Rankings
I’m talking about face-validity issues. Two of them came up In keeping with the theme of content reward, I wanted to
in the last two weeks regarding COVID data. build a summary page to show all the major COVID statistics

28 Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 codemag.com
codemag.com Title article 29
Figure 1: State Profile Page with new metrics/rankings and an overall composite ranking

Figure 2: Weekly Cases and Deaths for Massachusetts

for a state. I want this to be available either as a drill-down In Figure 1, you see in the upper right that Massachusetts
or even as a report tooltip. Additionally, I wanted to rank has the third highest total death count (8,310 deaths), and
the state from high to low compared with other states for the fourth highest death count per 100K residents (120.57).
each statistic, and then come up with a composite rank- Although those two ranking numbers are nearly the same
ing. This last part is a challenge: how can you rank states (third and fourth), they do illustrate that population and
according to overall impact? Obviously, you might initial- population density can factor into some of the rankings for
ly rank based on death numbers: but then again, smaller each metric. The colored map by county is based on what-
states have some very bad numbers for population density. ever metric I chose to display—in this case, deaths, with
Middlesex county in red to show the county with the highest
Figure 1 shows a Power BI report page for a state profile. number of total deaths.
I’ve chosen Massachusetts.
Now, let’s take a look at the overall ranking of 19, based on the
I ranked states with respect to each other, across all the met- six metrics with an asterisk beside the individual rankings. For
rics in Figure 1. I’m also going to show an overall composite the first three metrics, the state is doing “better” than most
rank based on six metrics that I’ll reference below. Gener- states. For the last three, the reverse. Going from left to right:
ally speaking, the lower the rank, the worse the situation for
that metric. So the state with the highest overall cumulative • 37th in the country in terms of deaths in the last two weeks
deaths (New York) will be ranked #1 for total death count, as a percent of two weeks prior (trending down 41.77%)
and the state with the lowest overall cumulative deaths (Wyo- • 44th in the country in cases in the last two weeks as
ming) will be ranked #48 for the same metric. a percent of two weeks prior (trending down 3.05%)

30 Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 codemag.com
• 45th in the country in terms of Case Threshold Per- This is simply one way to assess an overall impact score for
formance Index (no more than 50 new cases last two a state, relative to other states. Massachusetts is the “19th
weeks, per 100K residents) worse” in terms of overall impact. The six metrics above
• 5th in the country in terms of Deaths per Case, at 7.46% use a combination of current trends (where the state is do-
• 4th in the country in terms of Deaths Per 100K resi- ing well compared with most states) and overall numbers.
dents (120.57) The state has one of the higher death tolls in the U.S. but,
• 3rd in the country in terms of Deaths per Square Mile (1.06) as you can see from Figure 2, they have been trending
better.
If you add up all the ranking points, that sums to 138. Let’s
take a step back again. Ideally, a state (we’ll call it State A) So how did I perform the rankings? Because I’m using Power
with no cases and no deaths would be ranked #48 in each of BI for the visualizations, and ranking might be contingent
the 6 COVID metrics I’m using for a composite ranking, for on run-time filters, it’s best to use the RANK functions in
a grand total of 288 ranking points. At the other extreme, Power BI DAX to rank dynamically at runtime.
a state (State B) that was the absolute worst in all metrics
would be ranked #1 in each of the six COVID metrics, for a Let’s take a look at the third metric of the six, where we
grand total of six ranking points. don’t want more than 50 new cases per 100K residents in
the last two weeks. This metric was used in Pennsylvania
State A would be ranked #48 out of 48 overall, as 288 rank- as a guide for re-opening counties, and I’ve read that other
ing points is the maximum. That means, according to this states have followed suit (with some variations on the base
metric, that State A has been “least impacted” by COVID. threshold of 50). Remember that I stored population at the
Conversely, state B would be #1 out of 48, as six ranking county level in the table StateMasterPopulationStage, so
points is the lowest possible (again, lower is worse). There- you first need to establish the population for the state, and
fore, State B would be “most impacted.” come up with the goal/threshold:

Know Thy Data


I learned a hard lesson at
the age of 25. I won an
award for a software system I
developed in FoxPro in 1990
and presented it at a national
conference. Afterward,
someone asked me some
questions about the context
of the data, and said that my
answer (basically, “this is what
we get from the mainframe”)
wasn’t acceptable. He
criticized me privately, but
because it was someone
I highly respected, I admit
it was hard to take.
But the person was right.
Know thy data.

Figure 3: State Profile Page for Pennsylvania

codemag.com Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 31
CasesLast14DaysPer100KPopulationGoal = That score of .86 for the metric (cases over the last two
(sum(statemasterpopulationstage[population]) weeks versus a goal of no more than 50 new cases per 100K
/ 100000) * 50 residents) is one of the better ones in the country; on this
specific metric, they are ranked #45 (again, lower rankings
As Massachusetts has 6,892,503 residents, that means a goal are a reflection of negative metrics). Here is the Power BI
would be no more than 3,446 new cases in the last two weeks. DAX formulate for determining how that Index ranks across
Their case count in the last two weeks was 2,953, which all states:
means an overall index of .86. (As a general statement, an
index of 1 or lower is considered “in the right direction”). StateCaseThresholdPerformanceRank =
rankx(all
The calculation of the .86 for the Index is based on this DAX ('statemasterpopulationstage'[statename]),
formula: calculate( [CaseThresholdIndex]))

CaseThresholdIndex = Overall, I wanted to come up with a composite ranking that


sum(Covid19Last14Days[caseslast14Days]) covered the total impact of this pandemic along with recent
/ [CasesLast14DaysPer100KPopulationGoal] trends. I’m sure you can get five analysts together and come
up with at least five different ways to define composite rank-
ings and overall performance. This is just a starting point.

Obviously, I’m not going to show a dozen different states


here, but let’s look at the profile stage for one more state,
my home state of Pennsylvania (Figure 3).

From Figure 3, the overall composite ranking for PA is 11.


The death rate per case is very high, at 7%. Much of that is
attributed to the high number of cases and deaths in nurs-
ing homes in the state. The Death Rate per capita in PA
is very high, again due to PA being #6 overall in deaths.
The state situation had been improving significantly from
mid-May to late-June, although one metric of concern is the
weekly rise in cases for each of the last four weeks.

One interesting way to sanity-check this performance index is


the “blind comparison test.” Put the statistics for two states
side-by-side, along with the rankings, but don’t disclose the
state names themselves. The results might surprise you, or it
might help to uncover a flaw in the ranking logic.

Some New Trend-Based Measures


Note that back in Figure 1, one of the six metrics I used
to create a composite ranking is a new one: 14-day death
trend. Basically, if a state had 100 deaths in the last 14
days, and 80 deaths in the 14 days prior to that, that would
be a 25% increase in deaths.

Figure 4 revisits an interface I wrote about in Part 1 of this


article series: Showing states from high to low based on a
Figure 4: Show States ordered by any of our measures/calculations chosen metric.

Figure 5: Weekly numbers for the top two counties in Nevada (based on deaths)

32 Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 codemag.com
Here is the DAX formula that determines the dynamic mea- last two weeks, you can see a huge death spike compared to
sure, based on the user selection. two weeks prior (Figure 5).

DynamicMeasure = The DAX formula for this is a bit tricky, as you need to take
switch( values ( ShowMeasure[OptionName]), into account either division by zero, or instances where the
"Show Cases", sum(CovidHistory[Cases]), numerator is zero.
"Show Deaths", sum(CovidHistory[deaths]) ,
etc.) WeeklyDeathIncrease = if(
sum(covidweeklycounts[DthsL2Wks]) > 0 &&
From Figure 4, you can see that Nevada has the highest sum(covidweeklycounts[Dths2WPriL2Wks]) <= 0,
death rate increase. When you look at the counties on the 1,
right side of the dashboard page and sort on deaths in the if( sum(covidweeklycounts[DthsL2Wks]) <= 0 &&

Figure 6: A significant spike in weekly numbers for the top counties in South Carolina

Figure 7: Filtering based on counties in the NYC and Northern NJ area

codemag.com Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 33
Figure 8: Filtering based on the top five states (based on cases in the last two weeks)

death numbers the last few weeks. If we were to change the


two-week trend metric to a four-week trend, South Caroli-
na’s overall ranking would be even higher (worse) than #9.

The Overall Page, with Ability


to Create Custom Groups
Over time, I’ve found myself often having to refilter on spe-
cific conditions, such as the set of counties around NYC and
Northern New Jersey, or the top five states based on current
Figure 9: The rows in the CustomGeography table for cases. In my life, I’ve tended to do the same mundane task
the NYC/NJ hotspot far too many times before I stopped and thought of a more
efficient way. After about the umpteenth time of select-
ing nine counties or five states again and again and again,
sum(covidweeklycounts[Dths2WPriL2Wks]) <= I decided to build some groupings.
0,
0, Although I don’t have an interface to allow users to create
( sum(covidweeklycounts[DthsL2Wks]) – their own groupings (not yet, anyway!), I do have an ability
sum(covidweeklycounts[Dths2WPriL2Wks]) ) / to define some conditions in the database (some of which
sum(covidweeklycounts[Dths2WPriL2Wks]))) will get executed during the most recent data load).

In this situation, Nevada is at the lower end of the coun- Figure 7 shows the overall summary page for the nine coun-
try in state population, although that doesn’t diminish the ties in the NYC/Northern NJ area that represent the huge
significance of the increase. You could certainly adjust that hotspot of deaths.
metric from “deaths in the last two weeks” to “deaths per
capita in the last two weeks”. Figure 8 is the overall summary page for the top five states.

Before we move on, one other note: Back in Figure 4, you Okay, so how did I create these custom groupings? First,
saw Florida and Texas near the top of the list, consistent you create a new table called CustomGeography. You can
with the news stories in the last few weeks about the huge populate it with county FIPS code values.
spikes in those states. There’s another state that currently
has very concerning numbers: South Carolina. In Figure 6, select * from CustomGeography
I’ve selected South Carolina to view the top counties. where AttributeName =
'NYC and North Jersey hotspot'
South Carolina currently ranks #9 in my overall composite and AttributeValue =
ranking, driven mainly by their alarming rise in case and 'NYC and North Jersey'

34 Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 codemag.com
In the case of the nine counties in the NYC/NJ area, I popu- A New Visualization to Show States
lated them manually, as shown in Figure 9.
with Growing Death Rates
Of course, I might also want to filter on “all others, so I You can take your metric for deaths the last two weeks as
need to pull all the FIPS Code values for the ones that don’t a percentage of deaths the prior two weeks and show it in
meet that criteria, and insert them into the CustomGeogra- a visual. In Figure 10, I’m showing the states colored by
phy table with an Attribute Value of “Not NYC and Northern the death trend for the last two weeks: States in red have
Jersey.” the highest trends, light yellow and light green have either
slightly above or below zero trends respectively, and green
states have trends well below zero.

After about the umpteenth time Here’s one thing you can see with the scatter chart in Fig-
of selecting nine counties or five ure 10: New York and New Jersey are wide apart from the 0
X-Axis line. As I said in the beginning of this article, New
states again and again and again, York had a delayed death count dump in the last two weeks,
I decided to build some groupings. resulting in a very high increase. New Jersey had a delayed
death count dump three weeks ago, meaning the trend is
artificially low for the current two weeks.

select fipscode from statemasterpopulationstage You might want to exclude them for the purposes of analyz-
where fipscode not in (select fipscode from CustomGeography ing the other states. Power BI runtime allows you to right-
where AttributeName = click on a plotted point (as I’ve done in Figure 10) and ex-
'NYC and North Jersey hotspot' clude the states, one at a time.
and AttributeValue =
'NYC and North Jersey' In Figure 11, you can see an updated scatter chart that
and FIPSCode is not null) shows the overall death count on the Y axis and the death
trend for the last two weeks on the X axis. Now that New
This first group filter definition is a “static” set of values. If you York and Jersey are excluded, the scatter chart rescales. Two-Week Trends or
want to set a predefined lookup group/filter based on the top Four-Week Trends?
five cases, you can define one called “Top Five States,” and pop- California, Florida, and Texas are three states with the high-
ulate the county codes for those five states with a SQL query: est death counts that also have a high death trend over the The composite ranking
last two weeks. (You can also see South Carolina and Nevada concept in this article is a
select fipscode from statemasterpopulationstage all the way out to the far right on the Death Trend Y axis, discussion-starter. Someone
join ( although their populations are lower). suggested I use a four-week
select top 5 stm.statename,
trend instead of a two-week
trend, because even two
sum(cases) as TotCases You can click on one of the states (either a plotted point in
weeks doesn’t take into
from covid19DailyCounts cdc the scatter chart or the state in the map) to see the coun-
account a full virology cycle
join statemasterpopulationstage stm ty breakdown at the bottom of the page. I’ll click Florida,
of incubation and delays in
on cdc.fipscode = stm.FIPSCode which gives me the county breakdown in Figure 12. Here reporting data. It wouldn’t be
where casedate between you can see that Miami Dade County’s deaths have gone up the proverbial rocket science
dateadd(d,-14, 48% in the last two weeks, and Broward county’s deaths to either change this to use
(select max(casedate) from datelist) ) have nearly doubled (an increase of 192%) a four-week cycle, or even
and (select max(casedate) from datelist) provide the option to toggle
group by stm.statename between the two of them.
order by TotCases desc ) t The Playbook, Revisited I’ve already shown how you
on statemasterpopulationstage.statename = In Part 1 of this article series, I talked about common can implement dynamic
t.statename themes in analytic applications, such as shared filters, con- measures in Power BI.

Figure 10: Scatter chart and filled map of states based on two-week Death Trends

codemag.com Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 35
Figure 11: Same as Figure 10, but without New York/New Jersey (notice scatter chart rescales)

Figure 12: The same metric but for the counties in Florida

Sanity Check and tent reward, dynamic measures, trend-based metrics that In the Next Regular CODE Article
Regression Testing truly reflect changes in reality, and combining metrics to- When I set out to write on this topic, I wasn’t sure if I’d
gether to form a picture. I also talked about validation: in stretch it out over one or two issues. Well, I can say for a
I’ve had several nicknames
this case, whether it’s looking at any huge changes in num- certainty that there will be a third, and here are the topics:
in my career, some funny
bers, or verifying against other news sources. You can do
and some I can’t repeat. One
of my co-workers calls me
some of that with automated scripts/procedures, but some • A complete walkthrough of the ETL steps. I had
“Data,” after the Brent Spiner requires good old-fashioned eyeballing and reading. planned to cover the ETL, but the truth is that I wound
character in the Star Trek up changing my underlying data model in the last
Generations series. I look at There are also pages from the playbook that fall under the month, to more effectively handle some of the trend-
data as often now as I studied “process” area. I’ve been developing this project on my own ing. So my ugly little secret right now is that although
baseball stats as a kid. When I time, outside of my normal work schedule. (Does anyone in IT the dashboard application reflects the data, some of
see a number that looks odd, actually have a normal work schedule?) I’ve had some GoTo- the underlying ETL is nothing I’d want to present. I’ll
I track down why. Yes, I’ve Meeting sessions with some friends and colleagues, and either be cleaning that up and that will be the primary focus
built automated tools that I’ll demo what I’ve done, or I’ll give them the link and let them of the next article.
look for significant changes navigate while I watch. Just a 15-minute session can raise • I’m in the process of moving my back-end data-
to data and they’ve saved me ideas or opportunities for improvement. To be sure, this can base to Azure and will talk about some of the steps
time. Still, there’s no substitute backfire if you do it too often, but that regular feedback loop involved.
for walking back through can be very helpful in maintaining continuous improvement.
the analytic paths of your Stay Safe and Play It Smart
application to make sure that As a big fan of the musical rock group “Rush,” I’m reminded
either you haven’t broken Final Thoughts: of some lyrics from a 1991 track called “Neurotica” that goes
anything with a change and/ My primary website (www.stagesofdata.net) has an entire like this: “Fortune is random, fate shoots from the hip, I
or that some data anomaly section devoted to this COVID-19 reporting project. This is know you get crazy, try not to lose your grip.” We need to be
now exists for which you
my documentation center for the project. mindful of our actions every day. We need to absolutely re-
haven’t identified a pattern.
spect this virus, because this virus has absolutely no respect
There needs to be a healthy
mix of automated tests and
The public URL for the dashboard is https://bit.ly/2A6lk3a. whatsoever for us. This isn’t just a virus that causes pneu-
good old-fashioned eye- The link is also available on my primary website. I update monia (which itself has led to pandemics): even healthy
balling. the site weekly on Sunday evenings, after pulling down the people have suffered severe vascular damage. Still, we need
data from Saturday and viewing the contents. I also post to keep our grip on this cold reality. The vast majority of
notes on any new weekly findings. people in the world know this, but still, EVERYONE needs be
safe and play it smart, at all times.
Finally, my primary website also contains some links to
videos I’ve posted on YouTube that cover some of the main  Kevin S. Goff
features of this dashboard. 

36 Stages of Data: A Playbook for Analytic Reporting Using COVID-19 Data, Part 2 codemag.com
ADVERTORIAL

Break into the Tech Industry:


Enroll in an Online Tech Academy
Coding Boot Camp today!
The Tech Academy is a technology school that delivers online training, with students
all over the world. In fact, they just received 2020’s Best Online Coding Bootcamp Award.

Learn Coding Anywhere The Tech Industry is Still Hiring What Sets the Tech Academy Apart
The Tech Academy programs require no technical Despite recent global events, we are placing a high There are other online schools, so what makes
background or coding experience. Our classes are number of graduates in remote, technical positions. The Tech Academy special?
deisgned for absolute beginners. We specialize in While many companies have been struggling,
coding boot camps that train students in a wide the tech industry has been impacted less than • No tech background or coding experience
range of technology subjects, including: others. is required
• Online and in-person training
• Website development During these uncertain times, one thing is for sure: • Open enrollment—start anytime
• Computer programming the need for technology isn’t going anywhere – • Flexible scheduling options
• Design in fact, it’s increasing. • Self-paced program
• Data science • Outstanding job placement assistance
• And more... Prepare for your future today by completing a • Multiple financing options
Tech Academy Boot Camp from the comfort an
These programs can be sompleted in as little safety of your own home.
8 weeks and prepare graduates for working in
the tech industry.

Start Your Career


Path Today
The Tech Academy is enrolling now!

Contact us at:
[email protected]
Or call: (503) 206-6915

37
codemag.com
FIND OUT MORE AT LEARNCODINGANYWHERE.COM Title article
ONLINE QUICK ID 2009061

En Route to Full-Stack .NET


Development with Client-Side Blazor
At last, we’re able to enter the race for the Grand Prix of Web Development using a vehicle powered by C#, and by the looks
of it, our vehicle runs on rocket fuel. Of course, I’m talking about Microsoft Blazor. As we’ve followed the path to modern Web
development, we’ve always been chasing the taillights of frameworks powered by JavaScript, most prominently Angular, React,

and Vue. On May 19, 2020, Blazor WebAssembly was finally articles that outline exactly that (“Re-Assembling the Web
released, and full-stack Web development with .NET received with Web Assembly and Blazor” by Rick Strahl, issue Septem-
a spot on the track. ber/October 2018, https://codemag.com/Article/1809061/
Re-Assembling-the-Web-with-Web-Assembly-and-Blazor;
Microsoft introduced server-side Blazor back in September and “A New Era of Productivity with Blazor” by Ed Charbon-
2019, which allowed the community to delve into Web de- neau, CODE Focus Issue November 2019, https://codemag.
velopment using C# and .NET. Server-side Blazor executes com/Article/1911052/A-New-Era-of-Productivity-with-
all logic on the server and communicates with the browser Blazor). Although some of the code snippets/syntax and
through a constant SignalR connection. The programming namespaces might have changed with the newer releases
Otto Dobretsberger, syntax and the project structure is identical to client-side of .NET Core, the fundamental ideas and architectural de-
PhD Blazor projects, but we can still point out significant differ- signs around Blazor have remained the same. Therefore, I
[email protected] ences, as well as pros and cons between the two approaches. still consider these articles a valid resource for information.
What I want to highlight in this article is something I’m still
Otto is a Senior Software not able to find a lot of information or examples on: Real
Developer with EPS Software. The Qualifying: Client-side world applications that go beyond the tutorials and typical
He earned a Bachelors
Degree in Biomedical
vs. Server-side Blazor starter apps people develop to play around with and write
Until the release of WebAssembly, in-browser logic had to be about. In light of the current global situation revolving
Informatics in Austria, and a
executed almost exclusively as JavaScript. Regardless of the around the COVID-19 pandemic, I figured that I can write an
Masters and PhD in Computer
Science from the University setup of your back-end, a Web application without front-end application that enables the tracking of COVID-19 statistics,
of Houston. His background JavaScript was unthinkable. Client-side Blazor challenges and that allows me to compare different countries side by
is in heavy computational JavaScript’s dominance in the browser and adds C# to the side, including graphs. I’ll show how to set up the project
genetic and genomic data pool of choices. Because it’s pre-compiled into WebAssem- and make service calls to REST APIs for data acquisition. I’ll
analysis using C and C++. bly before being downloaded into the browser, it’s destined also demonstrate that a client-side Blazor application can
He specializes in WPF, C#, to be significantly faster than the JavaScript counterpart, easily be converted into a server-side Blazor application.
and C++, and is proficient in especially for applications relying heavily on computations Both the client-side and server-side version of the Blazor
several other languages as and performance. Even though it might still be early to sample project are already published on Microsoft Azure and
well. His experience ranges talk about using client-side Blazor for online gaming in the publicly available (links at the end of the article and in a
from working on projects browser, it certainly brings that finish line a little closer (a sidebar).
in the fields of biomedical line that JavaScript has yet to fully cross).
image processing, genetic
and genomic data analysis, In contrast to server-side Blazor, client-side Blazor has the The Race to a Real-World Example
analytical software for the capability to also work offline, as it doesn’t require a con- Start off by creating a new Blazor project in Visual Studio
oil/gas industry, and custom stant connection to the back-end. However, this aspect in 2019 (Note: VS2017 doesn’t support client-side Blazor proj-
control development. particular is a double-edged sword, as the download size ects). It’s imperative to ensure that you also have the most
of a single client-side Blazor component is most likely far current version of .NET Core SDK (3.1.301, as of time of writ-
Since 2014, Otto is also an
Adjunct Professor at the greater than a comparable solution developed in JavaScript ing) installed. You will be prompted to choose between a
University of Houston, or server-side Blazor. In order to run Blazor client-side, you Blazor Server App and a Blazor WebAssembly App--I chose the
having taught various have to download the entire runtime for the Web applica- latter (Figure 1).
programming courses from tion, which your compiled Web project will then execute on.
C++ to Java. That means that even an empty site, or the typical “Hello Visual Studio sets up the project for you and gives a start-
World” project, will already amass to several megabytes ing point, which should be quite familiar to everyone who’s
When he’s not writing in download size. Additionally, this also has an impact on experimented with Blazor before, be it client-side or server-
code, he’s usually found non-standard browsers. Although most browsers are per- side. You can see three default Razor components in the
somewhere in a gym, fectly capable of dealing with WebAssembly, some niche or Pages folder: Index, Counter, and FetchData. If you compile
training for his next embedded browsers might not be and thus won’t be able and run the application, your browser shows the familiar
powerlifting competition. to load a website built on client-side Blazor technology. In sample application that Microsoft put together, which is the
that case, server-side Blazor is still an option, as the cli- same for client-side and server-side Blazor projects. This
ent only receives standard HTML and JavaScript from the very example is already in one of our previous Blazor ar-
server. ticles, so I won’t repeat that discussion.

I’m not going to spend much more time on the characteris- Today, I have one goal: A page displaying COVID-19 data and
tics of a Blazor application, the set-up, its ecosystem, or its graphs, with direct country comparison capabilities. Start
architecture, as CODE Magazine has already published two by creating a new component in the Pages folder, and name

38 En Route to Full-Stack .NET Development with Client-Side Blazor codemag.com


Figure 1: Creating a new Blazor project

it Covid.razor. (Note: Components MUST start with a capi-


tal letter). Reference the new page in the navigation menu,
found in the NavMenu component inside the Shared folder.
Run the application and you should see a link to the new
component in the navigation bar. It’s still blank of course,
but you’re about to change that. The concept or mock-up
can be described as follows: You want to be able to select
a country, add it to a direct comparison list, and display
all selected countries as columns with information about
their COVID-19 cases. You also want graphs, as illustrated
in Figure 2.

Before you get started, you also need to answer two impor-
tant questions:

• Where are you getting your data from?


• What library are you using to display the graphs?

You have several options to choose among the freely avail-


able APIs for data acquisition. The ones I’m using for this
example are:

• API for lists of countries and COVID-19 data:


https://api.covid19api.com
• API for population data of individual countries:
https://restcountries.eu Figure 2: The UI mockup
• API for USA data specifically:
https://covidtracking.com
separation. Although for most countries these individual
The reason to use a separate service for USA data specifi- data responses are easily combined, for the USA with its
cally is based on the circumstance that the first mentioned 50 states, it takes much longer to do so, and thus using a
API will return the data broken up into individual states/ single service call with a different end point to retrieve USA
territories for countries big enough to justify such data data alone is justified.

codemag.com En Route to Full-Stack .NET Development with Client-Side Blazor 39


The graphs are provided by a DevExpress Blazor compo- Note: The static class called APIUrls provides all the neces-
nent. After registration on the DevExpress website, add the sary URL addresses for the service calls, so you don’t have
DevExpress.Blazor NuGet package to the project, which in- to repeat or copy/paste them everywhere you need them.
cludes the necessary dependencies. The DevExpress compo- You also need to make sure that you have the necessary
nents setup is slightly different between a client-side and a @using and @inject statements at the top of the Razor Page,
server-side Blazor project. Please refer to the official DevEx- so that you have access to classes like Client. Additionally,
press GitHub page for detailed information: https://github. initialize the CountryDatasets variable here, which is a list
com/DevExpress/Blazor#set-up-your-environment. of ParallelCountryInformation objects that holds all the
countries’ information necessary for comparison.
Building the General Layout
You’re now ready to create the COVID-19 comparison com- The SelectedCountryChanged event simply sets the _se-
ponent. Start off adding a combo box and a button to the lectedCountry property to the parameter passed in as
top of the components Razor Page, each hooked up with an ChangeEventArgs. The AddDataSet click event of the but-
event: a change event for the combo box, and a click event ton kicks off all the logic to load COVID-19 related data and
for the button: puts them into the right format for the charts to display.
Let’s take a peek at this process next:
<select class="form-control"
Client-side or Server-side? @onchange="SelectedCountryChanged"> According to the mock-up, you have to provide the following
<option value="">-- Select Country --</option> information for each country to display:
Not every project is meant to @foreach (var country in _countries)
be a client-side Blazor project, { • Country Name
just like not every project <option value="@country.code"> • Population
is meant to run server-side. @country.name</option > • Total COVID-19 cases
Evaluate all aspects of your } • Total COVID-19 deaths
application to make the
</select> • COVID-19 deaths per 100k citizens (a normalized value
right choice.
<button @onclick="AddDataSet">Add</button> that is suited to directly comparing countries of dif-
If your application needs any ferent sizes)
offline functionality, client- Populate the combo box with the data acquired from a service
side Blazor is the only option. call to one of the APIs mentioned earlier. In order for this The Heavy Lifting
If your application needs to data to be initially available, make this service call as soon As a first step in the AddDataSet function, make the API call
run on thin clients that don’t as the page is initialized. Override the OnInitializedAsync to retrieve population data (see Listing 2).
support WebAssembly, server- method within the @code segment of our component as
side Blazor is the only option. shown in Listing 1. Note: The static class APIUrls provides the URL to make a
REST API call. The returned URL string has a placeholder
symbol ‘*’, which you replace with the selected country from
Listing 1: Overriding the OnInitializedAsync method the combo box, for instance:
protected override async Task OnInitializedAsync()
{ var url = APIUrls.GetCountryPopulationDataUrl();
CountryDatasets = new List<ParallelCountryInformation>();
_countries = new List<ComboCountry>(); This function call is serviced by:
var url = APIUrls.GetCountryList();
var jsonCountries = await
new Client(new NewtonsoftSerializationAdapter(), public static string
baseUri: new Uri(url)).GetAsync<List<APICountry>>(); GetCountryPopulationDataUrl()
foreach (var c in jsonCountries.Body) {
{ return
_countries.Add(new ComboCountry{
"https://restcountries.eu/rest/v2/alpha/*";
code = c.ISO2, name = c.Country });
} }
_countries = (from c in _countries orderby c.name ascending
select c).ToList(); Next, you need to acquire COVID-19-related information for
} the selected country. You do this with another API call (and a
different one if the USA had been selected—as explained ear-
lier). One parameter in the REST API URL indicates whether
Listing 2: REST API call to load country population data you want confirmed cases or death cases. Because you need
both, perform the call twice per country, as shown in List-
private async void AddDataSet(MouseEventArgs e)
ing 3.
{
var countryToAdd = new ParallelCountryInformation();
var url = APIUrls.GetCountryPopulationDataUrl(); Naturally, the response is in JSON format and includes re-
url = url.Replace("*", _selectedCountry); cords such as this one:
var popJsonResponse = await new Client(new
{"Country":"Austria",
NewtonsoftSerializationAdapter(), baseUri: new
Uri(url)).GetAsync<ParallelCountryInformation>(); "CountryCode":"AT",
"Province":"",
countryToAdd.Name = popJsonResponse.Body.Name; "City":"",
countryToAdd.Population = popJsonResponse.Body.Population; "CityCode":"",

"Lat":"47.52","Lon":"14.55",
}
"Cases":18165,

40 En Route to Full-Stack .NET Development with Client-Side Blazor codemag.com


"Status":"confirmed", Iterate over the collection of countries that had been added
"Date":"2020-07-04T00:00:00Z"} to CountryDatasets. The properties country.Name, country.
Population and country.ConfirmedCases refer to parameters
The class RestCountry reflects these properties (although on the ParallelCountryInformation objects and can be ad-
you don’t really need some information that’s returned, dressed directly from within a <span>. The DevExpress chart
such as the Lon and Lat values). component is called DxChart, and is set up using various
mostly self-explanatory parameters, as seen in Listing 5. You
Each ParallelCountryInformation object in the list of coun- can decide which series type you want the DxChart to use.
tries you want to compare has three lists of CovidTimeStep I’ve chosen the DxChartLineSeries, but could exchange that
objects. These time steps are used to populate the charts. to any other series type supported by this DevExpress compo-
nent, such as DxChartBarSeries or DxChartAreaSeries. Make the Switch!
foreach (var c in jsonCountriesConfirmed.Body )
{ You’re also deciding dynamically which color you want your If you decide to change your
var timestep = new CovidTimeStep() charts to use. For that purpose, call the function getCol- hosting model later in
{ orForCountry, which returns separate colors for confirmed the development process
Cases = Int32.Parse(c.Cases), cases and death cases. (client-side to server-side or
Country = c.Country, vice versa), you don’t have
CountryCode = c.CountryCode, Because it was in the mock-up, I also added a remove but- to worry about any code
changes whatsoever.
Date = Convert.ToDateTime(c.Date), ton in each column to remove it from the comparison. This
Province = c.Province, works by simply passing the country’s name to a Remove- The only differences that
Status = c.Status Column function, which then removes the corresponding need to be made are within
}; data set from the CountryDatasets list. the project configuration and
timeStepCollectionConfirmed.Add(timestep); structure. You can do this
} The Final Lap: Compiled, Displayed, Published manually, or simply set up
You can run the client-side Blazor application and add sev- a new project in the preferred
The reason you keep the Province attribute is to distinguish eral countries to directly compare some of their COVID-19 hosting model and copy
responses that contain data for the country as a whole from related data by simply pressing F5, as shown in Figure 3. over your code files.
responses that contain that same data but broken up into
different states or provinces. For instance, Australia returns
data separately for New South Wales, Victoria, Queensland, Listing 3: REST API calls to load COVID-19 data
Western Australia, South Australia, Tasmania, Australian var covidUrl = APIUrls.GetCountryDailyCases();
Capital Territory, and Northern Territory. In this case, loop var selectedCountryToAdd = countryToAdd.Name;
over all entries in the response data that share a common
date and combine them. var confirmedCovidUrl = covidUrl.Replace("*",
selectedCountryToAdd);
confirmedCovidUrl = confirmedCovidUrl.Replace("#", "confirmed");
In order to provide the time steps for the normalized graph
(cases per 100k citizens), you don’t need to make additional var deathCovidUrl = covidUrl.Replace("*", selectedCountryToAdd);
API calls. You already know the country’s population, as well deathCovidUrl = deathCovidUrl.Replace("#", "deaths");
as the recorded COVID-19 cases. So you create another List var jsonCountriesConfirmed = await new Client(new
of CovidTimeStep objects, perform the normalization, and NewtonsoftSerializationAdapter(), baseUri: new
add the list to the object created for comparison. This whole Uri(confirmedCovidUrl)).GetAsync<List<RestCountry>>();
process is shown in Listing 4.
var timeStepCollectionConfirmed = new List<CovidTimeStep>();
var timeStepCollectionDeath = new List<CovidTimeStep>();
Note: Use BigInteger to perform the normalization, as
these multiplications typically exceed the maximum value
available for integer numbers.
Listing 4: Normalization of COVID-19 death cases per 100k citizens
Once the ParallelCountryInformation object has been pro- var timeStepCollectionDeathPer100k = new List<CovidTimeStep>();
vided with all the information you want to be displayed, add foreach (var step in countryToAdd.DeathTimeSteps)
{
it to CountryDatasets and notify the component that its var timestepdeathper100k = new CovidTimeStep()
state has changed (which causes a re-render). {
Country = step.Country,
CountryDatasets.Add(countryToAdd); CountryCode = step.CountryCode,
Date = step.Date,
this.StateHasChanged();
Status = "deathper100k"
};
You’re now all set with data objects. You have the capability
to select a country, load relevant data, and offer it to any BigInteger cases = new BigInteger(step.Cases);
consuming component that can work with it. BigInteger casesDividend = BigInteger.Multiply(cases,
new BigInteger(100000));

For the proposed layout, choose a simple horizontal layout BigInteger casesPer100k = BigInteger.Divide(casesDividend,
of columns, each of which represents one country. Accom- new BigInteger(Convert.ToInt32(countryToAdd.Population)));
plish that with a css class selector that you apply to a <div>:
timestepdeathper100k.Cases =
Convert.ToInt32(casesPer100k.ToString());
.arrange-horizontally > * {
display: inline-block; timeStepCollectionDeathPer100k.Add(timestepdeathper100k);
text-align: center; }

codemag.com En Route to Full-Stack .NET Development with Client-Side Blazor 41


The process to publish your app on Microsoft Azure is in- Next, go through the publish dialogue of the client-side
credibly simple and well-integrated. First, set up a new App Blazor project. When asked, select to publish to an existing
Service as a new Azure Resource. I named it Covid19Com- resource (you could also set up a new one during this dia-
pareWASM (as shown in Figure 4). logue, but for the purpose of streamlining the publication

Listing 5: Displaying the country information and graphs as columns within the component
<div class="arrange-horizontally"> <DxChartCommonSeries AggregationMethod="@(i =>
@foreach (var country in CountryDatasets) (int)i.Average())"
{ NameField="@((CovidTimeStep c) => "")"
<div style="width: 350px"> ArgumentField="@(i => i.Date.ToString("MM/dd"))"
<span>@country.Name </span> ValueField="@((CovidTimeStep i) => i.Cases)">
<button @onclick="@(() => RemoveColumn(country.Name))" <SeriesTemplate Context="settings">
style="width: 60px; height: 20px; color: red; <DxChartLineSeries Settings="@settings"
font-size: x-small">Remove</button> Color="@(getColorForCountry("confirmed"))">
<br /> </DxChartLineSeries>
</SeriesTemplate>
<span><b>Population:</b> @country.Population </span> </DxChartCommonSeries>
<br /> </DxChart>
</div>
<span><b>Total Cases: </b> @*...same approach for death cases & norm. death cases*@
@country.ConfirmedCases</span> </div>
<br /> }
<div> </div>
<DxChart Data="@country.ConfirmedTimeSteps">

Figure 3: Four countries in direct comparison.

42 En Route to Full-Stack .NET Development with Client-Side Blazor codemag.com


process, I like to have one set up and ready at this point). In order to emphasize how easy it is to port between client-
You’re prompted to select an Azure App Service to publish side and server-side Blazor applications, I’ve also hosted an
to. When logged in with the same account used on the MS identical but server-side Blazor version of this application
Azure Portal site, you can see the resource you just created at: https://covid19compare.azurewebsites.net/parallel.
a few moments earlier in the list of choices (Figure 5).
The only code-wise difference between the two versions is
Now the publishing setup is complete and any subsequent the setup required by the DevExpress component, which is
deployment is only one click away. The public URL that the outlined in their setup instructions on GitHub. Other than
application is published to can also be found on the publish- that, all user-created .cs, .css, and .razor files in are identi-
ing view within Visual Studio (Figure 6). cal in both projects. There are no differences in the deploy-
ment process either.
I’ve hosted the client-side Blazor app on Azure and it’s pub-
licly available at: http://covid19comparewasm.azureweb- If you browse to the published server-side application,
sites.net/. you’ll notice that it also includes a page with a different

Figure 4: Adding a new App Service to the Microsoft Azure portal

Find the Files


The client-side Blazor app
is hosted on Azure and
is publicly available at:
http://covid19comparewasm.
azurewebsites.net/

The identical server-side


Blazor app is also hosted on
Azure and is publicly available
at: https://covid19compare.
azurewebsites.net/parallel

Figure 5: Select the App Service you created in MS Azure to deploy your client-side Blazor application.

codemag.com En Route to Full-Stack .NET Development with Client-Side Blazor 43


Figure 6: Publishing the client-side Blazor application to MS Azure.

Figure 7: The published and expanded Blazor app (server side).

component, which shows a graph that allows you to com- mean that it should or needs to be used for every single
SPONSORED SIDEBAR: pare individual countries as a separate series within one upcoming Web project. And for some projects, server-side
Interested in Moving chart. In this visualization, you can even compare coun- Blazor might even be better suited. Ultimately though, I can
to the Cloud? tries to US states (here is where that Province parameter confirm the following:
CODE Can Help! previously mentioned comes in handy). I included a date
picker to select From and To dates, a color picker for chart Full stack .NET Web development has shifted into second
Take advantage of a line colors, and the ability to display the daily case chang- gear and moved over into the passing lane. JavaScript-pow-
FREE hour-long CODE es as opposed to the accumulated total number of cases. ered frameworks might still lead in this race, but Blazor is
Consulting session I also changed the application color scheme a little bit definitely equipped with a Turbocharger and, at some point,
(yes, FREE!) to jump-start (Figure 7). might challenge JavaScript for the Pole Position in this
your organization’s plans Grand Prix of Web Development.
to develop solutions on
the Microsoft Azure or Post-Race Conference and Analysis  Otto Dobretsberger
Amazon Web Services (AWS) Both the client-side and server-side Blazor version of this 
cloud platforms. application remain under development and will receive con-
For more information, tinuous updates on a regular basis as more features and
visit www.codemag.com/ components are added. This project has led me to the con-
consulting or email us at
clusion that the client-side Blazor technology is certainly
[email protected].
ready for real production projects. That, of course, doesn’t

44 En Route to Full-Stack .NET Development with Client-Side Blazor codemag.com


VIRTUAL
SEP. 15– 17, 2020

Louis Grasse

A re-imagined
virtual event where expouav.com
the commercial drone
community will gather
Visit the
to learn, connect virtual
and drive the exhibits
industry forward. for free!

Phase One Industrial

LEARN CONNECT EXPERIENCE


Expansive education program Facilitated networking, Cutting-edge UAS solutions,
with live and pre-recorded AI-powered matchmaking, drone demonstrations
solutions-oriented presentations & and focused roundtables & exclusive training
workshops with live Q&A and chat

Register by August 15th


for early-bird pricing.

Produced by Diversified Communications

THE COMMERCIAL UAV EVENT FOR:

Construction Energy Forestry Infrastructure Mining Public Safety Security Surveying


& Utilities & Agriculture & Transportation & Aggregates & Emergency Services & Mapping

codemag.com Title article 45


ONLINE QUICK ID 2009071

Prototyping with Microsoft Maquette:


A New Virtual Reality Tool
In this article, we’re going to talk about Microsoft Maquette (see https://www.maquette.ms). It’s a spatial prototyping tool aimed to
help users to mock-up virtual reality (VR) and augmented reality (AR) experiences very fast. This tool is still evolving and is currently
in beta. We briefly talked about this tool in a previous CODE Magazine article (see https://www.codemag.com/article/1903081)

But here we’re going to formally introduce it and explain One of the biggest challenges is to know whether a given
how to get started with it. We’ll also explain how you can experience is viable and fun to use and also that it solves a
tweak it by using scripting at runtime. given problem. Usually designers use mockup tools to cre-
ate an impression of a UX experience, and then try to judge
whether these mockups should come to life. Historically,
tools like Visual Basic or HyperCard were used to this pur-
If a picture is worth a thousand pose. Framer is a more modern version of such a tool for 2D.
words, a prototype is worth a Development of Maquette started in early 2017 after real-
Stefan Landvogt
thousand meetings. izing that no such simple tool is available for the VR and
[email protected]
 Tom & David Kelleyç AR context. A small team at Microsoft started the inception
Stefan is a Principal Software of the tool, and the first goal was to have a feature set
Developer for Microsoft rich enough so that the development of Maquette could be
and helps to ship Maquette driven by using Maquette itself. A very rudimentary version
to its customers. The target users for Maquette are mainly designers or de- was available after around three months of work, and since
velopers, people who’re looking for a fast way to evaluate then, the development of the tool has been done by using
He holds a Masters equivalent their ideas in AR and VR without actually building them. Maquette itself (kind of like a C-compiler written in C).
degree from the University Usually a designer creates a mockup in a 2D tool like Pho-
of Würzburg in Applied
toshop and other UX design tools and then presents it with
Computer Science and
PowerPoint on a traditional 2D screen. With VR and AR and
Artificial Intelligence and
started his career working
the addition of the third dimension, these mockups have Development of Maquette started
the potential to deceive the customer in the viability of a
on expert systems.
product. After getting funding, the designer teams up with
in early 2017 after realizing
a developer, who has to make a serious investment in time that a simple tool was available
and programming to create the experience—often in tools for VR and AR.
like Unity or Unreal. After the developer invests significant
time to implement the design, there are often realizations
that the design is faulty now that it’s been translated into
the VR/AR environment. The development process itself is also different from most
products: It happens in a so-called cabal setting with no
Maquette allows creation of a spatial prototype in a context management structure and no formal roadmap of the final
that can be presented in the medium that the experiences product. The team decides which feature will have the big-
Vassili Kaplan are built for. With it, it’s possible to iterate early on a UX gest impact—design- or business-wise—and implements
[email protected] and learn whether the application is actually useful and fun that, ships it to the customer, listens to feedback, and re-
http://www.iLanguage.ch to use. The intention of Maquette is to help AR and VR devel- peats. This all happens while designers, programmers, and
opers conceive and refine their concepts and help find the technical artists sit in the same room or are constantly con-
Vassili is a former Microsoft
next AR and VR killer application faster without the need to nected through chat applications. This leads to a product
Lync developer who has
worked in several countries.
fully implement it first. that might not have some “basic” features—like a layout
He has a Masters in system or easy alignment at first—but only has features that
are actually used and needed. With that, the team added, re-
Applied Mathematics with
Specialization in Computa-
Introduction to Microsoft Maquette fined, and fully realized features as it learned from feedback
tional Sciences from Purdue The biggest challenge with AR and VR applications is the and as the customers requested. After around nine months,
University, West Lafayette, addition of the third dimension to their experience. Also, the decision was made to share the internal tool with the
Indiana and a Bachelor the user interface is usually co-located with the actual general public, so Microsoft Maquette shipped through the
in Applied Mathematics experience—which leads to lots of interesting interaction Microsoft Store and Steam in October 2018, and through the
from ITAM, Mexico City. problems. Oculus Store in July 2020.

Even though there have been VR and AR headsets available The work to introduce scripting and interactivity was han-
in academics since Douglas Engelbart built the first proto- dled in a similar fashion. The goal has been to make easy
types in the 1960s, they only became available recently to problems in Maquette easy to solve, such that the “Hello
the masses after the availability of high-resolution displays World” for interactivity won’t require weeks of investment
from mobile phones, new inventions in tracking technology, and learning. How to best expose approachable, AR/VR-
and access to the power of high-end graphics cards. friendly interactivity and scripting isn’t a solved problem

46 Prototyping with Microsoft Maquette: A New Virtual Reality Tool codemag.com


and it’s hubris to think we can get it perfect up front. So, but adding the possibility of interactivity driven by scripting
the team will be introducing the initial functionality in order is the next big milestone and will enable better prototypes,
to learn what works well and then iterate. because they will be closer to the final experience.

In a first iteration, CSCS was used as a scripting solution.


We could’ve bolted in Python, JavaScript, or C#, but that
would be at odds with the altitude of problem and customer Adding the possibility of
Maquette is targeting and way more effort than integrating
CSCS. Maquette is neither Unity nor Unreal and not trying to
interactivity driven by scripting
be. Not all problems need bare metal access (which becomes is the next big milestone and
a security concern) nor expert programming features. These will enable better prototypes.
scripting options can also incur substantial initial and ongo-
ing development efforts to enable integration (e.g., inside
Unity) with less control and flexibility for the team to focus
on what’s currently rough and unsolved—easy creation of Adding the possibility to create stand-alone Maquette proj-
interactive content for and in an AR/VR context—which we ects that can be shipped by customers independent of Ma-
think is an important missing option. quette is another big step forward in the future. Other goals
are to create a simple sharing and presentation service of
Our choice of CSCS allowed us to get up and running with a Maquette projects and the port to newly available all-in-one
stable and sandboxed scripting facility to enable explora- headsets like the Oculus Quest. These goals shouldn’t be
tion of how to best integrate scripting and interactivity in misunderstood as product promises, but rather the afore-
Mixed Reality (MR) in a way best suited for mere mortals. mentioned bigger arcs. Every user can and should chime in
Choice of language syntax and semantics and even the spe- at http://aka.ms/maquette-community to drive Maquette
cific implementation is among the least of the interesting toward their needs and wants.
challenges.
Getting Started with Microsoft Maquette
Nevertheless, familiar syntax will reduce learning and fric- Microsoft Maquette is currently a Windows application for
tion which is why, at a second step, we decided to bring PC-tethered VR headsets that are compatible with SteamVR.
JavaScript into Maquette instead of CSCS. What are the main This includes Windows MR, Oculus, and HTC Vive headsets.
reasons for the switch? Because running VR tools takes a lot of system resources, you
also need a capable Windows PC or a laptop. Usually the mini-
There are many developers available with basic JavaScript mum specifications for a SteamVR-compatible PCs are fine.
knowledge. Not only hardcore-programmers, but also Web
designers and beginners with basic knowledge can work The download of Maquette alone is not enough, unfortu-
with it right away. There’s no learning curve for the lan- nately: A running SteamVR environment is needed as well as
guage, but you need to learn how the framework is embed- a VR headset with controllers that are set up well and track,
ded into the language. JavaScript is standardized through which might sometimes be a challenge.
ECMAScript, and there are no surprises as long as the imple-
mentation passes the test suite. JavaScript can be much
faster than CSCS if the implementation provides a just-in-
time compiler (JIT), but that JIT compiler makes the script-
ing language vulnerable to malicious attacks.

For a JavaScript implementation, we decided to use Jint.


Jint was developed by Sebastien Ros, who is currently at
Microsoft. Jint is a JavaScript interpreter for .NET, provid-
ing full ECMA 5.1 compliance, but doesn’t implement a JIT
compiler. (See the Jint links in the sidebar.)

We need real-world experiences and feedback, which isn’t


possible without use and iteration. As we iterate and resolve
how this is best done, anticipate that there may be changes
as we learn the lessons of what’s best for our community
and what’s most appropriate for long-term product and se-
curity needs.

Microsoft Maquette Roadmap


Since the release, the further development is driven by Ma-
quette’s customers, and although following a bigger arc,
the development team wants the tool moving along. The
team listens closely to the feedback of users and tries to
build a product to help them to find the next killer applica-
tion of VR and AR.

It’s hard to present a roadmap when the development per-


manently pivots toward the current needs of the customers, Figure 1: Screen with spotlights of Maquette projects presented when Microsoft Maquette starts.

codemag.com Prototyping with Microsoft Maquette: A New Virtual Reality Tool 47


When the users start Maquette, some initial projects and tu- every functionality is implemented as a C# method, which
torials are presented (see Figure 1), so that they can learn gives the implementor very fine-grained and ultimate con-
what the tool can do for them and how to do it—these can trol about everything. There’s no JIT and the script is inter-
be seen any time afterward, but the more experienced user preted every time the script is executed. The programmer
can also just skip them. For the first-time user, an interac- has 100% control of what is exposed, and it’s really simple
tive tutorial is started where the user learns the basics of to have a sandboxed environment. Only the functions and
the product: how to create and place objects, how to ma- classes that are explicitly exposed in CSCS are available to
nipulate them, and how to move around in the world. the user. This gives the implementor great control over what
is visible and what isn’t.
Creating Your First Object with Maquette
One of the best uses of Maquette is to place objects in a CSCS is very easy to extend: If you want to use the words
3D world, and make a sequence of scenes to see how these BEGIN and END instead of “{“ and “}” (curly brackets),
objects change over time without any form of programming. that’s doable in under a minute. You can create your own
It’s easy to create a cube or other 3D shapes or load models scripting language with your own syntax and semantics in
and place them in the world. Figure 2 shows how you can do minutes without the need to learn how the parser works.
it using the controllers. The flexibility can be a burden, but it allows you to just add
that one special kind of thing, that a “standardized” lan-
Take a look at a first tutorial (https://www.maquette.ms/tu- guage wouldn’t allow. Also, the implementation of CSCS is
torial-interacting-with-ui) that explains how to do it in great just a couple of files that are easily understood and easy
detail. As you can see in Figure 2, there are many different to debug and scrutinize for vulnerability. It’s very easy to
objects to choose from. After watching the tutorial, you’ll see create a very purposeful and powerful language that’s tai-
how easy it is to change the object’s size, color, shape, etc. lored toward the problem where programming space and
problem space are very tight and meaningfully coupled. An
After creating a few objects, you should quickly familiarize example could be a special loop construct over vertices or
yourself with using the controllers and navigating through 3D objects.
Maquette with them by leveraging the other tutorials that
are readily available. As mentioned before, the decision was to eventually replace
it with JavaScript. Nevertheless, in this section, and later
on, we’re going to show how we used CSCS to develop the
Microsoft Maquette Scripting Maquette Scripting Engine in order to give you an idea how
As mentioned above in the history part, CSCS was the first you can use it in your own projects, if you value language
step in developing the Maquette scripting engine. CSCS was flexibility and sandboxing more than the speed advantages
envisioned to make it very easy to create simple scripting that you get from the JIT compilation technology. Note that
languages of any kind. It’s very flexible with the syntax and the CSCS and JavaScript syntax, by default, is very similar,
so you shouldn’t have any problem in porting CSCS exam-
ples below to JavaScript, even if there are some differences,
they’re really minimal.

Note that the screenshots and the described behavior below


is from an internal version of Maquette that didn’t ship to the
general public, but to beta testers. Also, one feedback from
the beta testers was to be 100% compatible to JavaScript,
and following that feedback was one reason to switch as
well.

One of the guiding principles of Maquette was to become


some sort of “Visual Basic in 3D” or a “HyperCard in 3D.”

One of the guiding principles of


Maquette was to become a “Visual
Basic in 3D” or a “HyperCard in 3D.”

One crucial part here is to have a scripting language that


can be used with three different main purposes:

• To extend the tool during the edit phase of an experience


• To test the tool
• To create scripted experiences that run in a play mode

For this, an interface between the application itself and a


Figure 2: Tutorials and placing objects in the world. scripting back-end is needed.

48 Prototyping with Microsoft Maquette: A New Virtual Reality Tool codemag.com


Introduction to CSCS Scripting in Maquette
Some usage of CSCS in Maquette was previously described
in CODE Magazine—see https://www.codemag.com/arti-
cle/1903081. Note that even though CSCS is syntactically
close to JavaScript, the syntax can be easily changed to
whatever makes sense for the current project. Basically, you
have complete control of everything and can easily limit
what the user can or can’t do for the security aspect.

The CSCS syntax can be easily


changed to whatever makes
sense for the current project.

CSCS can be triggered in different ways.

One way is to trigger scripting from inside of the Maquette, us-


ing controllers. Select the icon showing curly braces in a box,
which is located in the lower middle section, and then browse
to the script file you want to execute—see Figure 3 for details.

The result of running the CreateCube.mqs script is shown


in Figure 4. The contents of CreateCube.mqs file is shown
in Listing 1.

What it does is the creation of a global action. This is accom-


plished by invoking the globalAction.Add() method which Figure 3: Executing scripts inside of the Maquette.
adds the function CreateCubeFromMenu to the panel with
the global actions, names that function “Create Cube” and
uses the image C_512.png from the Resources-Folder to dis- Listing 1: Creating Cube in Using CSCS Scripting
play an icon (consisting of a letter “C”): function PutInFrontOfUser(object, x, y, r, g, b )
{
globalAction.Add( 0, object.position = user.PositionInFront(0.6);
"Create Cube", object.rotation = user.RotationToFace(object);
object.scale = V3(0.1, 0.1, 0.1);
"<MAQUETTECONTENTSCRIPTS>/Resources/C_512.png", object.color = Color(r, g, b);
"CreateCubeFromMenu" ); object.translate(V3(x, y, 0.0));
return object;
With this, you can easily extend Maquette’s functionality at }
runtime. function CreateCubeFromMenu()
{
As soon as a new global action is added, it can be triggered myCube = CreateCube();
as well. After selecting letter C with a controller, effectively PutInFrontOfUser(myCube, -0.4, 0.2, 1.0, 0.0, 0.0);
CreateCubeFromMenu() function from Listing 1 is invoked return myCube;
}
and as a result you get a red cube as shown in Figure 4.
globalAction.Add( 0,
The PutInFrontOfUser() function takes any given object and "Create Cube",
puts it in front of the user with a given angle, distance, and "<MAQUETTECONTENTSCRIPTS>/Resources/C_512.png",
"CreateCubeFromMenu" );
scale:

function PutInFrontOfUser(obj, x, y, r, g, b)
{ play around with this) and the colors RGB (red, green, blue)
obj.Position = user.PositionInFront(0.6); parameters. The scale parameter is always set to 0.1 for all
obj.rotation = user.RotationToFace(obj); coordinates. You can easily play around with different param-
obj.scale = V3(0.1, 0.1, 0.1); eters, z-coordinates, scales, transformations, etc.
obj.color = Color(r, g, b);
obj.translate(V3(x, y, 0.0)); The CreateCubeFromMenu() function creates a red cube (be-
return obj; cause (r, g, b) parameters of (1, 0, 0) correspond to the red
} color).

The parameters passed to this method, besides the object Creating Multiple Objects
obj, are x and y object coordinates (you can see inside of this Let’s see an example of producing a series of figures. Here is
method that the z coordinate is always zero, but feel free to the CSCS code to create n cubes:

codemag.com Prototyping with Microsoft Maquette: A New Virtual Reality Tool 49


function CreateCubes(n) { Inside of the CreateCubes() method, you use a delta param-
Delta = 1 / n; eter in order to gradually change the colors of a cube. This
for (i = 0; i < n; i++) { is used in the call to the PutInFrontOfUser() method that we
cubeObj = new Cube(); discussed in the previous section.
r = 0.1 + i* delta;
g = 1.0 - i* delta; As you can see, the CSCS syntax is very similar to JavaScript
b = 0.9 - i* delta; syntax. You might want to ask why the code in the previous
PutInFrontOfUser(cubeObj, -0.2*i, 0.2, section has obj.Position with “Position” starting with an up-
r, g, b); percase letter, but obj.rotation is all in lowercase. This is be-
} cause CSCS is case insensitive (except “core” language con-
} structs, like if-else, for, while, function, class, new, return,
break, continue, try, throw, catch, etc.). This is one of its
CreateCubes(10); differences with JavaScript, the latter being case-sensitive.
ui.TakeScreenshot("C:/Pics/myscreenshot.jpg");
The actual creation of cubes is completed in the Create-
Cubes(10) method call, which creates 10 cubes. After invok-
ing that call, you take a screenshot of whatever you got on
the Maquette screen. This is done in the ui.TakeScreenShot()
method. The result of creating multiple objects is shown in
Figure 5.

Different from JavaScript, CSCS


is case-insensitive, except for
a few core language constructs.

As you can see there, each cube has a slightly different color
than its predecessor. This is accomplished via applying the
delta parameter to the RGB color parameters that we dis-
cussed above. The color changes from light green to red by
playing around with the RGBs. This is done by multiplying
delta by i in the call to PutInFrontOfUser() method, where i
is between 0 and n.

In this example, we used cubes. You can also use one of


many other geometrical figures. For example, in place of the
cubeObj = new Cube(); statement in the CreateCubes()
method above, you can construct any of the following ob-
jects below, but there are also other ways to bring objects
into Maquette.

Figure 4: Execution of CreateCube.mqs script and adding a global action. capsuleObj = new Capsule();
circleObj = new Circle();
coneObj = new Cone();
cubeObj = new Cube();
cylinderObj = new Cylinder();
planeObj = new Plane();
sphereObj = new Sphere();
torusObj = new Torus();
tubeObj = new Tube();

Debugging Maquette with Visual Studio Code


There is another way of invoking scripting: You can also debug
scripting using Visual Studio Code. This is quite easy to do.
You download Visual Studio Code (unless you have already
done it), get the free CSCS Visual Studio Code Extension from
the Marketplace (see links in the sidebar) and you’re all set!
Now you can start executing CSCS scripting commands by se-
lecting the code to execute and pressing Ctrl-8.

Basically, you can use this extension for the following types
Figure 5: Creating 10 cubes using scripting. of debugging:

50 Prototyping with Microsoft Maquette: A New Virtual Reality Tool codemag.com


• Debugging the whole file. You can set breakpoints, Scripting Extensions References
step into the code, inspect the variable values and Maquette scripting has a few special objects with their own
watch them. implementation. Some examples of such objects are: app, en- Download Microsoft Maquette:
• Select a part of the code to be executed and press Ctrl- vironment, globalAction, help, log, object, scene, system, ui, https://www.maquette.ms
8. This sends only the selected code for execution. If user, viewpoint, and some others. These objects provide some
SteamVR:
nothing is selected, the current line is sent for execu- Maquette related functionality in an object-oriented way. https://store.steampowered.com/
tion. This is the so called REPL (Read Eval Print Loop) steamvr
mode of execution. For example, in the code snippet in the previous section, we
• You can also start a new REPL command-line shell in used the CSCS ui.TakeScreenshot(“C:/Pics/myscreenshot. Maquette Tutorials:
Visual Studio Code by pressing Ctrl-7 from any editor jpg”) call to create a screenshot of whatever the user is see- https://www.maquette.ms/
Window. Then you can just start typing different com- ing in Maquette and save it into a given file. tutorials
mand on the shell.
The UI object is responsible for different user interface VR and MR Headsets:
parameters. It has some very useful methods with self-ex- https://www.microsoft.com/
planatory names, like ui.SceneMenu(), ui.SetResolution(), en-us/store/b/virtualreality?ici
Download VS Code, get ui.ShowTooltips(), and many others. You can get the full list d=CNavVirtualReality

the free CSCS Extension from through the scripting help command. Visual Studio Code:
https://code.visualstudio.com/
the Marketplace, and you can The Log objects helps printing various info or debug messages. download
start modifying your Maquette Some of its methods are: log.Debug(), log.Info(), and log.Error().
JavaScript Interpreter for .Net
project on-the-fly by selecting There’s also the User object, which has methods related to JintScript:
the code and pressing Ctrl–8. the position of the user inside Maquette. Some of the meth- https://github.com/
ods on that object are: user.PositionInFront(), user.Rota- sebastienros/jint
tionToFace(), user.Scale(), user.Position(), user.Rotation(),
user.Forward(), and a few others. ECMAScript Language
Figure 6 shows a sample session when the user typed the Specification:
http://www.ecma-international.
help command. Note that in order to call the next (or previ- The Viewpoint object is responsible for Maquette Viewpoints.
org/ecma-262/5.1/
ous) command from the command history, you type Ctrl-K Some of the useful methods and properties on that object
(Ctrl-I) on Windows and -K ( -I) on a Mac. This is be- are: viewpoint.GotoNext(), viewpoint.GotoLast(), viewpoint. CSCS Language E-book:
cause just pressing up and down arrows moves the cursor Add(), viewpoint.Remove(), viewpoint.Count, and others. https://www.syncfusion.com/
inside the editing area. ebooks/implementing-a-custom-
The System object is responsible for some auxiliary func- language
Note that the first two options of using CSCS Visual Studio tionality that might be useful to the user, for example:
Code Extension were previously discussed in the March/April system.OpenExplorer(), system.OpenURL(), system.GetRan-
2019 issue of CODE Magazine. domInInterval(), and some others.

Figure 6: CSCS REPL help screen.

codemag.com Prototyping with Microsoft Maquette: A New Virtual Reality Tool 51


Listing 2: Taking Screenshots of All Spotlights Using CSCS Scripting
function TakeScreenshotsOfAllSpotlights() viewpoints.Count(); viewpointCounter++) {
{ v = viewpoints.Get(viewpointCounter);
viewpoints.Visible = false; viewpoints.Goto(viewpointCounter);
timestampString = system.UniqueTimestamp(); app.WaitForFrame();
nSpotlightMax = spotlights.Count; ui.TakeScreenshot(myDir + "" + spotlightCounter + "_" +
for (spotlightCounter = 0; spotlightCounter < nSpotlightMax; sceneCounter + "_" + viewpointCounter + "_" +
spotlightCounter++) { v.name + ".png");
currentName = spotlights.Name(spotlightCounter); }
myDir = "c:/Spotlights/" + timestampString + "/"; }
spotlights.Load(spotlightCounter); }
}
for (sceneCounter = 0; sceneCounter < scenes.Count();
sceneCounter++) { TakeScreenshotsOfAllSpotlights();
scenes.Current = sceneCounter; system.OpenExplorer( "c:/Spotlights/", true );
for (viewpointCounter = 0; viewpointCounter <

waits for the redraw of the scene to happen with app.Wait-


ForFrame() call, and then takes a screenshot. The call to
system.OpenExplorer() is an example how scripting can also
interact with the system and open a folder in Windows Ex-
plorer.

When going over the scenes, you activate a current scene


via the following invocation:

scenes.Current = sceneCounter;

Note that each screenshot filename contains the viewpoint


name, which you can see in Figure 7.

Wrapping Up
Microsoft Maquette is a tool designed to facilitate spatial pro-
totyping and address a few existing problems in AR and VR
today. One of the first steps to getting started with Maquette
is watching video tutorials at https://www.maquette.ms/
tutorials. They contain a few examples on how to get started
and move into some more advanced topics.

Once you get more familiar with Maquette, navigating inside


and creating new scenes and objects, you can start using
Maquette scripting for more advanced usages, like prototyp-
ing of common scenes, testing, or changing the contents of
a scene at runtime on the fly.
Figure 7: Results of collecting screenshots from all spotlights.
We’re looking forward to your feedback: what you can cre-
ate with Maquette, or what other features in Maquette and
Taking Screenshots from All scripting you would like to see.

Spotlights Example Special thanks to Dan Newell, Jia Wang, Dan Corrigan, Ricar-
Microsoft Maquette ships with over 20 spotlights that pres- do Acosta, and Tong Chen for proofreading and suggestions.
ent you with what can be done with the tool. All of these
spotlights contain multiple scenes and every scene can have  Stefan Landvogt and Vassili Kaplan
one or multiple viewpoints. A viewpoint is a place that can 
be visited through a menu and can be a vantage point into
the 3D world created.

Let’s see an example of navigating through all of the exist-


ing spotlights and taking a screen shot from each scene at
every viewpoint. The scripting code performing this task is
shown in Listing 2. The result of collecting all of the screen-
shots is shown in Figure 7.

As you can see in Listing 2, the function TakeScreenshot-


sOfAllSpotlights() iterates through all available spotlights,
opens all scenes, and visits all viewpoints per scene. It then

52 Prototyping with Microsoft Maquette: A New Virtual Reality Tool codemag.com


OLD
TECH HOLDING
YOU BACK?

Are you being held back by a legacy application that needs to be modernized? We can help.
We specialize in converting legacy applications to modern technologies. Whether your application
is currently written in Visual Basic, FoxPro, Access, ASP Classic, .NET 1.0, PHP, Delphi…
or something else, we can help.

codemag.com/legacy
832-717-4445 ext. 9 • [email protected]

codemag.com Title article 53


ONLINE QUICK ID 2009081

A Deep Dive into ASP.NET


Core Localization
Globalization and localization are two important concepts that you should be aware of to internationalize your applications.
In essence, globalization and localization are concepts that help you reach a wider audience. The former relates to building
applications that support various cultures and the latter relates to how you can build your application that can support a

particular locale and culture. In other words, an application 9. Specify authentication as “No Authentication.” You
takes advantage of globalization to be able to cater to dif- won’t be using authentication here either.
ferent languages based on user choice. Localization is ad- 10. Click Create to complete the process.
opted by the application to adapt the content of a website
to various regions or cultures. A new ASP.NET Core project will be created in Visual Studio
2019. You’ll use the project you’ve just created later.
Broadly, the three steps you should follow to localize your
application include:
Configuring Startup
Joydip Kanjilal 1. Sift through your application to identify the localizable It should be noted that localization in ASP.NET Core is an
[email protected] content. opt-in feature and is not enabled by default. The ASP.NET
2. Create localized resources for the languages and cul- Core framework provides a middleware that is meant for
Joydip Kanjilal is an MVP tures the application has support for. localization. You can add this middleware to the request
(2007-2012), software 3. Implement a strategy that can be used to select a lan- processing pipeline by calling the UseRequestLocalization
architect, author, and guage or a culture per request. method on the IApplicationBuilder instance.
speaker with more than
20 years of experience.
I’ll discuss each of these points in this article as I show you First off, you should add localization services to the appli-
He has more than 16 years
how you can build multilingual applications in ASP.NET Core. cation. To add localization services to your application, you
of experience in Microsoft
.NET and its related can use the following code.
technologies. Joydip has You should have Visual Studio 2019 (an earlier version will
authored eight books, also work but Visual Studio 2019 is preferred) installed public void ConfigureServices(IServiceCollection services)
more than 500 articles, on your system. You can download a copy of it from here: {
and has reviewed more https://visualstudio.microsoft.com/downloads/ services.AddControllersWithViews();
than a dozen books. services.AddLocalization(opt =>
{
Getting Started opt.ResourcesPath = "Resources";
First off, let’s create a new ASP.NET Core MVC project in Vi- });
sual Studio. There are several ways to create a project in Vi- }
sual Studio 2019. When you launch Visual Studio, you’ll see
the start window and you can choose “Create a new project” The preceding code snippet illustrates how the localization ser-
from there. Alternatively, you can choose “Continue without vices are added to the service container. Note how the Resources-
code” to launch the main screen of the Visual Studio 2019 Path property has been used to set the path to the folder where
IDE. I’ll choose the first option in this example. resource files (for various locales) will reside. If you don’t specify
any value for this property, the application will expect the re-
To create a new ASP.NET Core 3.0 MVC project in Visual Stu- source files to be available in the application’s root directory.
dio 2019, follow the steps outlined below.
There are three methods used to configure localization in
1. Start Visual Studio 2019 IDE. ASP.NET Core. These include the following:
2. Click on the “Create new project” option.
3. In the next screen, select “ASP.Net Core Web Applica- • AddDataAnnotationsLocalization: This method is used
tion” from the list of the templates displayed. to provide support for DataAnnotations validation mes-
4. Click Next. sages.
5. Specify the name and location of your project in the • AddLocalization: This method is used to add localiza-
“Configure your new project” screen. tion services to the services container.
6. In the “Create a New ASP.NET Core Web Application” • AddViewLocalization: This method is used to provide
screen, select .NET Core as the runtime and ASP.NET support for localized views.
Core 3.0 (or later) from the drop-down list at the
top. I’ll discuss more on each of these soon.
7. Select “Web Application (Model-View-Controller)” as
the project template to create a new ASP.NET Core MVC Define the Allowed Cultures
application. In .NET, you can take advantage of the CultureInfo class for
8. Uncheck “Enable Docker Support” and “Configure for storing culture-specific information. You should now specify
HTTPS.” You won’t be using either of these here. the languages and cultures that you would like your applica-

54 A Deep Dive into ASP.NET Core Localization codemag.com


• AcceptHeadersRequestCultureProvider is used to re-
Culture Name Description
trieve culture information from the Accept-Language
en-US English (United States) header of your browser.
en-GB English (Great Britain) • QueryStringRequestCultureProvider is used to re-
de-DE German (Germany) trieve the culture information from the query string.
• CookieRequestCultureProvider is used to retrieve cul-
de-CH German (Switzerland) ture information from a cookie.
fr-FR French (France) • CustomRequestCultureProvider is yet another re-
fr-CH French (Switzerland) quest culture provider that takes advantage of a del-
egate to determine the current information.
Table 1: A brief list of cultures
Note that the request culture providers are called one after
the other until a provider is available that’s capable of de-
tion to provide support for. To do this, create a list of type termining the culture for the request. You can specify both
CultureInfo to store the supported cultures. as shown below. the available cultures and the RequestCultureProviders us-
ing the app.UseRequestLocalization extension method.
List<CultureInfo> supportedCultures
= new List<CultureInfo> You can also define the default request culture. The follow-
{ ing code snippet shows how this can be achieved.
new CultureInfo("en"),
new CultureInfo("de"), services.Configure
new CultureInfo("fr"), <RequestLocalizationOptions>
new CultureInfo("es"), (options =>
new CultureInfo("en-GB") {
}; options.DefaultRequestCulture
= new RequestCulture("en-US");
Table 1 lists some of the widely used cultures. });

Next, add the request localization middleware in the Config- You should also use the UseRequestLocalization extension
ureServices method of the Startup class. method in the Configure method of the Startup class to set
the culture information automatically based on the informa-
services.Configure tion provided by the Web browser. The following code snip-
<RequestLocalizationOptions> pet illustrates how this can be achieved.
(options =>
{ var options = app.ApplicationServices.GetService<IOptions<Request
List<CultureInfo> supportedCultures = new LocalizationOptions>>();
List<CultureInfo> app.UseRequestLocalization(options.Value);
{
new CultureInfo("en-US"), The complete source code of the Configure method is given
new CultureInfo("de-DE"), in Listing 2.
new CultureInfo("fr-FR"),
new CultureInfo("en-GB")
}; Listing 1: Add the request localization moiddleware in the ConfigureServices method
options.DefaultRequestCulture = new
public void ConfigureServices(IServiceCollection services)
RequestCulture("en-GB"); {
options.SupportedCultures = services.AddControllersWithViews();
supportedCultures;
options.SupportedUICultures = services.AddLocalization(opt =>
{
supportedCultures;
opt.ResourcesPath = "Resources";
}); });

To add or remove localization providers, use the RequestLo- services.Configure<RequestLocalizationOptions>


calizationOptions class. Note that where a culture is used for (options =>
{
number and date formats, a UI culture is used for reading List<CultureInfo> supportedCultures =
culture-specific data from the resource files. The complete new List<CultureInfo>
source code of the ConfigureServices method is given in {
Listing 1 for your reference. new CultureInfo("en-US"),
new CultureInfo("de-DE"),
new CultureInfo("fr-FR"),
Working with Request Culture Providers new CultureInfo("en-GB")
The request localization middleware in ASP.NET Core pertain- };
ing to the Microsoft.AspNetCore.Localization namespace options.DefaultRequestCulture =
takes advantage of certain components to determine the new RequestCulture("en-GB");
culture of a particular request. These components are called options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
RequestCultureProviders and few are added by default. });
}
These pre-defined providers include the following:

codemag.com A Deep Dive into ASP.NET Core Localization 55


Listing 2: Using the UseRequestLocalization extension method in the Configure method (options =>
{
public void Configure(IApplicationBuilder app,
List<CultureInfo> supportedCultures =
IWebHostEnvironment env)
{ new List<CultureInfo>
app.UseStaticFiles(); {
app.UseRouting(); new CultureInfo("en-US"),
app.UseAuthorization(); new CultureInfo("de-DE"),
new CultureInfo("fr-FR"),
var options =
app.ApplicationServices.GetService<IOptions new CultureInfo("en-GB")
<RequestLocalizationOptions>>(); };
app.UseRequestLocalization(options.Value);
options.DefaultRequestCulture =
app.UseEndpoints(endpoints =>
new RequestCulture(culture: culture,
{
endpoints.MapControllerRoute( uiCulture: culture);
name: "default", options.SupportedCultures =
pattern: supportedCultures;
"{controller=Home}/{action=Index}/{id?}"); options.SupportedUICultures =
});
} supportedCultures;

options.AddInitialRequestCultureProvider
(new CustomRequestCultureProvider
Listing 3: The Custom Request Culture Provider (async context =>
public class MyCustomRequestCultureProvider : {
RequestCultureProvider //Write your code here
{ return new ProviderCultureResult("en");
public override async Task<ProviderCultureResult>
}));
DetermineProviderCultureResult
(HttpContext httpContext) });
{
await Task.Yield();
return new ProviderCultureResult("en-US"); Create a Custom Request
}
}
Culture Provider
Note that the culture providers AcceptHeadersRequest-
CultureProvider, QueryStringRequestCultureProvider, and
CookieRequestCultureProvider are configured by automati-
Listing 4: Add the Custom Culture Provider to the RequestCultureProviders list cally by default. You can also create your own custom cul-
services.Configure<RequestLocalizationOptions>(options => ture provider.
{
List<CultureInfo> supportedCultures = new List<CultureInfo>
{
Before you add your custom culture provider, you may want
new CultureInfo("en-US"), to clear the list of all the culture providers. Your custom
new CultureInfo("de-DE"), culture provider is just like any other class that inherits the
new CultureInfo("fr"), RequestCultureProvider class and implements the Deter-
new CultureInfo("en-GB") mineProviderCultureResult method. You can write your own
};
implementation, i.e., resolve the culture for a request in-
options.DefaultRequestCulture = new RequestCulture side the DetermineProviderCultureResult method as shown
(culture: "de-DE", uiCulture: "de-DE"); in Listing 3.
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders.Clear(); Listing 4 illustrates how you can clear all default culture
options.RequestCultureProviders.Add(new providers and add the custom culture provider to the Re-
MyCustomRequestCultureProvider()); questCultureProviders list.
});

Create Resource Files for Each Locale


Use the CustomRequestCultureProvider There are various ways in which you can create resource files.
Class In this example, you’ll take advantage of the Visual Studio
Resource Designer to create an XML-based .resx file. Select
You might often want to use a custom request culture pro- the project in the Solution Explorer Window and create a new
vider in your applications. Let’s say that you want to store folder named “Resources” in it. Resources in .NET are com-
the language and culture information in the database—this prised of key/value pair of data that are compiled to a .re-
is exactly where a custom request culture provider can help. sources file. A resource file is one where you can store strings,
The following code snippet illustrates how you can add a images, or object data—resources of the application.
custom provider.
Next, add a resources file into the newly created folder.
const string culture = "en-US"; Name the resource file as Controllers.HomeController.en-
US.resx. Create another resource file named Controllers.
services.Configure HomeController.de-DE.resx. Store the name value pairs as
<RequestLocalizationOptions> shown in Table 2

56 A Deep Dive into ASP.NET Core Localization codemag.com


Accessing a Localized String Resource File Name Name Value
in Your Controller Controllers.HomeController.en-US.resx GreetingMessage HelloWorld!
To access a localized string in the controllers of your ap- Controllers.HomeController.de-DE.resx GreetingMessage Hallo Welt!
plication, you can take advantage of dependency injection
Table 2: The name value pair for Controllers.HomeController.en-US.resx
to inject an instance of IStringLocalizer<T>. The IStringLo-
calizer and IStringLocalizer<T> interfaces were introduced
in ASP.NET Core to work with localized applications. In Replace the content of the Views/Home/Index.cshtml file
other words, the IStringLocalizer and IStringLocalizer<T> with the following:
abstractions in ASP.NET Core can be used to retrieve local-
ized data based on the names of the resource. The IString- @{
Localizer interface takes advantage of the Resource- ViewData["Title"] =
Manager and ResourceReader classes (pertaining to the @ViewData["Title"];
System.Resources namespace) to provide culture-specific }
information.
<div class="text-center">
The following code snippet illustrates how you can retrieve <h1 class="display-4">
a localized string from IStringLocalizer<T>. @ViewData["Title"]</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">
string localizedString = building Web apps with ASP.NET Core</a>.</p>
_localizer["Hello World!"]; </div>

Here’s how it works. It searches for a resource file matching Listing 5 illustrates how you can access localized string in
the current culture. If it finds one and there’s a matching your controller.
row with the Name as “Hello World!”, it returns the Value. It
then searches for the parent culture and if a value is found, When you run the application, the message “Hello World” will be
it returns that. As a fallback, the string “Hello World!” is re- displayed in the Web browser, as shown in Figure 1.
turned if there’s no match. Culture fallback is a behavior in
which if the requested culture is not found, the application Now specify the culture you’d like the application to use in the
selects the parent of that culture. URL as shown here: http://localhost:1307/?culture=de-DE

Figure 1: The text “Hello World” displayed using a default locale in the Web browser

Figure 2: The text message “Hello World” is displayed using the German locale in the Web browser

codemag.com A Deep Dive into ASP.NET Core Localization 57


Listing 5: Accessing localized strings in the controller ViewData["Title"] = Localizer["PageTitle"];
}
public class HomeController : Controller
<h2>@ViewData["Title"]</h2>
{
private readonly IStringLocalizer<HomeController>
_stringLocalizer; You can have localized views—separate views for each cul-
ture, i.e., different razor files per culture. Setting this up is
public HomeController(IStringLocalizer<HomeController> quite straightforward; here’s what you need to specify in
stringLocalizer)
{ the ConfigureServices method:
_stringLocalizer = stringLocalizer;
} public void ConfigureServices
(IServiceCollection services)
public IActionResult Index()
{ {
string message = services
_stringLocalizer["GreetingMessage"].Value; .AddMvc()
ViewData["Title"] = message; .AddViewLocalization
return View();
} (LanguageViewLocationExpanderFormat.
//Other action methods Suffix);
} }

Next, you create a view for each culture such as About.en-


Remember, you’ve already added the list of supported cul- US.cshtml, About.en-GB.cshtml, About.de-DE.cshtml, etc.
tures and this is one of them. When you run the applica-
tion, you can observe the string “Hello World” in German, as
shown in Figure 2. DataAnnotations Localization
DataAnnotations is a feature in .NET (introduced in .NET
3.5) that enables you apply data attributes on your mod-
Using Localization in Views els. You can localize data annotation error messages with
You can localize a view in two different ways: Either localize the IStringLocalizer<T> as well.
entire view or localize strings, much the same way you would
do when localizing strings in a controller (as discussed in the Now consider the following class.
preceding section). Note that when localizing views, you should
take advantage of IViewLocalizer in lieu of IStringLocalizer<T>- public class AuthorViewModel
-you should inject IViewLocalizer into the view. To inject IView- {
Localizer into your view, you can use the following code. [Display(Name = "FirstName")]
[Required(ErrorMessage =
@inject IViewLocalizer Localizer "{0} is required")]
public string FirstName { get; set; }
Remember that the ViewLocalizer class inherits HtmlLocal-
izer and is used in razor views. You can take advantage of [Display(Name = "LastName")]
the Localizer property in your views as shown in the code [Required(ErrorMessage =
snippet. "{0} is required")]
public string LastName { get; set; }
<p>@Localizer["PageTitle"]</p>
[Display(Name = "BooksAuthored")]
The following code snippet illustrates how IViewLocalizer [Range(1, 99, ErrorMessage =
can be used in the Index.cshtml file. "{0} must be a number between
{1} and {2}")]
@using Microsoft.AspNetCore.Mvc.Localization public int BooksAuthored { get; set; }
@model AddingLocalization.ViewModels. }
HomeViewModel
@inject IViewLocalizer Localizer The ErrorMessage that you see for each of the Validation-
@{ Attribute is used as a key here to search for the localized

Figure 3: The Resource file, as viewed in Visual Studio Resource Editor

58 A Deep Dive into ASP.NET Core Localization codemag.com


Listing 6: Specifying the RouteDataRequestCultureProvider in the Configure method
public void Configure(IApplicationBuilder app, SupportedCultures = supportedCultures,
IWebHostEnvironment env) SupportedUICultures = supportedCultures
{ };
app.UseStaticFiles();
app.UseRouting(); var requestProvider = new
app.UseAuthorization(); RouteDataRequestCultureProvider();
requestLocalizationOptions.RequestCultureProviders.
IList<CultureInfo> supportedCultures = new Insert(0, requestProvider);
List<CultureInfo>
{ app.UseRequestLocalization(requestLocalizationOptions);
new CultureInfo("en-US"),
new CultureInfo("de-DE"), app.UseEndpoints(endpoints =>
new CultureInfo("fr"), {
new CultureInfo("en-GB") endpoints.MapControllerRoute(
}; name: "default",
pattern: "{culture=
var requestLocalizationOptions = new en-US}/{controller=Home}/{action=Index}/{id?}");
RequestLocalizationOptions });
{ }
DefaultRequestCulture = new RequestCulture("en-US"),

message as appropriate. Note how the RangeAttribute has RequestCultureProviders. SPONSORED SIDEBAR:
been used to specify the minimum and maximum number of Insert(0, requestProvider);
books authored by an author. Get .NET Core Help
Listing 6 shows the complete source code of the Configure for Free
Next, you create a resource file. Let’s create one for the Ger- method for your reference.
man locale and name it Models.AuthorViewModel.de-DE. Looking to create a new
resx with the content shown in Figure 3. project in .NET Core or ASP.
Summary NET Core? Get started with
a FREE hour-long CODE
For DataAnnotationsLocalization to work, call the AddData- ASP.NET Core and ASP.NET Core MVC provide excellent sup-
Consulting session to make
AnnotationsLocalization() method. If you’re using ASP.NET port for internationalization. Implementing it in your appli- sure you begin on the right
Core or ASP.NET Core MVC 2.0 or 2.2, here’s what you need to cations isn’t difficult either. You can take advantage of the foot. CODE consultants have
specify in the ConfigureServices method of your Startup class. built-in support for globalization in ASP.NET Core and ASP. been working with the .NET
NET Core MVC to build applications that can cater to vari- Core and ASP.NET Core teams
services.AddMvc(). ous locales. ASP.NET Core provides support for globalization since the early pre-release
AddDataAnnotationsLocalization(); through the Microsoft.Extensions.Localization assembly. builds. Leverage our team’s
services.AddLocalization(o => experience and proven track
{  Joydip Kanjilal record to make sure your
o.ResourcesPath = "Resources";  next project is a success.
}); No strings. No commitment.
For more information, visit
Here’s what to specify in the ConfigureServices method if www.codemag.com/
you’re using ASP.NET Core or ASP.NET Core MVC version 3.0 consulting or email us
or upward. at [email protected].

services.AddLocalization(opt =>
{
opt.ResourcesPath = "Resources";
});

services.AddControllersWithViews()
.AddDataAnnotationsLocalization();

Using the RouteDataRequestCulture


Provider
You can also specify culture as part of the URL, as shown
here: http://localhost:1307/en-US/Home/Index and here:
http://localhost:1307/de-DE/Home/Index.

To do this, you might want to take advantage of the Route-


DataRequestCultureProvider—yes, you read it right! The
following code snippet illustrates how you can set up this
provider in the Configure method of the Startup class.

var requestProvider = new RouteDataRequestCultureProvider();


requestLocalizationOptions.

codemag.com A Deep Dive into ASP.NET Core Localization 59


Dr. Neil Roodyn and
Talk to an RD Markus Egger
In this “Talk to an RD” column, Markus Egger and Dr. Neil Roodyn, two RDs sharing
The Microsoft Regional a long history and friendship, sat down virtually and had a rather thought-
Director Program provoking discussion about the impacts of the COVID-19 crisis. Both are involved
The Regional Director Program, or RDs for short,
with managing teams that are now working remotely more than before. Both are
is a program that allows Microsoft to identify involved with customers facing the same issues. Let’s listen in.
influential individuals in an effort to give the
community-at-large access to these individuals,
and also to provide a point of communication Markus Egger: How are things in Sydney? rules and have seats spread out. You can sit at a
and feedback into Microsoft. Regional Directors table with the people you’ve come with as long
do NOT work for Microsoft (and they aren’t Dr. Neil Roodyn: It’s sunny in Sydney. as it’s a group of 10 or less, but you have to be
paid for being part of the RD program), but I’m near the beach, which is a tough place to be. further spread out from other people than you
they have a formal relationship with Microsoft [laughs] Overall, in the scheme of where we are normally would be.
that provides them with great insights and
at this point, more than halfway through 2020,
connections within Microsoft.
the Australian solution works out reasonably well Markus: Sounds very similar to how it is here
The Microsoft Regional Director website for a lot of the challenges we’ve been encounter- [Markus was in Maui at the time of this conversa-
(https://rd.microsoft.com) defines the RD ing this year. I think it’s important to remember tion]. We’re in a mode where a lot of places are
program in the following words: that Australia really feels like we’ve been beaten allowed to open, but almost none of them are.
up badly. We had fires and flooding and now the Something like 95% of restaurants that can open
“The Regional Director Program provides Microsoft pestilence. are still closed. In fact, some of the ones that
leaders with the customer insights and real- already opened up have closed down again.
world voices it needs to continue empowering Markus: We have some CODE people in Australia.
developers and IT professionals with the world’s It feels like we’ll be talking about locusts next. Dr. Neil: It’s similar here. A lot of places are still
most innovative and impactful tools, services, closed because it’s not economically viable to
and solutions. Established in 1993, the program Dr. Neil: Right! I guess the final thing is taking open a restaurant. If you can only fit four tables
consists of 160 of the world’s top technology the firstborn males. But let’s not get too side- into a restaurant space that previously fit 10,
visionaries chosen specifically for their proven tracked before we even get started [laughs]. All and you still have to have waitstaff and chefs, it
cross-platform expertise, community leadership,
things considered, life is moving along. There’ve just doesn’t make sense. For some of the bigger
and commitment to business results. You will
been a lot of changes. People are learning to places, I think it makes more sense and they seem
typically find Regional Directors keynoting at
top industry events, leading community groups
work different ways. to be doing quite well. There’s also a certifica-
and local initiatives, running technology-focused tion process that the state government provides
companies, or consulting on and implementing Markus: Are you completely stuck at home? Can here [the New South Wales state government
the latest breakthrough within a multinational you go out and visit beaches and exercise and in Sydney] saying that a restaurant passed the
corporation.” such? Coronavirus Safe Location test. And that means
they’ve validated that the staff has been trained
Regional Directors are expected to have deep Dr. Neil: Yes, we can go out here in Sydney. The on how to keep everything clean, how to handle
technical understanding of many of the Microsoft government is now even trying to encourage people, how to make sure people are not stand-
technologies. Not just that, but RDs are expected people to go back into offices, but they are at the ing too close together and are maintaining social
to have an understanding of, and experience same time saying “if you can work from home, distancing, and all the rest of it. You have to put
with, competing technologies. RDs are also work from home.” So there’s a little bit of mixed in place hand sanitizers, access to sinks with hot
expected to go beyond technical expertise and messaging in there—there has been through this water, and so forth, to ensure proper hygiene.
have considerable business expertise. Many RDs whole process. You’re told that you can go out to They also do the same for other places, like gyms.
present at corporate events, advise governments the beach and exercise as long as you’re on your
and NGOs, and many similar scenarios. own or just with the people you live with. But But I think for desk workers, people who can work
Feel free to contact any of the Regional Directors then when you get to the beach, there’s a huge from home need encouragement and support to
to get access to an RD’s expertise and a well- sign saying “beach closed.” So are you allowed be allowed to work from home.
informed opinion that isn’t shaped or influenced to or are you not allowed [to go to the beach]?
by having to go along with Microsoft’s marketing [laughs] Then you call the council and they say, Markus: I imagine it’ll be somewhat of a lasting
speak. You can contact RDs to get advice and “you’re allowed to, but the sign means you can’t change going forward.
opinions on your projects regarding technical just sit on the beach, and you can’t just go and
needs. You can contact them to help analyze play in the water. If you’re exercising, that’s fine. Dr. Neil: It certainly feels that way.
how technology will influence your business. And there’s a one-hour limit.”
You can invite RDs to speak to your project Markus: You know, when you think back a dozen
stakeholders, board of directors, or corporate That’s all passed now. We’re now allowed to do years to the last big crisis, we optimized a lot of
event. And you can contact your RD for many bigger group activities up to 10 people. It seems things. Perhaps we’ve gotten a little lackadaisical
more scenarios. to be more and more relaxing. Restaurants are about that, but all in all, I feel we did optimize a
opening as long as there are social distancing lot of things. So now we’re in this new crisis and

60 Talk to an RD: Dr. Neil Roodyn and Markus Egger codemag.com


we’re all asked to optimize again. What can we op- Dr. Neil: It is! And it’s also a separate intellec- [CODE Custom Software/Consulting and CODE
timize? It seems to me a) the expense and b) the tual bubble. They may not get the intellectual Magazine], we used to be very traditional brick
overhead of having the commute to work. If you stimulation at home in the same way they get at and mortar, but over the last six to eight years,
eliminate that, that can be a big productivity gain. work, but they don’t get the emotional stimula- we’ve transitioned into a virtual company. We
I’ve now been pondering this for a while. That tion at work the same way they get a home. They still have a nice office. In fact, we just built out a
seems to be a big area of potential improvement. get pieces of what they need to fulfill themselves whole new office that we haven’t gotten to use,
from different places in their lives. I think that’s but overall, we’ve been set up to be virtual and
Dr. Neil: I agree. I think it cuts two ways. One very, very normal. What I see is that people are we routinely do things like a virtual cocktail hour
of the things I’ve been doing is trying to help missing out on pieces of that, where they can’t go where you have a screen full of different video
different teams move to this culture. Historically, for lunch with someone who’s super interesting feeds. And that certainly would have been very
I’ve always been very big on development teams and have a really deep conversation with them difficult to do not very long ago.
working in the same space and co-located be- about some technical aspect or even something
cause conversations happen that aren’t planned unrelated to their work. But there are other as- Dr. Neil: I think it’s the ad hoc, coffee machine,
that can add huge value. You overhear someone pects. They may be computer programmers in water cooler conversations, the overhearing
talking about some weird COM pointer error and the day, but very interested in ancient history other conversations where people may be strug-
you go “oh yeah, I used to know about that stuff. and they go and have some conversation around gling with something that you could help with, or
Let me see if I can help.” It’s those kinds of tran- something they were reading. They miss that you’re just interested in finding out what the so-
sient activities that are not well planned that ac- connection with each other [when they only work lution was. You may overhear something and go
tually can be incredibly valuable. from home]. I think there are both business and “well, I don’t know what it is.” And then the next
human aspects that we lose from being physically day you overhear that the problem was solved
There are also people who really enjoy going to disconnected. and you wonder how. And he says, “oh, well, Sue
work and being with their work peers in a world helped me because she had done it before.” And
that’s very different from their home life. You go At the same time, I think we’re amazingly lucky then you can find out an answer to something
home and you’ve got screaming children, or dogs that this happened at this time and not 15 or 20 that you wouldn’t necessarily know existed if you
that need feeding, or a spouse that needs your years ago because of the technology we have. weren’t in the same environment.
support and your love. It’s a different environ- This discussion between the two of us is a good
ment. This separation of work life and home life— example. You’re in Hawaii. I’m in Sydney. We’re Markus: It’s quite interesting when I think of my
for some people—is very much how they’ve lived having this real-time video chat conversation. We own company’s set up. We went through consider-
and how they enjoy it. couldn’t have done this to any degree back then. able effort and expenses to bring people togeth-
er a few times a year for no particular business
Markus: It’s a whole separate social bubble. Markus: And we can do it with a large group of reason, but just to get that personal connection
That’s a very interesting point. people and in larger teams. For my organization going. And we very specifically invested in that.

In this feature, we eavesdrop on a conversation between Dr. Neil Roodyn and Markus Egger, both seasoned Regional Directors.
Talk to Dr. Neil about: Talk to Markus about:
Software development, cloud computing, machine learning and AI, ASP.NET, HTML apps
More Personal Computing, cognitive services, (including Angular, Vue.js, etc.), the cloud,
bots, conversation as a platform, true social Azure, Microservices, Windows applications,
computing, robotics, quantum computing, software strategy, and much more.
the economic singularity, and the future. You can contact him at [email protected]

Dr. Neil Roodyn has long had a passion for software development, going Markus Egger is not just an RD, but as the founder and publisher of CODE
back to the 70s when he taught himself BASIC and 6502 Assembler. In the Magazine, he’s also directly associated with this publication. In his main
90s, Neil worked on a number of different real-time systems that led to a job, he is the President and Chief Software Architect of EPS Software Corp.
thesis entitled “Software Architectures for Distributed Real-Time Systems.” (a company better known for its CODE brands such as CODE Consulting,
Neil was awarded a PhD for this thesis from University College London and CODE Training, CODE Staffing, and CODE Magazine). He is also the founder
the brand of Dr. Neil was born. Since 1995, Dr. Neil has been involved in the of other business ventures, such as Tower 48, Wikinome, and others. In his
formation of many software companies, his roles varying from mentoring own words, he spends his time “like most software developers, writing
through to directorship. Dr. Neil keeps a very active involvement in the production code” both for consulting and custom app-dev customers,
software being produced and delivered today, and provides feedback and also for his own companies. He has worked for some of the largest
to both large and small companies on how to increase the value of their companies, including many Fortune 500 companies, such as Microsoft.
software business. Markus often takes on the role as a “CTO for hire.”

codemag.com Talk to an RD: Dr. Neil Roodyn and Markus Egger 61


TALK TO AN RD

Just before this crisis emerged, we did a small One interesting observation that I’ve made is that The other tool that I use very heavily in Teams—
internal conference where we brought everyone some people who were very reticent or introvert- even though it’s technically outside of Teams—is
out to Hawaii, for instance. We do it at different ed, who didn’t want to go and talk to people be- Whiteboard. When you’re working with a team
locations at least once a year. I have no idea how fore in the physical world and are now much more and you want all the people to engage, White-
that’s going to continue in the future because we vocal in the virtual world because everybody is board works really well. I love using a pen. I’m
found that super valuable. Like you discover that virtual. Now they can get in a chat room and they a stylus kind of person and I’ve always been into
your coworker has the same hobby as you do, and can talk about this, that, and the other and blah, the digital ink world.
now you have a completely different connection. blah, blah, blah.

But to switch gears a little: I know you’ve been Markus: Do you see those people communicate
very involved with things like eXtreme Program- more in writing, such as chats, or oral and aural? We get a lot of use out
ming going way back. I know you’re involved with of Whiteboard in
sizable organizations where you’re now trying to Dr. Neil: Both. With a large enough group of peo-
move some of those things that were tradition- ple, you’re going to start to see these different combination with
ally more of a “let’s do a standup in a room” ap- aspects of how people react. To some people, it’s MS Teams, especially when
proach, and now you’re helping organizations not so scary now that they’re on the other end of
doing that digitally. a screen rather than in-person. They feel more used with a digital pen.
comfortable talking to each other. And that’s in-
Dr. Neil: Most people have a scrum or an XP, like teresting too, because maybe that’s the kind of
standups or doing a retrospective, as regular person that would be better spending more days Markus: Which is why we’ve known each other for
things in their calendar, where they play a plan- working from home in the future, and maybe only ages.
ning game or work out what the next sprint is come into the office once or twice a week when
going to look like, or however they’re doing it. we go back to that normality. If we get back to Dr. Neil: That’s right! We first met in the Tablet
Those aren’t so challenging. It really wasn’t that that normality. Other people really miss being in PC era.
much of a big deal to get to “this is MS Teams the office. We just had a long weekend because
and this is how you use it and this is how you Monday was a holiday. And I know on Friday there Markus: That’s right. We worked on digital ink
use a whiteboard in Teams. This is how you share were some people, even though we’re already and the first handwriting recognition.
it.” Getting that kind of stuff up and running was seven months into this, who were going “well,
pretty quick and easy. what am I going to do on Monday?” Dr. Neil: I don’t think it was the very first [use of
handwriting], but it was the first on a Microsoft
In fact, there was one group of a couple of hun- Dr. Neil: Yes [laughs]. There is some of that. It’s tablet. On Windows XP. [Laughs] Microsoft had a
dred developers that moved from working in the also about teaching people that the informal ac- Pen for Windows that shipped on Windows 3.1 It
same building or same campus space. In the past, tivities are as valuable and sometimes more valu- was an additional product.
they could all, within a short walk, get to each able than the formal activities.
other. We were able to move them to an online Markus: It was a little bit akin to the Apple New-
virtual workplace within a week. They’re all tech- Markus: Do you promote or use specific tools for ton, probably.
nically capable, so obviously, they have a starting that or a mixture of tools? I know you’re using a
point. But a lot of them had not used the technol- lot of MS Teams. We use MS Teams and we also use Dr. Neil: Yeah. Kind of. It was in that era.
ogy that we’re now using every day. I think the Slack, Zoom, GoToMeeting, and others. We have a
more difficult aspect was trying to solve for how whole set of tools that we use and I often wonder Markus: You got a lot of use out of that, I take
people connect with each other more readily. if we shouldn’t standardize more or whether we it. [laughs]
should stick with letting people choose what they
Any group of people of any size is going to have feel they can be most productive with. Dr. Neil: Ummm....not so much. [laughs]. But it
some extroverts that have no problem with click- was interesting to me. I was attracted to it then
ing the call button and randomly just talk to Dr. Neil: I’ve become a religious follower of MS because I’m the kind of person who thinks better
people. And then there are some introverts that Teams. I was using Zoom and BlueJeans for a cou- with a pen in my hand than I do with my hands on
aren’t really comfortable contacting someone ple of other teams and I got frustrated with them, a keyboard. It helps me think about what’s going
by pushing a button and feel they want to know partly because the platforms failed to scale very on and get my ideas down if I can just sketch.
whether that person is available to talk to or not. quickly when suddenly everyone was working from
All the social cues that used to be there because home in the first couple of weeks. I just thought, We use Microsoft Whiteboard a lot. It runs on
you could look over and see that they’re at their “who really has the ability to cope with this level any Windows device and it runs on iPad. If you
desk or going for a coffee are gone. I always tell of scale in infrastructure?” It seemed, through trial have a pen, you get the extra bit of nice func-
them “just don’t worry about it. They can always and error, that MS Teams was the one. I know a lot tionality where you can engage. I think the other
not answer.” In fact, even while I’m talking to of people complain about it, but I really haven’t nice thing about Whiteboard is that, as different
you, I’ve got another screen here and someone had issues. I can quite easily swap among five ten- people are scribbling on the same whiteboard at
from another team has pinged me. I just said, ants each day, having multiple calls and conversa- the same time, you see their little icons popping
“I’m on a call.” Easy. tions, and just not struggle with it. up all over the screen.

62 Talk to an RD: Dr. Neil Roodyn and Markus Egger codemag.com


TALK TO AN RD

You can track who’s doing what. You can see a Dr. Neil: I think that’s super interesting, espe- in Europe. Whatever. But productivity output is
bigger picture emerge as multiple people scribble cially in the kind of businesses that you and I not necessarily proportional to that. Whatever
down. You can divide the page into columns and are in, which are really creative businesses. Cre- the case might be, we think of it as a 40-hour
people can enter stuff in their own columns, or ative output isn’t something that you measure week. When you really think about it, the aver-
however you want to do it. You can create notes by how much time is spent sitting in front of age worker probably spends about 60 hours on
and move them around on the page. I found that the screen. One of the most incredible develop- things related to work, whether it’s the fact that
a very useful free-form tool in the same way I’ve ers I ever worked with probably spent more time you had to drive in to work or take an hour for
always liked having a whiteboard in a working on his back on a sofa than he did typing on the lunch. There was really no reason to be in town
space when I’m in the same room as someone. computer. He did a lot of thinking and then he for lunch other than work. Or the fact that we
It encourages that wider conversation. I think would do some furious typing and then he would had to drop the kids off at daycare. That takes an
sharing screens fluidly and being able to share go back to thinking. And his code was insanely hour a day. I think 60 hours is much closer to the
screens between different people in the con- good, insanely high quality, with very few defects real number that people spend “at” work. If you
versation is also very powerful. People want to ever found in the code that he wrote. But he’s not say, “let’s reduce that to 50, but we get an extra
share, whether it’s a new UI they’re designing or someone you’d measure based on how many lines 10 productive hours out of you,” that’s a win-win
they want to have a conversation around some of code he’d written or how many hours he spent for everyone.
plan they’ve put together. Just being able to do in front of the keyboard.
that in Teams has been powerful. Dr. Neil: Yeah. Although again, it depends on
You’re not typing a document that you already the kind of work you’re doing. There’s work with
Bringing in guests is also very powerful. Know- know or that’s being dictated into your head. intention where I’m actually sitting here at my
ing that you can lock MS Teams down to people You’re thinking of new ways of solving problems. desk, thinking about a problem, trying to fix
within your organization, but you can have teams That’s a very hard thing to measure in hours any- something, trying different things, coding dif-
where you can bring in guests in a controlled way. If you’re of the mindset that you need to ferent tests, making them pass. And then there’s
fashion is another aspect of it. There are lots have people sitting behind their desk for a cer- “sleep programming,” if you want to call it that,
of pieces of the puzzle that connect together to tain period of time, that’s probably based on where I’m away, but my brain hasn’t totally
make what I’ve found to be a very useful toolkit some incorrect assumptions to start with. Even switched off. Although I’m not consciously think-
that MS Teams has presented. with pure data entry, you’re not really getting the ing about this particular problem I’m trying to
most value out of workers that way. solve, my unconscious is still working through it.
Markus: I believe you can use the Whiteboard Software development, if you’re really into the
product whether you use Teams or not, is that Some of the teams I’ve been working with—and project that you’re building, is the kind of thing
correct? I like to think that it has to do with me support- that you might wake up at two in the morning
ing them over the years—had in place a bunch and go “that’s what I need to do!”
Dr. Neil: Yes. The Whiteboard product isn’t at- of measurements that help them understand how
tached to Teams. It’s a separate product. It’s at- their dev team was doing. It could be things like So what do you do? Do I bill my customer from
tached to Active Directory. Most of the organiza- defect counts. You know, very simple high-level 6:00 p.m. until two in the morning where it was
tions I work with are enterprise-scale organiza- things like “how many defects do we have report- ticking in the back of my head even though I
tions. They have their own Office365, so we work ed a month?” or “how quickly are we fixing these played the guitar and watched TV and put the
with those inside their firewall, logged in as an defects?” or “what’s the severity of the defect?” kids to bed or whatever? Do you bill for that as
AD under their domain. Is it severity zero where the whole system doesn’t well? Clearly something was happening in the
work? Is it severity 12 and it really doesn’t mat- back of your brain that made you wake up at two
Markus: Most people already have a license for it ter if no one ever fixes it? Just measuring those in the morning with that sudden breakthrough
because it’s part of Office. things is a good starting point, but there are idea that’s worth a lot. It’s interesting that so
other things you can measure. many companies measure software development
Dr. Neil: Right. Microsoft made big move to make by those traditional 40-hour weeks.
it free for everyone during this period. So that’s Teams that already had a lot of measurement
made it useful too, in that if I have a team that’s in place when they moved into work from home I’ve had lots of conversations with different prod-
not necessarily hooked into the Microsoft ecosys- transitioned quite smoothly because their pro- uct teams and senior leaders about this over the
tem, I can add them as guests into my domain ductivity was very clear. We saw some spikes in years, because I think it’s very broken. We want
and bring them in to conversations. productivity initially, which was interesting. solutions, outcomes. It’s not a new thing. Google
People were like “oh, well, I don’t have to com- has been doing it for a very long time. Actually,
Markus: That brings us right back to people work- mute now. I’ll just do an extra half hour or an it originally came from Andy Grove and Intel. The
ing from home and wanting it to stay that way, extra hour. I’ll be sitting in my pajamas coding, “objectives and key results” concept, where you
which gets into discussions such as how you know but who cares?” And all of that turned out to be have high-level objectives for your year and you
what your employees are up to at what point in a positive. have a set of key results that you’re measured on,
time, and then how much do you want to know maybe quarterly. If you have the kind of business
and how much should you know, and how do Markus: I find that part highly interesting, be- where you can set out those bigger goals and
you monitor productivity and productivity versus cause we tend to think of a work week as roughly have some measurements over time, you can give
hours worked. What do you make of all that? 40 hours. A little more in the US, a little less people much more freedom because they under-

codemag.com Talk to an RD: Dr. Neil Roodyn and Markus Egger 63


TALK TO AN RD

stand “this is what I’m being measured on” and such as “who am I collaborating with?” or “who prior to this situation, because you were already
they can go away and think about it. am I talking to?” Also, how much time have I moving to a set-up where you wanted the ability
spent in meetings versus my focus time? The to hire the best people no matter where they were
If that means the best way for them to do that is question is: How do you measure those things? located. You were in Hawaii and you had people
to lie on the floor and stare at the ceiling for two in Texas and in Europe, and so you were already
weeks while they think about the problem and Markus: And how well do those tools integrate all moving to this more distributed set-up.
then furiously solve something and then lie on the things you do versus just the Microsoft view
the floor for two weeks and furiously solve some- of the world? Markus: It was out of necessity back then as well,
thing, that’s fine. And there are different types because we simply wanted to hire good people
of people who’d prefer to slowly plod their way Dr. Neil: As we start getting more of this into from around the world and certainly across the
through the activity to get to those results. Mea- dashboards, we can start rolling it up into big- US, and people were less and less excited about
suring those goals and the success of reaching ger pictures of what people are doing. It will al- moving to Houston.
those goals is much more valuable than measur- low us to understand that “oh, this person is a
ing how many lines of code he wrote or was she communicator and look how much time they’re Listen, I used up a lot of your time already. I
typing today or did she open Visual Studio today? spending, helping other people online in Visual don’t want to take away from your productivity,
Who cares, as long as you’re getting the result Studio.” In Visual Studio Live Share, I can track although maybe in the back of your mind, you’re
that you’re looking for? Maybe you didn’t open how many people in a dev team use it, how many thinking about other development problems and
Visual Studio for two days, but then maybe you hours they used it, and what kind of activity they solved them by now. [laughs].
coded solid for 70 hours. How do you measure did. Am I seeing better results now that they’re
that? There are people who work that way. I think using Live Share? I think there’s a lot of inter- Dr. Neil: [laughs] Actually I’ve just finished this...
productivity is a very difficult thing to measure. esting stuff that we already have the ability to no. Just kidding.
You need bigger goals and you need different capture. As we start doing that, we’ll find other
levels of measurement. The teams that did well aspects of what we’re doing that are just as im- Markus: You and I used to get together in person
in their initial move to work-from-home were the portant. all the time and have great discussions. I miss
teams that had more things being measured. that. Hopefully we’ll be able to get back to that
again.
Markus: That’s definitely something that will be
interesting to watch over the next few years. You The reality is that when we Dr. Neil: I think we will. It’s going to change and
do need to measure as a business. We can’t just it’s probably going to be a lot less often going
trust all workers to be great. You need to evalu-
all worked in a single office, forward, because a lot of us are being a lot more
ate it and make sure you get the kind of results I had less of an idea of how cautious about travel too.
that you want. As a business owner, I don’t have productive my team was
any silver bullets at this point. I certainly have a Markus: Maybe. Maybe we’ll start valuing those
pretty good feel for how some of my people are than I do in a distributed, travel opportunities more as well. Maybe my days
doing at home. virtual setup today. of 48 hours in Australia can change to once a year
and stay a month or two instead.
I think it depends a lot on the roles people play.
For me, at least as a manager, keeping track is Dr. Neil: I’ve often valued spending more time in
much easier with creative people such as soft- Markus: One aspect that I find interesting is that places than just going for a week or two weeks.
ware developers. I also run other businesses—a people tend to think that because of the situa- By being somewhere for two or three months, you
magazine, an escrow company, and a few things tion we’re now in, all of this is going to change get a lot more value than just rushing off again
like that. How do I evaluate the performance of because everybody wants to work from home, and like we used to do.
my customer service department working from that reduces the ability to manage and oversee.
home? By customer satisfaction, probably, but But when I think back to the days where we liter- Markus: It’s definitely true. Well, anyway, thank
then how do I measure that whole interaction ally worked just a brick and mortar company with you very much for your time. This was an excit-
exactly? I think that’s an area where there’s huge everybody in one building across multiple floors ing talk.
potential for optimization for businesses going and everybody went to work in the morning in a
forward. very traditional set up, I probably had less of an Dr. Neil: Absolutely. Awesome.
idea as to how productive people were or whether Thank you very much too.
Dr. Neil: You use Office365, right? Do you get a they were browsing the Internet while at work
report every two weeks or a monthly report of than I do today.  Markus Egger
your activities in Office? 
Dr. Neil: Yeah. I think that’s true. A lot of that
Markus: Yeah. depends on how your team engages. It’s about
whether they’re the kind of people who will reach
Dr. Neil: I think those kinds of things are already out to you because you’re not in the office. I think
starting to be built into the tools. You get things your culture had already started to adapt to that

64 Talk to an RD: Dr. Neil Roodyn and Markus Egger codemag.com


UR
GET YO R
O U
FREE H

TAKE
AN HOUR
ON US!
Does your team lack the technical knowledge or the resources to start new software development projects,
or keep existing projects moving forward? CODE Consulting has top-tier developers available to fill in
the technical skills and manpower gaps to make your projects successful. With in-depth experience in .NET,
.NET Core, web development, Azure, custom apps for iOS and Android and more, CODE Consulting can
get your software project back on track.

Contact us today for a free 1-hour consultation to see how we can help you succeed.

codemag.com/OneHourConsulting
832-717-4445 ext. 9 • [email protected]

codemag.com
ONLINE QUICK ID 2009101

Interactive Unit Testing with


.NET Core and VS Code
In this issue, I continue discussing testing JavaScript in VS Code with an introduction to interactive testing in the context of
.NET Core 3 in VS Code. In November 2020, .NET 5 will be upon us and it’s crucial to keep our knives sharpened on the latest
technology from Microsoft. How do you incorporate .NET Core/.NET 5 and VS Code in your toolbox? The answer is simple,

you learn by doing. .NET, as we know it, has had a good run. you can download and install as well as use their learning
It’s been 18 years. resources to get you up and running.

Step 1: Install VS Code Step 2: Create Your Project Folder


In order to work through the examples in this article, you’ll Visual Studio is a very feature-rich IDE. Some may say that
need to have Visual Studio Code installed on your computer. it’s too feature rich! It’s not an exaggeration to say that
If you aren’t familiar with Visual Studio Code, you can learn most Visual Studio users only use a fraction of features
about it here: https://code.visualstudio.com/. From there, (maybe 20-30%). VS Code’s goal is to be a lean editor where
John V. Petersen
[email protected]
linkedin.com/in/johnvpetersen
Based near Philadelphia,
Pennsylvania, John is an
attorney, information
technology developer,
consultant, and author.

Figure 1: VS Code automatically installs a context menu choice for easy launching from the File Explorer.

Figure 2: VS Code provides ready access to release notes for the version installed on your workstation.

66 Interactive Unit Testing with .NET Core and VS Code codemag.com


Figure 3: Out of the figurative “Box,” VS Code only contains
a handful of functions. This is why VS Code is considered an
editor, as opposed to an IDE, like Visual Studio.

you have the things you plan to use through extensions. It’s
also fair to say that how you approach the VS Code tool will
be quite different from how you approached Visual Studio.

I prefer VS Code because it eliminates the clutter of unhelpful Figure 4: For many of the things that are automatically available in Visual Studio, you must
abstractions. Do I find myself handing some things manually install an extension in VS Code.
and working with the command line more? I absolutely do. I
don’t see that as taking a step backward. Rather, I see it as re-
fining how I perform my work in a way that allows me to focus
directly on the work. To that end, wherever you choose, create
a folder named UnitTestingInNETCore for your workspace. Once
you’ve created the folder, navigate to that folder in File Explorer,
right click, and select Open with Code, as illustrated in Figure 1.

Step 3: Reviewing the VS Code Interface


Figure 2 illustrates how your sessions should appear after
an initial install or an update. VS Code is updated regularly
and, unlike the friction that comes with Visual Studio up- Figure 5: The test explorer extension installs a new icon for easy access.
dates, VS Code updates occur in a matter of seconds!

If you’re used to working with Visual Studio, the VS Code en- fishing rod. You already know how to fish. You just need
vironment looks quite different. Instinctively, you may start guidance on how to use a different tool.
clicking around looking for things that automatically appear
in Visual Studio. More likely than not, you won’t find what One of the things that grabs attention is not what’s in VS
you’re looking for and this may frustrate you. Be patient! Code, but what isn’t. There’s no clutter. VS Code’s entire prem-
Think of this exercise as teaching yourself how to build a ise is based on letting you choose what you need for your proj-

Figure 6: Many of the tasks you carry out in VS Code, like creating new projects, are done via a PowerShell Terminal instance.

codemag.com Interactive Unit Testing with .NET Core and VS Code 67


Figure 7: Many of the tasks you carry out in VS Code, like creating new projects, are via a PowerShell Terminal instance.

Figure 8: Each project is contained in its own folder. To perform tasks, whether it’s to add a NuGet Package or build/
compile, a terminal must be available.

ects. That, coupled with the different context of Visual Studio, is to undertake interactive unit testing in VS Code like you do
might make you feel lost. What now appears in the left-hand in Visual Studio. How do you do that if the feature doesn’t ex-
portion of the editor is the Explorer. In Visual Studio jargon, ist? The answer is found in asking the right question. The real
it’s kind of like the Solution Explorer. But it’s more like the File question is whether there’s an extension. In fact, there is an
Explorer. And if you start right-clicking project artifacts like extension and in the next step, you’ll install it.
you would in Visual Studio, you’ll quickly understand!

Figure 3 illustrates the six icons in the far left-hand portion


Step 4: Installing the .NET Core
Figure 9: Right-clicking
a project’s top-most node of the editor: Test Explorer
provides access to a number Figure 4 illustrates how to access extensions and, more
of useful functions, including Where Is the Test Explorer? specifically, how to search for and view the details for the
the ability to launch There’s no icon for testing and if you search through the menu .NET Core Test Explorer Extension. You’ll note that there are
a new terminal window. options, you won’t find anything there either. The task at hand many kinds of test explorer extensions. For example, there

68 Interactive Unit Testing with .NET Core and VS Code codemag.com


are Java- and Python-specific explorers, to name just two. Step 5: Create and Build a Class
Although VS Code is still relatively new, the extension eco-
system is quite robust. Like any ecosystem, there are good Library with Functions for Testing
items and there are not-so-good items. With that in mind, I could easily short circuit the process and just create ev-
you’ll need to carefully scrutinize which extensions you erything you need in a unit test project. But that isn’t very
choose to bring into your environment. If you’re interested useful. In the real world, there are distinct libraries for class
in learning more about how to build your extensions, the libraries and tests. For the tests to work properly, you es-
following link will get you started: https://code.visualstu- tablish a project reference in the unit test project to the
dio.com/api/get-started/your-first-extension. project that contains the code you’re putting under test.
You’ll do the same thing here.
Figure 5 illustrates the VS Code environment after the .NET
Core Test Explorer has been installed. The first step is to establish a class library project. To do
that, you need to create a terminal instance. There are two
With the test explorer extension installed, a new icon is ways to do that. The first is from the Terminal Menu, where
available. Now that you have a way to interact with tests in you select New Terminal. The other way is to invoke the
VS Code, let’s create some projects so that you can see how shortcut Ctrl+Shift+` (apostrophe). Figure 6 illustrates the
to run tests and verify your code. VS Code instance with a terminal instance.

Figure 10: The OmniSharp Extension enables VS Code to understand how to work with C# and compile files.

Figure 11: With OmniSharp installed, you can build C# projects in VS Code.

codemag.com Interactive Unit Testing with .NET Core and VS Code 69


Figure 12: The xunit template, like any project template, takes care of installing all of the necessary dependencies.

Figure 13: VS Code and


the Test Extension need to
know where to find the test
projects.

Figure 14: Under Settings\Extensions\.Net Core Test Explorer, you need to set the Test Project Path. If you follow a naming
convention, wildcards can be used to cover multiple test projects.

A command you’ll want to be well acquainted with is dotnet with VS Code, we’re much closer to what is going on. With fewer
new: (https://docs.microsoft.com/en-us/dotnet/core/tools/ abstractions, it’s easier to understand what’s going on when
dotnet-new). In Visual Studio, when you want to create a new you have to fix a problem. How many times have things just not
solution or project within a solution, that task is typically car- worked in Visual Studio without a clue as to what exactly went
ried out via point-and-click operations against a menu option. wrong? Those problems are less likely to occur with VS Code.
Although there are some operations in VS Code carried out in
that manner, many will be carried out via the command line. Figure 7 illustrates how to create a new class library. In the
To some, that may seem like a step backward. In my opinion, terminal, type dotnet new classlib -n Math.

70 Interactive Unit Testing with .NET Core and VS Code codemag.com


Figure 15: A passing test is indicated with a green checkmark.

With a new class library project, the next step is to ensure


Figure 16: Adding project and NuGet references is a matter of adding the necessary entry to
that the code compiles. Figure 8 illustrates that process.
the csproj xml file.
In order to build the project, you need to either change to
the Math subdirectory that contains your class library or you
need to open another terminal window. Figure 9 illustrates
how to open another terminal window.

Finally, let’s create a simple Add function. To do that, click


the Class1.cs file in the explorer. Figure 10 illustrates an ex-
ample of an automatic VS Code prompt. If you click the Show
Recommendations button, focus shifts to the extension view.
VS Code isn’t a C#- or VB-specific editor. Provided that the
necessary infrastructure is in place, VS Code can support any
language. In this case, you need to install support for C# com-
pilation. This is accomplished via the OmniSharp Extension.

The following code snippet and Figure 11 illustrate the


simple Add method. Figure 17: You can debug a test by right-clicking the test in the explorer.

public static int 
Add(int pOne, int pTwo) => (pOne+pTwo);

Step 6: Add an XUnit Test Project


and Create and Run a Unit Test via
the Unit Test Explorer
You’re almost there! To create the XUnit test project, in a
terminal, you need to navigate back to the root folder. Fig-
ure 12 illustrates what happens after you issue the dotnet
new xunit -n MathTests command.

Now that you have a unit test project with a test, you should
be able to see the test in the test explorer, right? Wrong!!
Figure 13 illustrates a cryptic message.

Figure 14 illustrates what you need to do in order to get VS


Code and the Test Explorer to recognize your tests.

If you click the arrow next to the test name in the explorer,
the test runs. Figure 15 illustrates a passing test run.
Figure 18: When debugging tests, you have access to watched variables, breakpoints,
Before you can write unit tests against your code, the unit and the call stack.
test project needs a reference to the math class library proj-
ect. The process for this, in concept, is the same as Visual a developer, retain control because the environment is doing
Studio. The concrete steps to implement, however, are quite less for you behind the scenes. If something goes wrong, the
different. In VS Code, you often need to go into the csproj file cause and the cure become that much more apparent. Figure
and edit the XML directly. This is typically not the case in Vi- 16 illustrates how to add the project reference.
sual Studio. This may seem like a step backward. In my opin-
ion, working directly with project artifacts means that you, as Figure 17 illustrates a simple test with a breakpoint.

codemag.com Interactive Unit Testing with .NET Core and VS Code 71


Figure 19: Clear evidence is provided for failing tests.

Figure 20: Tests can easily be run from the command line. VS Code provides quick access to details on failing tests through
interactive and intuitive prompts.

Figure 18 illustrates the breakpoint being hit in an interac- wish to add. I think you’ll find that VS Code isn’t really a prim-
tive debugging session. itive tool when compared to Visual Studio. Sure, Visual Studio
has a lot of features. How many of them do you really use?
Finally, Figure 19 illustrates a passing and failing test. And if you use refactoring tools like Resharper or CodeRush,
performance can often be an issue. You won’t find versions
If you prefer to run tests from the command line, you can of Resharper or CodeRush in VS Code for the simple reason
easily do that. The process for that is illustrated in Figure 20. that you don’t need them; there are several good (and free!!)
refactoring extensions available. Some are better than oth-
ers. Try them by kicking the tires and find the one that works
Conclusion for you. Give VS Code a try. I think you’ll find as editors go,
Writing and interacting with unit tests in VS Code is really no it’s well suited to the task. And if you like working on a Mac,
more difficult than how it’s done in Visual Studio. VS Code is there’s a native version of VS Code for Mac as well! Enjoy!
just a tool and like any tool, you simply need to understand
how to use it. You’ll find that through extensions, you can add  John V. Petersen
many features. The key is that you control which features you 

72 Interactive Unit Testing with .NET Core and VS Code codemag.com


CODE COMPILERS

(Continued from 74) first words in the Agile Manifesto that codified
principles 19 years ago says it best:
collaborative culture. There’s a responsibility to lift
people up. It’s one thing to talk. It’s another thing Sep/Oct 2020
to walk it and live the sermon. Volume 21 Issue 5
We are uncovering better ways
It’s not enough to have people who are willing of developing software by doing Group Publisher
to teach. There needs to be an environment that Markus Egger
it and helping others do it.
is conducive to giving people the opportunity Associate Publisher
to learn. It’s not about top down vs. bottom up. Rick Strahl
It’s about meeting in the middle. If it sounds like Editor-in-Chief
liberal democracy, that’s because that’s precisely The challenges faced by the NT team were no differ- Rod Paddock
what successful organizations practice. And by do- ent from those faced by other teams, and the Show-
Managing Editor
ing so, they avail themselves of the ability to take stopper book provides insights into how the team Ellen Whitney
advantage of wisdom its personnel have. confronted what were many serious issues. NT lead-
ership (specifically Cutler himself) recognized the Content Editor
Melanie Spiller
propriety of testing by non-developers. That was, to
Read Showstopper! a large degree, a very ego-driven personality who Editorial Contributors
Otto Dobretsberger
In 1993, an amazing piece of software was finally set aside his ego for the benefit of the project that Jim Duffy
available to the public: Windows NT. NT was com- was a creation of his vision and realized by his drive. Jeff Etter
prised of millions of lines of code amongst 40K files, Mike Yeager
written by a team of about 250 people over a period Eight years after NT was released, 17 notable and Writers In This Issue
of about four years. As somebody who was an early famous software developers met at the Snowbird Richard Campbell Otto Dobretsberger
NT adopter, I remember well the fanfare for its re- Resort in Utah to craft the Agile Manifesto and its Markus Egger Kevin S. Goff
Joydip Kanjilal Vassili Kaplan
lease. At the time, I knew nothing of what it took to supporting principles. We’re still trying to figure Stefan Landvogt Sahil Malik
build NT. As it turns out, it took many things: money, it out 19 years later! How do we learn? By doing. John V. Petersen Paul D. Sheriff
careers, egos, mental health, marriages, etc. That is In other words, we practice the craft, and in that
Technical Reviewers
what the book “Showstopper! The Breakneck Race to regard, what software developers do is no differ- Markus Egger
Create Windows NT and the Next Generation at Micro- ent from what any other professional does. Practice Rod Paddock
soft” by G. Pascal Zachary is all about. I profess that implies skills development and continuous learning.
Production
it’s been in my library for quite some time, but I just We learn by doing and our feedback loop is empirical Franz Wimmer
finished reading it. Originally published in 1994, it’s a data. In my opinion, good developers take the time King Laurin GmbH
book I wish I’d read years before. It’s the kind of book to understand and appreciate history, and then they 39057 St. Michael/Eppan, Italy
that’s worthy of many readings during your career. learn from those experiences and go on from there. Printing
Fry Communications, Inc.
My biggest takeaway from the book is the reinforce- Delivery is a feature. We can’t analyze a problem 800 West Church Rd.
Mechanicsburg, PA 17055
ment that there are no new problems and that the to death. We must be pragmatic. We must avoid
past is just a prologue. There are lots of great in- perfection becoming the enemy of progress. That’s Advertising Sales
sights into the challenges of managing large teams, not a license to just throw something over the wall Tammy Ferguson
832-717-4445 ext 26
complex features, and testing. What makes Show- for the sake of delivery. Tools and techniques come [email protected]
stopper such a poignant read is how it focuses on and go, but principles endure. That was the gist of
the one individual who led the NT team and his my technical article on Unit Testing in VS Code (in Circulation & Distribution
General Circulation: EPS Software Corp.
journey as a developer that started in the early this issue). VS Code is quite different from Visual Newsstand: The NEWS Group (TNG)
1960s. As of today, David Cutler is nearly 80 and is Studio. If you take on VS Code with Visual Studio Media Solutions
still a distinguished technical fellow at Microsoft. expectations, you’ll flounder. On the other hand, Subscriptions
Here is a somewhat recent article from 2016 that if you consider, at an abstract level, what you’re Subscription Manager
highlights Cutler’s and his cohorts’ history: https:// trying to accomplish, and apply testing principles; Colleen Cade
news.microsof t.com/features/the-engineers- principles that transcend specific tools, there’s no [email protected]
engineer-computer-industry-luminaries-salute- longer an impedance mismatch between context US subscriptions are US $29.99 for one year. Subscriptions
dave-cutlers-five-decade-long-quest-for-quality/. and your mode of problem solving. The solution outside the US are US $49.99. Payments should be made
will make its presence known and will just fall into in US dollars drawn on a US bank. American Express,
MasterCard, Visa, and Discover credit cards accepted.
place. And if it doesn’t, it’s time to cut bait and
Never Stop Learning consider a change in approach. Often, that change
Bill me option is available only for US subscriptions.
Back issues are available. For subscription information,
This is not the same thing as being on the ham- requires a refined problem definition, a better e-mail [email protected].
ster wheel of technical churn where we trot out understanding of the problem, or a better under- Subscribe online at
and deploy new things for the sake of it! I’ve lost standing of the tooling, just to name three possi- www.codemag.com
track of my own past efforts to learn enough about bilities. How things work as they do is subordinate
CODE Developer Magazine
something just to realize that it’s better to leave it to why things work as they do. When you focus on 6605 Cypresswood Drive, Ste 425, Spring, Texas 77379
on the shelf, never to be touched again! The issue why, you necessarily will focus on base principles. Phone: 832-717-4445
is the never-ending quest to somehow leverage
the good without encountering the not-so-good. Why do base principles always apply? Because
How do we keep control of a large project and, at principles endure. And why is that? Because there
the same time, keep control of what today we call are no new problems.
the work-life balance? In striking the balance, how
do we decrease defects (showstoppers), increase  John V. Petersen
features, and increase delivery velocity? The very 

codemag.com CODA: On First Principles 73


CODA

There Are No New Problems


There may be no new problems, and yet, we do our level best to try and create them. It’s often said that
what we do as software developers is hard. What we do isn’t hard. How we chose to go about what
we do, that’s what makes it difficult to do. If a task is difficult, it’s not necessarily because the task is

intrinsically difficult. There are several items to today. It may very well be that what’s being faced hand, they’re different. The contradiction can be
check off first: today is more serious than what was faced in 1918. resolved if context is considered. On one hand,
That, however, is a different issue, one of scale. being a Luddite isn’t good. On the other hand,
• Do I understand the problem (have I suf- technical churn that’s always in the favor of what
ficiently broken it down)? Another way to illustrate the point is by way of fic- is perceived as being the latest and greatest isn’t
• Have I attained sufficient competency to tion writing. Christopher Booker’s 2004 book “The good either. To be the greatest at anything im-
both practice my craft generally and in a Seven Basic Plots” sets forth that proposition in a plies that there’s empirical evidence to support
specific context? very straight-forward way. The seven basic plots are: that. New things, by their very nature, lack em-
pirical evidence. That’s why we should always test
In the law, when an issue is analyzed, we determine • Overcoming the monster first. Another quote for the pile: Trust but verify.
whether there are threshold issues that must be • Rags to riches
first addressed. For example, if you slip and fall in • Tragedy
a store, can you recover damages? That depends on • Comedy
Wisdom: Where Knowledge
whether the shopkeeper was negligent. Negligence • The quest Meets Experience
depends on whether the shopkeeper had a duty to a • Voyage and return It’s important not to confuse the use case with the ac-
foreseeable plaintiff to exercise reasonable care. If • Rebirth tual problem. For example, the need for a new daily
reasonable care was exercised, that settles the mat- sales report isn’t the problem; it’s a feature request.
ter. The issue of damages is irrelevant. In fact, we can break these down further to ris- There are many subordinate issues that need to be ad-
ing and falling. Sometimes the story is one or the dressed to achieve this goal. At its core, the request is
Software development is no different in that we other. And sometimes it’s a combination of the about data retrieval, which, in turn, has (among other
need to always break things down to their constitu- two, multiple times in different sequences. For all things) rendering, performance, and security con-
ent parts. In other contexts, we call this root-cause the twists and turns and factual differences, when cerns. As we bore into the details, we eventually real-
analysis. In other contexts, we employ what is known we break them all down in a coding project, there ize that for each thing we encounter, there’s a solution
as the scientific method. We learn by observation are only a few categories that we end up with. Any that’s been employed before. One question is how far
and testing. From that, empirical data is produced software does at least one and up to four things: into the weeds we need to go to break the problem
that provides the evidence for us to be able to make create, retrieve, update, and delete data. down to its constituent parts. There’s no precise an-
informed decisions. Whether we, in fact, make in- swer. The generic answer is to dig as deep as is neces-
formed decisions, depends on whether we follow the Whether 50 years ago or today, we have problems sary and no deeper. What does that mean? It means
evidence and whether we respect the science. That’s to address with technical solutions. To do that, that you keep digging until you hit familiar territory.
the irony of our industry. Logic and reason are at its we write, test, and deploy code. That’s what we
core. But too often, those two things are the first do. At a foundational level, software creates, re- If, after digging for a while, nothing is familiar or
casualty of problem solving. The scientific method, trieves, updates, and deletes data. These are the isn’t understood, look in the mirror. You may lack
in one form or another, traces its roots back many CRUD operations and that is what the software the necessary experience to deal with the issue. You
thousands of years: i.e., principles endure. does. How the software does what it does, that’s need the wisdom to know when you don’t know. And
indeed different. Where we used to rely exclusively if you don’t possess it, collaborate with someone
There are many phrases we employ today that on relational databases and SQL, today we may who does. Anything else and you’re just burning
convey these meanings: What’s past is prologue rely on NOSQL and unstructured solutions. Very cycles with no benefit. This is where a culture of col-
and Those who forget the past are condemned to different things, to be sure. But at a fundamental laboration and inclusiveness matters. No one person
repeat it. These quotes are attributed to William level, we’re talking about the same thing: key val- has all the answers. And yet there are those among
Shakespeare and George Santayana, respectively. ue pairs. That’s what JSON is. That’s what XML is. us who seem to instinctively know how to break
And yet often, JSON is regarded as a newer, more down and solve problems. In other words, there are
Whether it be politics, foreign relations, domestic modern thing and XML is regarded as old. In .NET those who know that boiling the ocean is an exer-
policy, human nature, etc., the lesson is clear; al- Core and VS Code, we have the appsettings.json cise in futility. Knowledge comes from learning, not
though there may be some new and novel aspect to file. It’s just a configuration file, like app.config instinct. Those of us who have the knowledge and
any individual thing, taken all together, the prob- or the ini files we employed in days past. They’re experience have a moral, ethical, and professional
lem has been encountered before. Take COVID-19 as different things and, at the same time, the same obligation to share it so that we can pass it on, be-
an example. The analog used to understand it is 100 things. What’s the threshold issue? It’s context. cause once upon a time, somebody helped us. That
years old: the 1918 flu pandemic. That’s not to say isn’t enough, though. Organizations and leadership
that the two are the same. They’re not. But there Another phrase that comes to mind is: The only have their roles to play as well. They must foster a
are many similarities and, at a root level, the same constant is change. It seems like a paradox: On
core issues faced in 1918-19 are what’s being faced one hand, things are the same; on the other (Continued on page 73)

74 CODA: On First Principles codemag.com


Upskill from anywhere
Build in-demand tech skills no matter where you are
with access to courses on top technologies,
skill assessments, paths and more.

Expert-led courses Pluralsight IQ Guided learning

Keep up with the pace of Validate skill levels with Measure progress towards
change with thousands of assessments that take personal objectives and
expert-led, in-depth courses. 10 minutes or less. learning goals.

Start for free today


pluralsight.com/codemag
Build faster.
Build better.
Build .NET on AWS.
To learn about .NET on AWS visit
go.aws/codemag

You might also like