Skip to content

Commit 3af8b62

Browse files
committed
add sample
1 parent 77cd2d9 commit 3af8b62

File tree

12 files changed

+276
-1
lines changed

12 files changed

+276
-1
lines changed

Microsoft.DurableTask.sln

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client.AzureManaged.Tests",
8181
EndProject
8282
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker.AzureManaged.Tests", "test\Worker\AzureManaged.Tests\Worker.AzureManaged.Tests.csproj", "{B78F1FFD-47AC-45BE-8FF9-0BF8C9F35DEF}"
8383
EndProject
84+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetWebApp", "samples\portable-sdk\dotnet\AspNetWebApp\AspNetWebApp.csproj", "{869D2D51-9372-4764-B059-C43B6C1180A3}"
85+
EndProject
8486
Global
8587
GlobalSection(SolutionConfigurationPlatforms) = preSolution
8688
Debug|Any CPU = Debug|Any CPU
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
8+
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
9+
10+
<!-- Disable automatic assembly attribute generation -->
11+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
12+
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="Azure.Identity" Version="1.13.1" />
17+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
18+
<PackageReference Include="Microsoft.DurableTask.Generators" Version="1.0.0-preview.1" OutputItemType="Analyzer" />
19+
20+
<!-- Reference to the Azure Managed client and worker projects -->
21+
<ProjectReference Include="../../src/Client/AzureManaged/Client.AzureManaged.csproj" />
22+
<ProjectReference Include="../../src/Worker/AzureManaged/Worker.AzureManaged.csproj" />
23+
<ProjectReference Include="../../src/Shared/AzureManaged/Shared.AzureManaged.csproj" />
24+
</ItemGroup>
25+
26+
</Project>

samples/AspNetWebApp/DockerFile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
2+
3+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
4+
WORKDIR /app
5+
EXPOSE 8080
6+
7+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
8+
WORKDIR /src
9+
COPY ["AspNetWebApp.csproj", "."]
10+
RUN dotnet restore "./AspNetWebApp.csproj"
11+
COPY . .
12+
WORKDIR "/src/."
13+
RUN dotnet build "AspNetWebApp.csproj" -c Release -o /app/build
14+
15+
FROM build AS publish
16+
RUN dotnet publish "AspNetWebApp.csproj" -c Release -o /app/publish /p:UseAppHost=false
17+
18+
FROM base AS final
19+
WORKDIR /app
20+
COPY --from=publish /app/publish .
21+
ENV ASPNETCORE_ENVIRONMENT=Production
22+
ENTRYPOINT ["dotnet", "AspNetWebApp.dll"]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Microsoft.DurableTask;
2+
using Microsoft.DurableTask.Client;
3+
4+
namespace AspNetWebApp.Scenarios;
5+
6+
[DurableTask]
7+
class HelloCities : TaskOrchestrator<string, List<string>>
8+
{
9+
public override async Task<List<string>> RunAsync(TaskOrchestrationContext context, string input)
10+
{
11+
List<string> results =
12+
[
13+
await context.CallSayHelloAsync("Seattle"),
14+
await context.CallSayHelloAsync("Amsterdam"),
15+
await context.CallSayHelloAsync("Hyderabad"),
16+
await context.CallSayHelloAsync("Shanghai"),
17+
await context.CallSayHelloAsync("Tokyo"),
18+
];
19+
return results;
20+
}
21+
}
22+
23+
[DurableTask]
24+
class SayHello : TaskActivity<string, string>
25+
{
26+
public override Task<string> RunAsync(TaskActivityContext context, string cityName)
27+
{
28+
return Task.FromResult($"Hello, {cityName}!");
29+
}
30+
}

