Configuring SQL Server dependencies in ASP.NET microservices with Tye
Over the past few weeks I've been playing with Tye, and I've been really impressed so far. Tye is designed to simplify the process of developing microservices with ASP.NET Core.
It is able to detect all of the microservices in a solution and start them all up with a single tye run
command, enabling them to easily discover each other. It provides some great added-value features as well, such as a dashboard showing you all of the services and enabling you to access their logs.
But the feature I want to focus on in this post, is the ability for Tye to automatically start containerized dependencies. This means I can automate starting up a SQL database (following on from my recent post about containerizing SQL databases).
Tye Configuration
For Tye to know what containerized dependencies your microservices rely on, you do need to specify a configuration file. This is easily generated with the tye init
command, and good effort has been made to keep the contents minimal (as many of us have had enough of YAML overload).
Here's a section I added under the services:
part of my tye.yaml
file. It specifies that we want to run the SQL Server Docker image, giving it the name of sqlserver
, and providing the environment variables we want to use.
With bindings, I can say what port I want to expose on the container, and what is really nice is that I can construct a connection string that will be automatically made available to all other microservices via an environment variable.
- name: sqlserver
image: mcr.microsoft.com/mssql/server:2019-latest
env:
- name: SA_PASSWORD
value: "V0teM!cr0s3rv!ce"
- name: ACCEPT_EULA
value: 'Y'
bindings:
- port: 1433
connectionString: Server=${host};User Id=sa;Database=VoteDB;Password=${env:SA_PASSWORD};
Using a Volume
It's probably a good idea to store the SQL database data in a volume, so that we can tear down our containers without losing our data. That can easily be set up by adding another section to the sqlserver
service in our tye.yaml
file.
volumes:
- name: vote-storage
target: /var/opt/mssql
Named bindings
So far, so easy, but I wanted to use my SQL container to host two databases, and so generate separate connection strings for each database. That proved tricky as you need to create what are called "named" bindings in Tye, and documentation is somewhat lacking in this area.
Eventually I discovered the syntax to create named bindings, allowing me to set up two connection string bindings. To make this work, I also had to give a name to the default binding. Exploring the Tye source code, it looks like this binding should be called "http".
bindings:
- name: http # think this is the name needed for the port
port: 1433
- name: voteDb
connectionString: Server=${host};User Id=sa;Database=VoteDB;Password=${env:SA_PASSWORD};
- name: queueDb
connectionString: Server=${host};User Id=sa;Database=VoteQueueDb;Password=${env:SA_PASSWORD};
With this in place, any microservice that wants to access one of these named bindings can do so with a simple call to the GetConnectionString
extension method provided by Tye:
config.GetConnectionString("sqlserver", "queueDb");
Summary
Tye is a really simple and powerful way to start up multiple ASP.NET Core microservices, along with all their dependencies and configure everything to communicate with each other. I recommend experimenting with it if you are embarking on a microservices project with ASP.NET Core.
For reference, here's the entire tye.yaml
file I used in my demo app:
name: VoteTye
ingress:
- name: ingress
bindings:
- port: 8080
rules:
- path: /vote
service: voteweb
- path: /results
service: resultsweb
services:
- name: VoteService
project: VoteService/VoteService.csproj
- name: voteweb
project: VoteWebApp/VoteWebApp.csproj
- name: resultsweb
project: ResultWebApp/ResultWebApp.csproj
- name: sqlserver
image: mcr.microsoft.com/mssql/server:2019-latest
env:
- name: SA_PASSWORD
value: "V0teM!cr0s3rv!ce"
- name: ACCEPT_EULA
value: 'Y'
volumes:
- name: vote-storage
target: /var/opt/mssql
bindings:
- name: http # think this is the name needed for the port
port: 1433
- name: voteDb
connectionString: Server=${host};User Id=sa;Database=VoteDB;Password=${env:SA_PASSWORD};
- name: queueDb
connectionString: Server=${host};User Id=sa;Database=VoteQueueDb;Password=${env:SA_PASSWORD};