February 10th, 2025

.NET Aspire and Data API builder with the Community Toolkit

Jerry Nixon
Senior Program Manager

What's Data API builder (DAB)

Data API builder (DAB) creates secure REST and GraphQL endpoints over your database (SQL, Postgres, Cosmos, MySQL) in Azure, on-prem, or anywhere you can run a container. It supports every programming language, requires no additional framework, is rich with features, and – because it is open source – is completely free.

 

There is no code generation with Data API builder (DAB), instead a single JSON file describes your data source and the tables, views or stored procedures you want to expose. This file, the configuration file, is the only requirement to get DAB up and running. The stateless and scalable engine is production-ready and built for developers.

 

Learn more about Data API builder: https://aka.ms/dab

.NET Aspire

Announcing .NET Aspire Integration with Data API builder (DAB)

 

As engineers add components to their solution architecture, their development environments can become complex and difficult to replicate across teams and environments. Placing each service in a dedicated container simplifies the setup—but only if you’re familiar with Docker.

Enter .NET Aspire, a simple orchestration tool that replaces Docker’s complexity with the familiarity of C#. It’s supported in Visual Studio, Visual Studio Code, and Rider, offering built-in framework features, including SQL Server and Azure SQL Database support.

Now, developers can leverage the Community Toolkit to add even more out-of-the-box capabilities, including Data API builder (DAB) integration.

What is the Community Toolkit?

What is the Community Toolkit? It’s a NuGet package—just like .NET Aspire—that you add to your project, allowing DAB containers to be added, configured, and managed without needing to handle the details yourself.

dotnet add package CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder

Get started

As of December 2024, .NET Aspire is delivered as a NuGet package, though you can still use the dotnet command line to create the initial solution. Visual Studio offers a clean GUI experience, but let’s stick to VS Code so our Mac friends can play along.

Using the CLI to get started generates your AppHost project, ServiceDefaults projects, and the SLN file to bring it all together. What you don’t get is your database, your API, or your client/website. And that’s how I like it—start small and add what you need rather than start big and strip away what you don’t.

Can it be that simple? Well, sort of—you get the framework, but the rest is up to you. Here’s the command-line statement:

dotnet new aspire

I have dotnet version 9.0.102 installed. 

C# in Visual Studio Code

If you are going to code C# in Visual Studio Code, you’re going to need the amazing C# Dev Kit extension to get the full debugging experience.

Your data-driven app

Adding SQL Server to your Aspire application is as simple as editing the Program.cs file in your AppHost project. This is where you configure .NET Aspire through its DistributedApplication class, adding services with clean C# syntax. Then, Aspire orchestrates your services as Docker containers when you hit F5—handling all the messy networking and shared environment variables for you automatically.

Now you’re ready to add just a few lines to get going.

In this snippet, I’ve included both the basic and some advanced things so you can see all your options. I’ll walk through each of them—sure, I could have pretended you can add SQL Server in just one line, but that wouldn’t reflect any realistic use case for a data-driven application.

var dabconfig = @"C:\Temp\demo\dab-config.json";
var dacpac = @"C:\Temp\demo\database.dacpac";

var builder = DistributedApplication
    .CreateBuilder(args);

var sql = builder.AddSqlServer("sql")
    .WithDataBindMount("my-sql-volume")
    .WithLifetime(ContainerLifetime.Persistent)
    .AddDatabase("db");

var sqlproj = builder.AddSqlProject("dacpac")
    .WithDacpac(dacpac)
    .WithReference(sql);

var api = builder.AddDataAPIBuilder("api", dabconfig)
    .WithReference(sql)
    .WaitForCompletion(sqlproj);

builder.AddProject<Projects.ConsoleApp1>("console")
    .WithReference(api);

builder.Build().Run();

AddSqlServer()

You’ll use AddSqlServer to configure the SQL container for your developer environment. When you hit F5, the container image will be downloaded (the first time) and started for you. But it’s not available by default, you need to install the SqlServer component from NuGet.