samples/AspNetWebApp/Program.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.Text.Json.Serialization;
2+
using Azure.Core;
3+
using Azure.Identity;
4+
using Microsoft.DurableTask;
5+
using Microsoft.DurableTask.Client;
6+
using Microsoft.DurableTask.Worker;
7+
using Microsoft.DurableTask.Worker.AzureManaged;
8+
using Microsoft.DurableTask.Client.AzureManaged;
9+
10+
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
11+
12+
string endpointAddress = builder.Configuration["DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS"]
13+
?? throw new InvalidOperationException("Missing required configuration 'DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS'");
14+
15+
string taskHubName = builder.Configuration["DURABLE_TASK_SCHEDULER_TASK_HUB_NAME"]
16+
?? throw new InvalidOperationException("Missing required configuration 'DURABLE_TASK_SCHEDULER_TASK_HUB_NAME'");
17+
18+
TokenCredential credential = builder.Environment.IsProduction()
19+
? new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = builder.Configuration["CONTAINER_APP_UMI_CLIENT_ID"] })
20+
: new DefaultAzureCredential();
21+
22+
// Add all the generated orchestrations and activities automatically
23+
builder.Services.AddDurableTaskWorker(builder =>
24+
{
25+
builder.AddTasks(r => r.AddAllGeneratedTasks());
26+
builder.UseDurableTaskScheduler(endpointAddress, taskHubName, credential);
27+
});
28+
29+
// Register the client, which can be used to start orchestrations
30+
builder.Services.AddDurableTaskClient(builder =>
31+
{
32+
builder.UseDurableTaskScheduler(endpointAddress, taskHubName, credential);
33+
});
34+
35+
// Configure console logging using the simpler, more compact format
36+
builder.Services.AddLogging(logging =>
37+
{
38+
logging.AddSimpleConsole(options =>
39+
{
40+
options.SingleLine = true;
41+
options.UseUtcTimestamp = true;
42+
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss.fffZ ";
43+
});
44+
});
45+
46+
// Configure the HTTP request pipeline
47+
builder.Services.AddControllers().AddJsonOptions(options =>
48+
{
49+
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
50+
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
51+
});
52+
53+
// The actual listen URL can be configured in environment variables named "ASPNETCORE_URLS" or "ASPNETCORE_URLS_HTTPS"
54+
WebApplication app = builder.Build();
55+
app.MapControllers();
56+
app.Run();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"iisSettings": {
4+
"windowsAuthentication": false,
5+
"anonymousAuthentication": true,
6+
"iisExpress": {
7+
"applicationUrl": "http://localhost:36209",
8+
"sslPort": 0
9+
}
10+
},
11+
"profiles": {
12+
"http": {
13+
"commandName": "Project",
14+
"dotnetRunMessages": true,
15+
"applicationUrl": "http://localhost:5008",
16+
"environmentVariables": {
17+
"ASPNETCORE_ENVIRONMENT": "Development",
18+
"DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS": "https://wbtestdts02-g7ahczeycua9.westus2.durabletask.io",
19+
"DURABLE_TASK_SCHEDULER_TASK_HUB_NAME": "wbtb100"
20+
}
21+
}
22+
}
23+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Diagnostics;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.DurableTask;
4+
using Microsoft.DurableTask.Client;
5+
6+
namespace AspNetWebApp;
7+
8+
[Route("scenarios")]
9+
[ApiController]
10+
public partial class ScenariosController(
11+
DurableTaskClient durableTaskClient,
12+
ILogger<ScenariosController> logger) : ControllerBase
13+
{
14+
readonly DurableTaskClient durableTaskClient = durableTaskClient;
15+
readonly ILogger<ScenariosController> logger = logger;
16+
17+
[HttpPost("hellocities")]
18+
public async Task<ActionResult> RunHelloCities([FromQuery] int? count, [FromQuery] string? prefix)
19+
{
20+
if (count is null || count < 1)
21+
{
22+
return this.BadRequest(new { error = "A 'count' query string parameter is required and it must contain a positive number." });
23+
}
24+
25+
// Generate a semi-unique prefix for the instance IDs to simplify tracking
26+
prefix ??= $"hellocities-{count}-";
27+
prefix += DateTime.UtcNow.ToString("yyyyMMdd-hhmmss");
28+
29+
this.logger.LogInformation("Scheduling {count} orchestrations with a prefix of '{prefix}'...", count, prefix);
30+
31+
Stopwatch sw = Stopwatch.StartNew();
32+
await Enumerable.Range(0, count.Value).ParallelForEachAsync(1000, i =>
33+
{
34+
string instanceId = $"{prefix}-{i:X16}";
35+
return this.durableTaskClient.ScheduleNewHelloCitiesInstanceAsync(
36+
input: null!,
37+
new StartOrchestrationOptions(instanceId));
38+
});
39+
40+
sw.Stop();
41+
this.logger.LogInformation(
42+
"All {count} orchestrations were scheduled successfully in {time}ms!",
43+
count,
44+
sw.ElapsedMilliseconds);
45+
return this.Ok(new
46+
{
47+
message = $"Scheduled {count} orchestrations prefixed with '{prefix}' in {sw.ElapsedMilliseconds}."
48+
});
49+
}
50+
}

samples/AspNetWebApp/Utils.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace AspNetWebApp;
2+
3+
static class Utils
4+
{
5+
public static async Task ParallelForEachAsync<T>(this IEnumerable<T> items, int maxConcurrency, Func<T, Task> action)
6+
{
7+
List<Task> tasks;
8+
if (items is ICollection<T> itemCollection)
9+
{
10+
tasks = new List<Task>(itemCollection.Count);
11+
}
12+
else
13+
{
14+
tasks = [];
15+
}
16+
17+
using SemaphoreSlim semaphore = new(maxConcurrency);
18+
foreach (T item in items)
19+
{
20+
tasks.Add(InvokeThrottledAction(item, action, semaphore));
21+
}
22+
23+
await Task.WhenAll(tasks);
24+
}
25+
26+
static async Task InvokeThrottledAction<T>(T item, Func<T, Task> action, SemaphoreSlim semaphore)
27+
{
28+
await semaphore.WaitAsync();
29+
try
30+
{
31+
await action(item);
32+
}
33+
finally
34+
{
35+
semaphore.Release();
36+
}
37+
}
38+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Debug",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS": "https://{your-durable-task-endpoint}.durabletask.io",
9+
"DURABLE_TASK_SCHEDULER_TASK_HUB_NAME": "{your-task-hub-name}",
10+
"CONTAINER_APP_UMI_CLIENT_ID": "{your-user-managed-identity-client-id}"
11+
}

0 commit comments

Comments
 (0)