December 2017

Volume 32 Number 12

[Data Points]

Building UWP Apps for Local and Cloud Data Storage

By Julie Lerman

Read the entire EF Core 2 and UWP App Data series:

Using EF Core 2 and Azure Functions to Store UWP App Data Locally and Globally, Part 1
Using EF Core 2 and Azure Functions to Store UWP App Data Locally and Globally, Part 2
Using EF Core 2 and Azure Functions to Store UWP App Data Locally and Globally, Part 3
Using EF Core 2 and Azure Functions to Store UWP App Data Locally and Globally, Part 4

Julie LermanThis is the first of a multi-part series that will show you how to store data on a device running a Universal Windows Platform (UWP) app, as well as how to store the app’s data in the cloud. This article will focus on the local storage using Entity Framework Core (EF Core) and a SQLite database. The subsequent parts will add in capabilities to store and retrieve the UWP app data to the cloud using Azure Functions and Azure Cosmos DB.

In the early days of EF Core, when it was still called EF7, I took advantage of its new ability to run not just on a full .NET Framework setup, but on devices, as well. In my first Pluralsight course on EF7 (which was designed as a preview of the features being built), I created a small and quite silly game called Cookie Binge. It ran on Windows Phone 8 and as a Windows Store app for Windows 8, storing its data locally using EF7 and SQLite. The game was a spinoff of a demo app built by the EF team that focused on capturing unicorns. The CookieBinge game lets you eat cookies (by clicking on them) and when you’re finished binging, you indicate either that the spree was totally worth it or that you feel guilty for scarfing down all those cookies. The number of cookies you consume becomes your score and your selection of “worth it” or “guilty” is tied to the score in the game’s history. It’s a silly game with no real goal, just my way of exploring how to incorporate EF 7/Core into a mobile app.

When EF Core was released in 2016, I evolved the game to run as a Universal Windows Platform (UWP) app, which could be played on any device running Windows 10. The recently released EF Core 2.0 now has a dependency on .NET Standard 2.0. The latest version of UWP, which targets the just-released Windows 10 Fall Creators Update, also relies on .NET Standard 2.0, allowing you to use EF Core 2.0 in this new generation of UWP apps.

That means it’s time for me to update the CookieBinge app yet again, this time to EF Core 2.0. The only way to do that is to also update the UWP app to target the new version of Windows 10. Keep in mind that my focus will remain on the data access, not on designing the UI of the app to benefit from the features of Windows 10. Additionally, I won’t cover the new features of EF Core 2.0 in this example. Instead, I’ll be looking at the ability to combine these two technologies.

I’ll build the application from scratch, rather than updating the existing app, so you can follow along. After I get the game updated to EF Core 2.0, I’ll add a new feature to allow sharing of scores with other game players. To accomplish this, in addition to storing scores locally on the game device, the app will leverage Azure Functions and Azure Cosmos DB so users can share their scores around the globe. I’ll perform these tasks in subsequent installments of this series.

Your Dev Environment

Although EF Core can be built and run on cross-platform environments, UWP is a Windows-only platform, so you’ll need to do this work on a Windows machine. To be specific, you’ll need Windows 10 Fall Creators Update and Visual Studio 2017 version 15.4 (or later) installed. Visual Studio doesn’t install the .NET Standard 2.0 SDK, so you must install that manually. You’ll find links to the .NET Standard downloads at bit.ly/2mJArWx. You’ll see that you can download either the runtime or the SDK (which includes the runtime). Select the SDK downloads appropriate for your machine.

Be sure to install the UWP tooling for Visual Studio, which you can do by selecting the UWP workload in the Visual Studio installer. If you don’t have the Windows 10 SDK build 16299 enabled in the workload, please make sure to do that.

An Early-Adopter Caveat

Note that as I’m writing this, EF Core 2.0 is not wholly aligned with the new version of UWP, though some fixes are coming soon in the 2.0.1 patch. For example, there’s a current bug with executing relational queries when in a UWP project. My sample avoids this problem. You can track the issues on the EF Core GitHub repository at bit.ly/2xS35Mq.

Creating the New UWP Project

Once all of the proper tooling is installed, Visual Studio will display a set of UWP project templates in a section named Windows Universal. From that set of templates, I’ll create a new Blank App (Universal Windows). The framework dropdown defaults to 4.7.1 (although the .1 is cut off in Figure 1) and I’ll leave that default as the base version of .NET Framework for my app. I named the new project CookieBinge2.

Creating a UWP Project from the Blank App Template
Figure 1 Creating a UWP Project from the Blank App Template

You’ll be prompted to select a target version and a minimum version of UWP. Be sure to choose Windows 10 Fall Creators Update for both. The current build number is 16299.

When the project is created, you’ll see the files and folder in Solution Explorer, as shown in Figure 2.