NuGet Gallery | Aspire.Hosting.SqlServer 9.0.0

dotnet add package Aspire.Hosting.SqlServer

Though I’m not configuring a custom password or port, you can easily do that. The first time you debug, Aspire will create a persistent password for your SQL Server in local user secrets—you can edit those as needed.

Use the MSSQL extension in VS Code

Use the MSSQL extension in VS Code to connect to your new server using “localhost,1433” as the server and whatever database name you’ve chosen—in my sample, it’s “db”.

AddDatabase()

You’ll use AddDatabase to configure your SQL Server connection string. At least for now (I say it that way because there’s been discussion about changing this method’s behavior), this .NET Aspire method only modifies the connection and does not create a database in your SQL Server container. It’s important to call this method; otherwise, your connection string will be incomplete. Just remember to create your database in later steps.

WithDataBindMount()

This is optional. However, you can use WithDataBindMount() to configure .NET Aspire to create and reuse a persistent data volume for your SQL container—basically, a virtual hard drive in Docker that is created once and then reused. This provides convenient continuity between debug sessions, allowing data changes in one session to persist in subsequent sessions. Should you delete the volume, Aspire will create a fresh one the next time you hit F5.

WithLifetime()

This is optional. However, you can use WithLifetime() to instruct .NET Aspire to leave your SQL container running when your debug session ends. The next time you hit F5, Aspire will reuse the existing container—if it exists—or create a new one if needed. There’s no question that SQL Server, like many advanced services, can take several seconds to spin up and be ready. This feature, new in Aspire 9 by popular demand, can significantly reduce the time it takes to get your environment up and debugging.

WithReference()

This is not optional. Adding WithReference() tells .NET Aspire that while orchestrating containers, it should inject the provided connection string from one service into another. This not only sets up networking behind the scenes but also allows you to use connection strings in environment variables or leverage .NET Aspire’s native service discovery feature to connect to services in other containers.

How you can use service discovery in a Web API.

using Microsoft.Aspire.Hosting;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;

var builder = Host.CreateApplicationBuilder(args);

// Enable Aspire service discovery
builder.Services.AddServiceDiscovery();

// Retrieve the connection string and register SqlConnection in DI
var connectionString = builder.Configuration.GetConnectionString("sql");
builder.Services.AddScoped<SqlConnection>(_ => new SqlConnection(connectionString));

var host = builder.Build();

using var scope = host.Services.CreateScope();
var sqlConnection = scope.ServiceProvider.GetRequiredService<SqlConnection>();

// Simulate an operation
Console.WriteLine($"Database Connection: {sqlConnection.ConnectionString}");

// Keep the application running
await host.RunAsync();

How you can use service discovery in a Console app.

var builder = Host.CreateApplicationBuilder(args);

// Enable Aspire service discovery
builder.Services.AddServiceDiscovery();

// Retrieve the connection string and register SqlConnection in DI
var connectionString = builder.Configuration.GetConnectionString("sql");
builder.Services.AddScoped<SqlConnection>(_ => new SqlConnection(connectionString));

using var host = builder.Build();

// Start the host in the background
await host.StartAsync();

using var scope = host.Services.CreateScope();
var sqlConnection = scope.ServiceProvider.GetRequiredService<SqlConnection>();

// Simulate an operation
Console.WriteLine($"Database Connection: {sqlConnection.ConnectionString}");

// Ensure proper shutdown
await host.StopAsync();

AddSqlProject()

This is optional. However, you can use AddSqlProject() to point .NET Aspire to your SQL Database Project, load the schema, and run the seeding scripts you have defined there. You need to create the database somehow, and using a SQL Database Project is a solid approach. However, this is not available by default—you need to install the SqlProject component from NuGet: Read the docs.

dotnet add package CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects

Three kinds of SQL Database Projects

