Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for 8.2 #49

Merged
merged 10 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Come learn all about [.NET Aspire](https://learn.microsoft.com/dotnet/aspire/),

- **Orchestration**: Built-in orchestration with a simple, yet powerful, workflow engine.Use C# and familiar APIs without a line of YAML. Easily add popular cloud services, connect them to your projects, and run locally with a single click.
- **Service Discovery**: Automatic injection the right connection strings or network configurations and service discovery information to simplify the developer experience.
- **Components**: Built-in components for common cloud services like databases, queues, and storage. Integrated with logging, health checks, telemetry, and more.
- **Integrations**: Built-in integrations for common cloud services like databases, queues, and storage. Configured for logging, health checks, telemetry, and more.
- **Dashboard**: See live OpenTelemetry data with no configuration required. Launched by default on run, .NET Aspire's developer dashboard shows logs, environment variables, distributed traces, metrics and more to quickly verify app behavior.
- **Deployment**: manages injecting the right connection strings or network configurations and service discovery information to simplify the developer experience.
- **So Much More**: .NET Aspire is packed full of features that developers will love and help you be more productive.
Expand Down Expand Up @@ -48,7 +48,7 @@ This .NET Aspire workshop is part of the [Let's Learn .NET](https://aka.ms/letsl
1. [Service Defaults](./workshop/2-servicedefaults.md)
1. [Developer Dashboard & Orchestration](./workshop/3-dashboard-apphost.md)
1. [Service Discovery](./workshop/4-servicediscovery.md)
1. [Components](./workshop/5-components.md)
1. [Integrations](./workshop/5-integrations.md)
1. [Deployment](./workshop/6-deployment.md)

A full slide deck is available for this workshop [here](./workshop/AspireWorkshop.pptx).
Expand Down
6 changes: 3 additions & 3 deletions complete/Api/Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.StackExchange.Redis.OutputCaching" Version="8.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.7" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Aspire.StackExchange.Redis.OutputCaching" Version="8.2.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
</ItemGroup>


Expand Down
244 changes: 118 additions & 126 deletions complete/Api/Data/NwsManager.cs
Original file line number Diff line number Diff line change
@@ -1,136 +1,128 @@
using Api.Data;
using System.Text.Json;
using System.Web;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.Extensions.Caching.Memory;
using System.Text.Json;
using Api.Data;

namespace Api
{

public class NwsManager(HttpClient httpClient, IMemoryCache cache)
{
JsonSerializerOptions options = new()
{
PropertyNameCaseInsensitive = true
};

public async Task<Zone[]?> GetZonesAsync()
{
return await cache.GetOrCreateAsync("zones", async entry =>
{
if (entry is null)
return [];

entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);

// To get the live zone data from NWS, uncomment the following code and comment out the return statement below. This is required if you are deploying to ACA
//var response = await httpClient.GetAsync("https://api.weather.gov/zones?type=forecast");
//response.EnsureSuccessStatusCode();
//var content = await response.Content.ReadAsStringAsync();
//var zones = JsonSerializer.Deserialize<ZonesResponse>(content, options);
//return zones?.Features
// ?.Where(f => f.Properties?.ObservationStations?.Count > 0)
// .Select(f => (Zone)f)
// .Distinct()
// .ToArray() ?? [];


// Deserialize the zones.json file from the wwwroot folder
var zonesJson = File.Open("wwwroot/zones.json", FileMode.Open);
if (zonesJson is null)
return [];

var zones = await JsonSerializer.DeserializeAsync<ZonesResponse>(zonesJson, options);

return zones?.Features
?.Where(f => f.Properties?.ObservationStations?.Count > 0)
.Select(f => (Zone)f)
.Distinct()
.ToArray() ?? [];
});

}

static int forecastCount = 0;
public async Task<Forecast[]> GetForecastByZoneAsync(string zoneId)
{

forecastCount++;
if (forecastCount % 5 == 0)
{
throw new Exception("Random exception thrown by NwsManager.GetForecastAsync");
}

var response = await httpClient.GetAsync($"https://api.weather.gov/zones/forecast/{zoneId}/forecast");
response.EnsureSuccessStatusCode();
var forecasts = await response.Content.ReadFromJsonAsync<ForecastResponse>(options);
return forecasts?.Properties?.Periods?.Select(p => (Forecast)p).ToArray() ?? [];
}

}

public class NwsManager(HttpClient httpClient, IMemoryCache cache, IWebHostEnvironment webHostEnvironment)
{
private static readonly JsonSerializerOptions options = new()
{
PropertyNameCaseInsensitive = true
};

public async Task<Zone[]?> GetZonesAsync()
{
return await cache.GetOrCreateAsync("zones", async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);

// To get the live zone data from NWS, uncomment the following code and comment out the return statement below.
// This is required if you are deploying to ACA.
//var zones = await httpClient.GetFromJsonAsync<ZonesResponse>("https://api.weather.gov/zones?type=forecast", options);
//return zones?.Features
// ?.Where(f => f.Properties?.ObservationStations?.Count > 0)
// .Select(f => (Zone)f)
// .Distinct()
// .ToArray() ?? [];

// Deserialize the zones.json file from the wwwroot folder
var zonesFilePath = Path.Combine(webHostEnvironment.WebRootPath, "zones.json");
if (!File.Exists(zonesFilePath))
{
return [];
}

using var zonesJson = File.OpenRead(zonesFilePath);
var zones = await JsonSerializer.DeserializeAsync<ZonesResponse>(zonesJson, options);

return zones?.Features
?.Where(f => f.Properties?.ObservationStations?.Count > 0)
.Select(f => (Zone)f)
.Distinct()
.ToArray() ?? [];
});
}

private static int forecastCount = 0;

public async Task<Forecast[]> GetForecastByZoneAsync(string zoneId)
{
// Create an exception every 5 calls to simulate and error for testing
DamianEdwards marked this conversation as resolved.
Show resolved Hide resolved
forecastCount++;

if (forecastCount % 5 == 0)
{
throw new Exception("Random exception thrown by NwsManager.GetForecastAsync");
}

var zoneIdSegment = HttpUtility.UrlEncode(zoneId);
var zoneUrl = $"https://api.weather.gov/zones/forecast/{zoneIdSegment}/forecast";
var forecasts = await httpClient.GetFromJsonAsync<ForecastResponse>(zoneUrl, options);
return forecasts
?.Properties
?.Periods
?.Select(p => (Forecast)p)
.ToArray() ?? [];
}
}
}