The Project as Initially Created by the Template
Figure 2 The Project as Initially Created by the Template

Because this column is focused on data, not UI building, I’ll take some shortcuts to get the UI in place, then add in the APIs and code for EF Core and its data-persistence activities.

You’ll find the necessary code to copy and paste in the download associated with this column. To start, delete the images in the new project’s Assets folder and in their place, copy the images from the Assets folder (shown in Figure 3) provided in the download.

The Images You’ll Be Copying into the Assets Folder
Figure 3 The Images You’ll Be Copying into the Assets Folder

Adding in Persistence Logic with EF Core 2.0

You’ll start by adding two classes to the project, as follows.

The CookieBinge.cs class is not much more than a DTO, containing only properties that need to be stored. There’s no real logic that needs to be performed. Here’s the class:

public class CookieBinge {
  public Guid Id { get; set; }
  public DateTime TimeOccurred { get; set; }
  public int HowMany { get; set; }
  public bool WorthIt { get; set; }
}

The BingeService class encapsulates the tasks I determined this game needs to perform, serving to marshal information between the UI and the data store. BingeService has three methods: RecordBinge (to persist the results locally), GetRecentBinges (to retrieve a subset of the game scores) and ClearHistory (to clear all results from the local store). Here’s the class before the logic of those methods has been implemented:

public static class BingeService {
    public static void RecordBinge(
      int count, bool worthIt) { }
    public static IEnumerable<CookieBinge>    
      GetRecentBinges(int numberToRetrieve)
    public static void ClearHistory() {  }
  }

In order to flesh out the service, I need to add in the persistence functionality. This is where EF Core 2.0 comes in to read and store the binge results to the device. EF Core has a provider for SQLite databases, which can be used directly on a variety of devices, so that’s what I’ll use for this demo.

Now that I’ve chosen the data store, I can add the EF Core provider for SQLite into my app and it will, in turn, pull in the related EF Core packages it relies on, including SQLite if needed. EF Core is no longer one big package. Instead, the logic is divided into various assemblies, so you can minimize its footprint on your device (or server or wherever you deploy it) with only the subset of logic your application requires.

In Visual Studio 2017, you use the NuGet Package Manager to add NuGet packages into your projects—either visually or via Power­Shell commands such as install-package. I’ll use the PowerShell commands, which means opening the Package Manager Console (Tools | Manage NuGet Packages | Package Manager Console). As I have only one project in my solution, the console should indicate that CookieBinge2 is the default project, as shown in Figure 4. There I can install the SQLite provider package.

Installing the SQLite Package in the Package Manager Console Window
Figure 4 Installing the SQLite Package in the Package Manager Console Window

When the installation is complete, you’ll see this package listed in the project references in Solution Explorer. For the curious, if you force Visual Studio to show all files, expand the obj folder and then open the project.assets.json file, you’ll be able to see the dependencies that were pulled in by the SQLite provider, such as Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.Relational and others. With EF Core and the SQLite provider in place, you can now create the DbContext. Add a new file named BingeContext.cs and copy the code listed in Figure 5.

Figure 5 The DbContext Class to Manage Persistence with EF Core

using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Windows.Storage;
namespace CookieBinge2 {
  public class BingeContext:DbContext {
    public DbSet<CookieBinge> Binges { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder options){
      var dbPath = "CookieBinge.db";
      try {
        dbPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, dbPath);
      }
      catch (InvalidOperationException){
        #TODO handle this exception
      }
      options.UseSqlite($"Data source={dbPath}");    }
  }
}

Because this DbContext is in the UWP project, I’m taking advantage of available resources such as System.IO and Windows.Storage APIs. The download for this article will reflect this pattern. In contrast, another version of the app on my GitHub account (bit.ly/2liqFw5) has the back-end logic and EF Core in a separate .NET Standard Class Library project and uses dependency injection to provide the file path to the context. If you plan to use EF Core migrations to evolve the model and database schema, you’ll need to use that separated architecture.

The context has a single DbSet to interact with CookieBinge data. The code in the OnConfiguring method lets EF Core know that it will use the SQLite provider and specifies a local file path for storing the data on the device where the app is running.

Now that the context is there, I can flesh out the BingeService methods. RecordBinge will take in values sent from the UI, build up a CookieBinge object and pass that on to the BingeContext to store into the local database:

public static void RecordBinge(int count, bool worthIt) {
  var binge = new CookieBinge {
                HowMany = count,
                WorthIt = worthIt,
                TimeOccurred = DateTime.Now};
  using (var context = new BingeContext()) {
    context.Binges.Add(binge);
    context.SaveChanges();
  }
}

The GetRecentBinges method will retrieve data from the locally stored database using the BingeContext and pass it back to the UI that made the request and has a panel to display that data:

public static IEnumerable<CookieBinge> GetRecentBinges(
      int numberToRetrieve) {
   using (var context = new BingeContext()) {
     return context.Binges
               .OrderByDescending(b => b.TimeOccurred)
               .Take(numberToRetrieve).ToList();
   }
}

The final method in BingeService, ClearHistory, empties out the local database completely—in case you don’t want to be tormented by the excessive amount of binging you’ve done:

public static void ClearHistory() {
  using (var context = new BingeContext(){
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();
  }
}

This takes care of the back end of the game’s logic.

The UI and Its Logic

The front end consists of the UI (MainPage.xaml), UI logic (MainPage.xaml.cs) and the BingeViewModel.cs file. There are a number of methods in these classes of interest to the functionality I’ve created in the back end. In MainPage.xaml.cs, there are two methods that call into the BingeService class. The ReloadHistory method calls the GetRecentBinges method and binds the results to a list on the UI:

private void ReloadHistory() {
  BingeList.ItemsSource = BingeService.GetRecentBinges(5);
}

The ClearHistory method calls the service method to purge all of the data from the local database and then forces the UI to reload its display of the history, which will now be empty:

private void ClearHistory_Click(object sender, RoutedEventArgs e) {
  BingeService.ClearHistory();
  ReloadHistory();
}

There are also methods in the UI that respond to the user clicking the Worth It and Not Worth It buttons to store the results, but these don’t call the service directly. Instead, they call into the BingeViewModel method, StoreBinge, which then calls the service method to store the data. The StoreBinge method first calls the service method, passing along the data from the UI:

BingeService.RecordBinge(_clickCount, worthIt);

StoreBinge also performs some additional magic in the UI to clean up the just-finished binge and prepare the UI for a new binge. You can see this additional code in the download.

This separation of concerns that results in passing data from one file to another allows for future maintenance of this app to be simpler. Fortunately for me, the code changes as I’ve transitioned the app from EF7 to EF Core 1 to EF Core 2 have been minimal.

There’s one last task to perform, which is to trigger the application to make sure that the local database file exists. In the App.xaml file, add the following code into the constructor method after this.Suspending += OnSuspending:

using (var context = new BingeContext()) {
  context.Database.EnsureCreated();

In the separated architecture on GitHub where you can use migrations, you’ll see a call to the Migrate command after Ensure­Created to make sure any model changes are applied.

Running the App

In Visual Studio, you target either the local machine, or the simulator, which opens up a separate window that simulates your local machine, to run or debug the app. There’s also an emulator for HoloLens available at bit.ly/2p8yRMf. I’ll just run my app on the local machine. Figure 6 shows the game play with a hint of the computer desktop background around the edges. Each click on the cookie displays the word “Nom!” at the mouse pointer and increments the “Cookies Eaten” counter. When satiated, the user clicks either the cool blue icon or the dead as a doornail icon to complete the game. Figure 7 shows the Scores page, displaying the last five scores along with the Clear History button. This UI is just what a data geek could hack together. For proper UWP UI design guidance, you’ll find a great place to get started at bit.ly/2gMgE74.

Binging on the Cookie; Six Eaten So Far
Figure 6 Binging on the Cookie; Six Eaten So Far

The History Stored in the Local Database via EF Core
Figure 7 The History Stored in the Local Database via EF Core

Coming Up: Sharing with Other Players via Azure Functions and Cosmos DB

If you can forgive the simplicity of my little game and design, the focus of the lesson here is that EF Core can now work directly on a device because it now relies on .NET Standard, not the full .NET Framework, and UWP supports .NET Standard. This means you can now use EF Core 2 in UWP apps on a variety of Windows 10 devices, such as HoloLens, Xbox, Windows Mixed Reality and more.

I’ve used EF Core 2.0 to store data locally on the device where the CookieBinge game is being played. In my next column I’ll prepare for a new set of capabilities to the game by tying it to the Web. Using Azure Functions, the app will be able to send scores to an Azure Cosmos DB database, allowing users to compare their own scores across the various UWP devices they play on, as well as compare their cookie binging to other bingers around the world. Azure Functions and Azure Cosmos DB will keep me covered as my Cookie Binge game dominates the globe and needs to scale at the click of a button.


Julie Lerman is a Microsoft Regional Director, Microsoft MVP, software team mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other topics at user groups and conferences around the world. She blogs at the thedatafarm.com/blog and is the author of “Programming Entity Framework,” as well as a Code First and a DbContext edition, all from O’Reilly Media. Follow her on Twitter: @julielerman and see her Pluralsight courses at juliel.me/PS-Videos.

Thanks to the following Microsoft technical expert for reviewing this article: Clint Rutkas


Discuss this article in the MSDN Magazine forum