Visual Studio SQL Database Project – Microsoft’s well-established, fully supported and feature rich project type used in Visual Studio, designed for full database development and management. It includes schema design tools, schema comparison, refactoring support, and pre/post-deployment scripts. It supports all SQL Server versions and a wide range of DDL statements. Generates a .dacpac for deployment. Best suited for developers who need comprehensive tooling with a graphical interface.

 

SDK-Style SQL Database Project – Microsoft’s modern alternative using the .NET SDK project format, offering better integration with CI/CD pipelines and cross-platform workflows. Lacks the SSDT schema comparison, refactoring tools, and graphical designers. Its VS Code integration is limited, and its Visual Studio integration is more limited. It can generate a .dacpac, but its feature set is lighter than the Visual Studio project. Ideal for teams focused on automation and modern DevOps practices. This is Microsoft’s recommended approach for new projects.

 

MSBuild.Sdk.SqlProj – A community-driven, MSBuild-based approach created to remove the dependency on Visual Studio and SSDT during the interim while SDK-Style was being developed. It enables fully automated, cross-platform builds for .dacpac generation but lacks a dedicated development environment. Best for CI/CD workflows where automation is prioritized over interactive development.

WithDacpac()

This is optional. However, if you are using AddSqlProject(), you can use WithDacpac() to point to a .dacpac file you previously created from your SQL Database Project. You generate this file simply by building the project—it’s up to you whether you move it from the /bin folder or not. This approach supports all types of SQL Database Projects. However, if you are using SDK-style or MSBuild.Sdk.SqlProj projects, .NET Aspire can build your project for you and automatically pick up the .dacpac without requiring you to provide its path.

AddDataAPIBuilder()

You’ll use AddDataAPIBuilder() to configure a Data API builder container for your developer environment. When you hit F5, the container image will be downloaded (the first time) and started automatically. However, this is not available by default—you need to install the Data API builder component from NuGet.

NuGet Gallery | Microsoft.DataApiBuilder 1.3.19

dotnet add package Aspire.Community.DataAPIBuilder

dab-config.json

The dab-config.json file is the local Data API builder (DAB) configuration file, defining how the API interacts with your database and what objects are exposed through your Data API.

When using AddDataAPIBuilder(name, configFilePath), you provide the path to this file so the container starts with your configuration, ensuring your Data API builder instance is ready to serve requests.

Read the configuration docs.

WaitForCompletion()

This is important. You will find two related methods: WaitFor() and WaitForCompletion(). WaitFor() ensures that one service waits for another service’s container to be started and available. WaitForCompletion(), however, waits not just for the container to start but also for the service inside the container to be fully initialized and ready to handle requests.

In this case, we are instructing Data API builder (DAB) to wait for the completion of SQL Server and the publishing of the SQL Project’s .dacpac. This ensures that when DAB starts and validates the configuration file against the data source, SQL Server in this case, that validation process can run successfully.

Conclusion

Adding Data API builder (DAB) to a .NET Aspire solution is now a built-in feature, thanks to the .NET Aspire Community Toolkit. This allows you to provision a DAB container through the Aspire orchestrator without needing a deep understanding of Docker—or even containers in general.

What’s great about this integration is that you can include Data API builder with minimal effort while eliminating a lot of existing or potential boilerplate code. DAB is an engine, not a code generator. Your JSON configuration file is all it needs to stand up REST and GraphQL endpoints, enabling rich and secure CRUD operations against SQL Server, PostgreSQL, MySQL, and Cosmos DB.

Hit F5 and have fun!

Data API builder Resources

.NET Aspire Community Toolkit Resources

  • .NET Aspire Community Toolkit repository: here
  • .NET Aspire Community Toolkit NuGet home: here
  • .NET Aspire Community Toolkit MS Learn docs: here

Online Videos Resources

Author

Jerry Nixon
Senior Program Manager

SQL Server Developer Experience Program Manager for Data API builder.

0 comments