Link Search Menu Expand Document

Getting Started

All the examples in this workshop are based on Autobarn, a fictional website for listing used cars for sale. Autobarn is an ASP.NET web application that uses an in-memory data store, so you can download and run the Autobarn code without any external dependencies.

Prerequisites

To complete the exercises in the workshop, you’ll need the Autobarn source code and the .NET 10 SDK. The Autobarn repository is hosted on GitHub. Get started by cloning the Autobarn repo to your local machine:

git clone git@github.com:ursatile/autobarn.git
cd autobarn/Autobarn
dotnet build
dotnet test
dotnet run --project Autobarn.Website

Now browse to http://localhost:5000/ and you should see the Autobarn website running.

Tunneling to localhost using ngrok

ngrok (https://ngrok.com/) is an “all-in-one cloud networking platform that secures, transforms, and routes your traffic to services running anywhere”, but before it got all high & mighty, it was just “secure tunnels to localhost” - and that’s what we want.

If you’re Dylan, you can run this:

ngrok http https://localhost:5001 --url autobarn.dev

(that works for Dylan because he’s got a paid ngrok license and he’s set up DNS so that the autobarn.dev domain points to an Ngrok tunnel endpoint)

If you’re not, you can just run ngrok http https://localhost:5001 and it’ll give you a secure tunnel from the internet to your local machine - useful for sharing work in progress, debugging webhook integrations, and all kinds of other scenarios.

Sqlite and in-memory databases

Autobarn uses Entity Framework (EF) Core, but by default, it’s configured to use the Sqlite DB provider with an in-memory database:

SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. SQLite is the most used database engine in the world. SQLite is built into all mobile phones and most computers and comes bundled inside countless other applications that people use every day.

from https://sqlite.org/

In other words, SQLite is a tiny database engine that doesn’t use a server: it runs in the same process as our application code, which makes it perfect for development and testing scenarios where we need to connect a database but we don’t really care what happens to the data.

If you take a look in Program.cs you’ll see these lines:

SqliteConnection sqliteConnection = new($"Data Source=:memory:");
// Open a DB connection to keep the in-memory database alive while the app is running
sqliteConnection.Open();
builder.Services.AddDbContext<AutobarnDbContext>(options => options.UseSqlite(sqliteConnection));

and

using var scope = app.Services.CreateScope();
await using var db = scope.ServiceProvider.GetRequiredService<AutobarnDbContext>();
await db.Database.EnsureCreatedAsync();

The in-memory provider for Sqlite (initialised by the "Data Source=:memory" connection string) will create a temporary database that persists for as long as there’s at least one open connection, so we explicitly open a connection that stays open for the lifetime of the application, and then we use EF Core’s Database.EnsureCreated() method to create the data schema.

Populating the Autobarn Database

We’ve got a bunch of sample data included in the Autobarn app, in a set of CSV files in the Autobarn.Data.Sample folder/namespace.

EF Core provides three methods for populating a database with data:

Managing Lookup Data with HasData

The EF Core model builder providers a HasData mechanism which you can use this for data that customers can’t change. Country lists, tax codes, all the static data that forms part of your application.

  • Data has to be stable. Anything with GUIDs, identities, timestamps, or any other non-deterministic values will be destroyed and rebuilt every time you apply a DB migration.
  • Data must be exposed as flat records, rather than actual entity objects.

We’re going to use HasData for vehicle makes and models:

// Add this to AutobarnDbContext.OnModelCreating()
modelBuilder.Entity<Make>().HasData(SampleData.CarMakeCsvData);
modelBuilder.Entity<CarModel>().HasData(SampleData.CarModelCsvData);

For seeding realistic test data that simulates the transactional customer data in a real system, we can use UseSeeding and its counterpart UseAsyncSeeding:

// Add this to AutobarnDbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
	optionsBuilder.UseAsyncSeeding(async (dbContext, _, cancellationToken) => {
		var db = (AutobarnDbContext) dbContext;
		if (await db.Vehicles.AnyAsync(cancellationToken)) return;
		var models = await db.Models.ToListAsync(cancellationToken);
		var vehiclesToInsert = SampleData.VehicleCsvData
			.Select(csv => new Vehicle {
				Registration = csv.Registration,
				Model = models.Single(m => m.Code == csv.ModelCode),
				Color = csv.Color,
				Year = csv.Year
			});
		await db.Vehicles.AddRangeAsync(vehiclesToInsert, cancellationToken);
		await db.SaveChangesAsync(cancellationToken);
	});
}