I'm currently experimenting with .NET Aspire. According to docs, Aspire is used to streamline connections and communication between services in applications. One of the features that Aspire offers is the ease of communications between services, by giving each a special name that can be resolved via them, removing the need to manually set the ports and match the addresses for making services connect.
In the .NET Aspire Quickstart Documentation, is it explained that you can set custom names to your projects in the AppHost Project's Program.cs file like this:
var builder = DistributedApplication.CreateBuilder (args);
var usersApi = builder
.AddProject<Projects.Bargeh_Users_API> ("usersapi"); // Setting the name "usersapi" to the project
builder.AddProject<Projects.Bargeh_Main_Wapp> ("bargehmainwapp")
.WithReference (usersApi)
.WithReference (smsApi)
.WithLaunchProfile ("https");
builder.Build ().Run ();
Again from the documentation, you can add this code in the project that wants to have access to the other service and let Aspire know that you need that service and resolve that URL for you:
builder.Services.AddHttpClient<UsersApiHttpClientProvider>(
static client=> client.BaseAddress = new("http://usersapi"));
Then, you can inject UsersApiHttpClientProvider wherever you want and make use of it.
This approach works, but only for accessing APIs that are open to normal HTTP requests. The problem is, that the UsersApi that I'm using is not a REST API, but it's a GRPC service. I can connect to the API and send requests to it, but I'm unable to send real GRPC requests.
My question is, how can I make my client projects (a Blazor Web App in this case) connect to my backend GRPC Services using Grpc.Net packages, while the connection between them is established by .NET Aspire?
So, after 2 days of investigation, comparing with implementation of existing modules and a lot of attempts I guess I figured out how it should be done.
Code of AppHost for me looks like following:
Here
Projects.Serveris an application with Grpc server.Now the trickiest part. After analyzing approaches MS uses for connecting other services like Sql Server I can conclude that they use different
*Annotationclasses that are able to to convert Resource (in my case Project reference) to real connection string, for example from launch settings or IP + port from running container or some static values.The produced connection strings depending on annotation can be passedeither as connection strings or as
services__<registration name>__<index>as Environment Variables.Both can be read from
IConfigurationas default app builders connect by default Environment Variables Configuration Source..AddSqlServerContainer(...)and.AddSqlServerConnection(...)produce for example produces connection strings with registered name..WithReference(<project>)produces service annotation. So you have to look to get the connection string like followingAs alternative you can do binding using Option Pattern approach. Already implemented client side code uses binding to some specific section to pick main set of settings and put them into Settings class, then manually resolve connection string using
.GetConnectionString()method onIConfigurationclass and then assign connection string to the Settings class.So I conclude that Grpc channel and client should be constructed as
Factories or other mechanisms to do it as for your flavor.
IMPORTANT NOTE: For me
.WithReference()produces TWO connection strings like:The first one is not consumable locally as cannot be resolved. The second one contains port that differs from the port I see in Aspire UI and from what you see in
launchSettings.jsonbut it actually works. I didn't get exactly why I have two urls yet but at least I can peak correct one manually or with skipping one starting withhttp://_httpIn theory you can add your own annotation with
.WithAnotation()after you add register project inHostor re-use existing annotation with the same method. That annotation might produce also connection string for example and you will be able consume the connection string.I hope it helps you.
UPDATE: It seems that two URL is intentional behavior and one of them is what they call "Named endpoint". See named-endpoints