The easiest way to connect .NET Aspire to any database
There are many ways you can integrate databases with .NET Aspire. Previously, I already wrote about both SQL and NoSQL database integrations.
.NET Aspire can manage the entire database provisioning for you if you choose to use any provider-specific integration libraries. There are also various additional configuration options you can apply to connect .NET Aspire to existing databases hosted elsewhere.
But what if I told you that there is a mechanism that allows you to significantly simplify the process of connecting your .NET Aspire instance to an existing database? You don’t even need to use any provider-specific integration libraries in your Aspire host application.
Moreover, the same method will work for any database type, whether it’s SQL Server, Postgres, Mongo, or something else entirely!
But before exploring what this method is, let’s address the problem we are trying to solve. That is, why would you want to connect your distributed application to an external database to begin with?
Why would you want to connect to an external database
By default, if you use database-specific integration libraries, .NET Aspire hosts the database server inside a Docker container with a persistent runtime. This makes things very convenient for running things on a development machine. All you have to do is press a button, and your entire database gets provisioned!
But, if you’ve been working with application back-end and infrastructure for some time, you will know that it’s not a good thing to do in production. Docker containers are meant to be transient, while your database is meant to be persistent. Docker containers are meant to be somewhat stateless, while databases are obviously meant to be stateful.
Granted, with a persistent runtime, your Docker container will try to retain its state for as long as it can. However, it’s still at the mercy of its orchestrator, whether it’s Kubernetes, Docker Swarm, or anything else.
If you have a lot of sensitive customer data, losing it all may put you out of business. And if you run your databases in a Docker container, then this is something within the realm of possibility.
This is why, in production, you would typically not run your database in a container. You would host a standard database on a standard database server. For example, you can create a SQL Server instance in Azure and connect your applications to it. This way, the database remains practically forever (or until someone either deletes the database or the server platform).
With modern tools, you won’t have to worry about something happening to the host hardware. You would typically host a database in the cloud, and it will be distributed between multiple copies of physical hardware. Therefore, if the physical server stops working, your database will still run on another physical server. I am planning to talk about how exactly this happens in one of my future articles.
However, in a development environment, you probably want some mechanism that allows you to reset your database to the “factory settings” quickly and conveniently. This is precisely why running a database in a Docker container is a good idea.
The good news is that .NET Aspire allows you to implement the hybrid approach, where your development database is hosted inside a Docker container and your production database is an existing managed instance. We will cover this approach in this article too.
For now, let’s get familiar with the core mechanism of connecting Aspire-hosted apps to an existing database.
Integrating .NET Aspire with any database
So, let’s cut to the chase. To connect your .NET Aspire to any database of any type, you will need to invoke the AddConnectionString()
method on the distributed application builder inside the Program.cs
file of the host application.
As the name suggests, the reason this method works is that it connects to a database via a pre-defined connection string. And it works with any connection string and any database, whether it’s SQL Server, Oracle, or something else entirely, like Mongo.
Here’s a code snippet that shows how it’s done:
var builder = DistributedApplication.CreateBuilder(args);
var sqldb = builder.AddConnectionString("sqldb");
var apiService = builder
.AddProject<Projects.AspireApp_ApiService>("apiservice")
.WaitFor(sqldb)
.WithReference(sqldb);
builder.AddProject<Projects.AspireApp_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService);
builder.Build().Run();
Here’s a step-by-step breakdown of what we do:
We create a service by invoking the
AddConnectionString()
method and store it in thesqldb
variableWe use the
WithReference()
method to pass thesqldb
variable as a reference to a service that we want to connect to the database
So, how exactly does the AddConnectionString()
method know what connection string to pick up? After all, we only passed the sqldb string into it.
Well, the string value that we pass into it, while being arbitrary, has a dual purpose:
It acts as the resource name for service discovery, just like any other named resource in .NET Aspire
It points to a specific connection string in the settings
So, the actual connection string needs to be set under the ConnectionStrings section of the settings, such as the appsettings.json file. The section must have the entry that has the same key as the string we passed into the method and a valid database connection string as the value. It will look similar to this:
"ConnectionStrings": {
"sqldb": "{your connection string}"
}
So, we connected our Aspire host to an external database. We passed its connection string reference into the service that we want to connect to the database. Now, we need to establish the actual connection.
Connecting your apps to the database
Connecting applications to an Aspire database is easy. All you need to do is install the appropriate client package that will match the database type. The standard service discovery mechanism will take care of the rest.
You can find an example of its implementation here. But let’s go through it.
In our example, it’s assumed that our API service, hosted by .NET Aspire, connects to a SQL Server database. Therefore, we will install one of the NuGet packages specific to SQL Server. It can be either of these:
Aspire.Microsoft.Data.SqlClient
Aspire.Microsoft.EntityFrameworkCore.SqlServer
Let’s assume we chose the latter. Then, to establish the database connection, all we have to do is register our database dependencies in the Program.cs
file while passing the connection string reference name, as the following snippet demonstrates:
builder.AddSqlServerDbContext<WeatherDbContext>("sqldb");
As long as we haven’t forgotten to pass the connection string reference into this service reference, the .NET Aspire service discovery mechanism will be able to match the name with the right connection string. And we don’t have to explicitly store the actual connection string inside every service where we need to use the database connection.
Wrapping up
As you could see, connecting .NET Aspire to an existing database is easy. The brilliant developers who built .NET Aspire made it clever enough to work with any database type in exactly the same way. All you need to do is make sure you use the correct client library to connect to it.
If you have been enjoying my articles on .NET Aspire, then you may want to consider getting my book, which is currently available in early access. I wrote it with the help of the development team from Microsoft that built .NET Aspire, so you’ll find some things in the book that you cannot easily find online.
A new chapter is about to be added, which talks extensively about integrating Aspire with SQL databases of different types and various gotchas you can encounter during the process.
The book is available here:
Also, if you are finding my content useful, consider subscribing.
I hope you found today's article helpful, and I'll speak to you next time!