namespace Microsoft.Extensions.DependencyInjection
{


public static class NwsManagerExtensions
{

public static IServiceCollection AddNwsManager(this IServiceCollection services)
{
services.AddHttpClient<Api.NwsManager>(client =>
{
client.BaseAddress = new Uri("https://api.weather.gov/");
client.DefaultRequestHeaders.Add("User-Agent", "Microsoft - .NET Aspire Demo");
});

services.AddMemoryCache();

return services;
}

public static WebApplication? MapApiEndpoints(this WebApplication? app)
{
if(app is null)
return null;

app.UseOutputCache();

app.MapGet("/zones", async (Api.NwsManager manager) =>
{
var zones = await manager.GetZonesAsync();
return TypedResults.Ok(zones);
})
.WithName("GetZones")
.CacheOutput(policy =>
{
policy.Expire(TimeSpan.FromHours(1));
})
.WithOpenApi();

app.MapGet("/forecast/{zoneId}", async Task<Results<Ok<Api.Forecast[]>, NotFound>> (Api.NwsManager manager, string zoneId) =>
{
try
{
var forecasts = await manager.GetForecastByZoneAsync(zoneId);
return TypedResults.Ok(forecasts);
}
catch (HttpRequestException ex)
{
return TypedResults.NotFound();
}
})
.WithName("GetForecastByZone")
.CacheOutput(policy =>
{
policy.Expire(TimeSpan.FromMinutes(15)).SetVaryByRouteValue("zoneId");
})
.WithOpenApi();

return app;

}

}
public static class NwsManagerExtensions
{
public static IServiceCollection AddNwsManager(this IServiceCollection services)
{
services.AddHttpClient<Api.NwsManager>(client =>
{
client.BaseAddress = new Uri("https://api.weather.gov/");
client.DefaultRequestHeaders.Add("User-Agent", "Microsoft - .NET Aspire Demo");
});

services.AddMemoryCache();

// Add default output caching
services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});

return services;
}

public static WebApplication? MapApiEndpoints(this WebApplication app)
{
app.UseOutputCache();

app.MapGet("/zones", async (Api.NwsManager manager) =>
{
var zones = await manager.GetZonesAsync();
return TypedResults.Ok(zones);
})
.CacheOutput(policy => policy.Expire(TimeSpan.FromHours(1)))
.WithName("GetZones")
.WithOpenApi();

app.MapGet("/forecast/{zoneId}", async Task<Results<Ok<Api.Forecast[]>, NotFound>> (Api.NwsManager manager, string zoneId) =>
{
try
{
var forecasts = await manager.GetForecastByZoneAsync(zoneId);
return TypedResults.Ok(forecasts);
}
catch (HttpRequestException)
{
return TypedResults.NotFound();
}
})
.CacheOutput(policy => policy.Expire(TimeSpan.FromMinutes(15)).SetVaryByRouteValue("zoneId"))
.WithName("GetForecastByZone")
.WithOpenApi();

return app;
}
}
}
20 changes: 2 additions & 18 deletions complete/Api/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
Expand All @@ -20,14 +21,6 @@
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7032;http://localhost:5271"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (.NET SDK)": {
"commandName": "SdkContainer",
"launchBrowser": true,
Expand All @@ -39,14 +32,5 @@
"publishAllPorts": true,
"useSSL": true
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:27734",
"sslPort": 44380
}
}
}
}
4 changes: 2 additions & 2 deletions complete/AppHost/AppHost.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.1.0" />
<PackageReference Include="Aspire.Hosting.Redis" Version="8.1.0" />
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.2.0" />
<PackageReference Include="Aspire.Hosting.Redis" Version="8.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
9 changes: 4 additions & 5 deletions complete/AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache")
.WithRedisCommander();
.WithRedisCommander();

var api = builder.AddProject<Projects.Api>("api")
.WithReference(cache);
.WithReference(cache);

var web = builder.AddProject<Projects.MyWeatherHub>("myweatherhub")
.WithReference(api)
.WithExternalHttpEndpoints();

.WithReference(api)
.WithExternalHttpEndpoints();

builder.Build().Run();
4 changes: 2 additions & 2 deletions complete/MyWeatherHub/MyWeatherHub.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@


<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Client" Version="8.0.7">
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Client" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Loading