Skip to content

Commit 14b94eb

Browse files
committed
sample
sample fix
1 parent 1adf7cc commit 14b94eb

File tree

12 files changed

+427
-0
lines changed

12 files changed

+427
-0
lines changed

Microsoft.DurableTask.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure", "src\Extensions\Azu
7777
EndProject
7878
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Tests", "test\Extensions\Azure\Azure.Tests.csproj", "{DBB5DB4E-A1B0-4C86-A233-213789C46929}"
7979
EndProject
80+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetWebApp", "samples\portable-sdk\dotnet\AspNetWebApp\AspNetWebApp.csproj", "{869D2D51-9372-4764-B059-C43B6C1180A3}"
81+
EndProject
8082
Global
8183
GlobalSection(SolutionConfigurationPlatforms) = preSolution
8284
Debug|Any CPU = Debug|Any CPU
@@ -199,6 +201,10 @@ Global
199201
{DBB5DB4E-A1B0-4C86-A233-213789C46929}.Debug|Any CPU.Build.0 = Debug|Any CPU
200202
{DBB5DB4E-A1B0-4C86-A233-213789C46929}.Release|Any CPU.ActiveCfg = Release|Any CPU
201203
{DBB5DB4E-A1B0-4C86-A233-213789C46929}.Release|Any CPU.Build.0 = Release|Any CPU
204+
{869D2D51-9372-4764-B059-C43B6C1180A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
205+
{869D2D51-9372-4764-B059-C43B6C1180A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
206+
{869D2D51-9372-4764-B059-C43B6C1180A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
207+
{869D2D51-9372-4764-B059-C43B6C1180A3}.Release|Any CPU.Build.0 = Release|Any CPU
202208
EndGlobalSection
203209
GlobalSection(SolutionProperties) = preSolution
204210
HideSolutionNode = FALSE
@@ -237,6 +243,7 @@ Global
237243
{5227C712-2355-403F-90D6-51D0BCAE4D38} = {8AFC9781-F6F1-4696-BB4A-9ED7CA9D612B}
238244
{662BF73D-A4DD-4910-8625-7C12F1ACDBEC} = {5227C712-2355-403F-90D6-51D0BCAE4D38}
239245
{DBB5DB4E-A1B0-4C86-A233-213789C46929} = {E5637F81-2FB9-4CD7-900D-455363B142A7}
246+
{869D2D51-9372-4764-B059-C43B6C1180A3} = {EFF7632B-821E-4CFC-B4A0-ED4B24296B17}
240247
EndGlobalSection
241248
GlobalSection(ExtensibilityGlobals) = postSolution
242249
SolutionGuid = {AB41CB55-35EA-4986-A522-387AB3402E71}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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="Grpc.Net.Client" Version="2.67.0" />
18+
<PackageReference Include="Microsoft.DurableTask.Client.Grpc" Version="1.5.0" />
19+
<PackageReference Include="Microsoft.DurableTask.Worker.Grpc" Version="1.5.0" />
20+
<PackageReference Include="Microsoft.DurableTask.Generators" Version="1.0.0-preview.1" OutputItemType="Analyzer" />
21+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
22+
23+
<!-- Reference to the Azure extensions project -->
24+
<ProjectReference Include="../../../../src/Extensions/Azure/Azure.csproj" />
25+
</ItemGroup>
26+
27+
</Project>
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+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.Extensions.Azure;
8+
9+
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
10+
11+
string endpointAddress = builder.Configuration["DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS"]
12+
?? throw new InvalidOperationException("Missing required configuration 'DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS'");
13+
14+
string taskHubName = builder.Configuration["DURABLE_TASK_SCHEDULER_TASK_HUB_NAME"]
15+
?? throw new InvalidOperationException("Missing required configuration 'DURABLE_TASK_SCHEDULER_TASK_HUB_NAME'");
16+
17+
TokenCredential credential = builder.Environment.IsProduction()
18+
? new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = builder.Configuration["CONTAINER_APP_UMI_CLIENT_ID"] })
19+
: new DefaultAzureCredential();
20+
21+
// Add all the generated orchestrations and activities automatically
22+
builder.Services.AddDurableTaskWorker(builder =>
23+
{
24+
builder.AddTasks(r => r.AddAllGeneratedTasks());
25+
builder.UseDurableTaskScheduler(endpointAddress, taskHubName, credential);
26+
});
27+
28+
// Register the client, which can be used to start orchestrations
29+
builder.Services.AddDurableTaskClient(builder =>
30+
{
31+
builder.UseDurableTaskScheduler(endpointAddress, taskHubName, credential);
32+
});
33+
34+
// Configure console logging using the simpler, more compact format
35+
builder.Services.AddLogging(logging =>
36+
{
37+
logging.AddSimpleConsole(options =>
38+
{
39+
options.SingleLine = true;
40+
options.UseUtcTimestamp = true;
41+
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss.fffZ ";
42+
});
43+
});
44+
45+
// Configure the HTTP request pipeline
46+
builder.Services.AddControllers().AddJsonOptions(options =>
47+
{
48+
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
49+
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
50+
});
51+
52+
// The actual listen URL can be configured in environment variables named "ASPNETCORE_URLS" or "ASPNETCORE_URLS_HTTPS"
53+
WebApplication app = builder.Build();
54+
app.MapControllers();
55+
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://localhost:8082",
19+
"DURABLE_TASK_SCHEDULER_TASK_HUB_NAME": "samples"
20+
}
21+
}
22+
}
23+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Hello World with the Durable Task SDK for .NET
2+
3+
In addition to [Durable Functions](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview), the [Durable Task SDK for .NET](https://github.com/microsoft/durabletask-dotnet) can also use the Durable Task Scheduler service for managing orchestration state.
4+
5+
This directory includes a sample .NET console app that demonstrates how to use the Durable Task Scheduler with the Durable Task SDK for .NET (without any Azure Functions dependency).
6+
7+
## Prerequisites
8+
9+
- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
10+
- [PowerShell](https://docs.microsoft.com/powershell/scripting/install/installing-powershell)
11+
- [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli)
12+
13+
## Creating a Durable Task Scheduler task hub
14+
15+
Before you can run the app, you need to create a Durable Task Scheduler task hub in Azure and produce a connection string that references it.
16+
17+
> **NOTE**: These are abbreviated instructions for simplicity. For a full set of instructions, see the Azure Durable Functions [QuickStart guide](../../../../quickstarts/HelloCities/README.md#create-a-durable-task-scheduler-namespace-and-task-hub).
18+
19+
1. Install the Durable Task Scheduler CLI extension:
20+
21+
```bash
22+
az upgrade
23+
az extension add --name durabletask --allow-preview true
24+
```
25+
26+
1. Create a resource group:
27+
28+
```powershell
29+
az group create --name my-resource-group --location northcentralus
30+
```
31+
32+
1. Create a Durable Task Scheduler namespace:
33+
34+
```powershell
35+
az durabletask namespace create -g my-resource-group --name my-namespace
36+
```
37+
38+
1. Create a task hub within the namespace:
39+
40+
```powershell
41+
az durabletask taskhub create -g my-resource-group --namespace my-namespace --name "portable-dotnet"
42+
```
43+
44+
1. Grant the current user permission to connect to the `portable-dotnet` task hub:
45+
46+
```powershell
47+
$subscriptionId = az account show --query "id" -o tsv
48+
$loggedInUser = az account show --query "user.name" -o tsv
49+
50+
az role assignment create `
51+
--assignee $loggedInUser `
52+
--role "Durable Task Data Contributor" `
53+
--scope "/subscriptions/$subscriptionId/resourceGroups/my-resource-group/providers/Microsoft.DurableTask/namespaces/my-namespace/taskHubs/portable-dotnet"
54+
```
55+
56+
Note that it may take a minute for the role assignment to take effect.
57+
58+
1. Get the endpoint for the scheduler resource and save it to the `DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS` environment variable:
59+
60+
```powershell
61+
$endpoint = az durabletask namespace show `
62+
-g my-resource-group `
63+
-n my-namespace `
64+
--query "properties.url" `
65+
-o tsv
66+
$env:DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS = $endpoint
67+
```
68+
69+
The `DURABLE_TASK_SCHEDULER_ENDPOINT_ADDRESS` environment variable is used by the sample app to connect to the Durable Task Scheduler resource.
70+
71+
1. Save the task hub name to the `DURABLE_TASK_SCHEDULER_TASK_HUB_NAME` environment variable:
72+
73+
```powershell
74+
$env:DURABLE_TASK_SCHEDULER_TASK_HUB_NAME = "portable-dotnet"
75+
```
76+
77+
The `DURABLE_TASK_SCHEDULER_TASK_HUB_NAME` environment variable is to configure the sample app with the correct task hub resource name.
78+
79+
## Running the sample
80+
81+
In the same terminal window as above, use the following steps to run the sample on your local machine.
82+
83+
1. Clone this repository.
84+
85+
1. Open a terminal window and navigate to the `samples/portable-sdk/dotnet/AspNetWebApp` directory.
86+
87+
1. Run the following command to build and run the sample:
88+
89+
```bash
90+
dotnet run
91+
```
92+
93+
You should see output similar to the following:
94+
95+
```plaintext
96+
Building...
97+
info: Microsoft.DurableTask[1]
98+
Durable Task gRPC worker starting.
99+
info: Microsoft.Hosting.Lifetime[14]
100+
Now listening on: http://localhost:5008
101+
info: Microsoft.Hosting.Lifetime[0]
102+
Application started. Press Ctrl+C to shut down.
103+
info: Microsoft.Hosting.Lifetime[0]
104+
Hosting environment: Development
105+
info: Microsoft.Hosting.Lifetime[0]
106+
Content root path: D:\projects\Azure-Functions-Durable-Task-Scheduler-Private-Preview\samples\portable-sdk\dotnet\AspNetWebApp
107+
info: Microsoft.DurableTask[4]
108+
Sidecar work-item streaming connection established.
109+
```
110+
111+
## View orchestrations in the dashboard
112+
113+
You can view the orchestrations in the Durable Task Scheduler dashboard by navigating to the namespace-specific dashboard URL in your browser.
114+
115+
Use the following PowerShell command to get the dashboard URL:
116+
117+
```powershell
118+
$baseUrl = az durabletask namespace show `
119+
-g my-resource-group `
120+
-n my-namespace `
121+
--query "properties.dashboardUrl" `
122+
-o tsv
123+
$dashboardUrl = "$baseUrl/taskHubs/portable-dotnet"
124+
$dashboardUrl
125+
```
126+
127+
The URL should look something like the following:
128+
129+
```plaintext
130+
https://my-namespace-atdngmgxfsh0-db.northcentralus.durabletask.io/taskHubs/portable-dotnet
131+
```
132+
133+
Once logged in, you should see the orchestrations that were created by the sample app. Below is an example of what the dashboard might look like (note that some of the details will be different than the screenshot):
134+
135+
![Durable Task Scheduler dashboard](/media/images/dtfx-sample-dashboard.png)
136+
137+
138+
## Optional: Deploy to Azure Container Apps
139+
1. Create an container app following the instructions in the [Azure Container App documentation](https://learn.microsoft.com/azure/container-apps/get-started?tabs=bash).
140+
2. During step 1, specify the deployed container app code folder at samples\portable-sdk\dotnet\AspNetWebApp
141+
3. Follow the instructions to create a user managed identity and assign the `Durable Task Data Contributor` role then attach it to the container app you created in step 1 at [Azure-Functions-Durable-Task-Scheduler-Private-Preview](..\..\..\..\docs\configure-existing-app.md#run-the-app-on-azure-net). Please skip section "Add required environment variables to app" since these environment variables are not required for deploying to container app.
142+
4. Call the container app endpoint at `http://sampleapi-<your-container-app-name>.azurecontainerapps.io/api/orchestrators/HelloCities`, Sample curl command:
143+
144+
```bash
145+
curl -X POST "https://sampleapi-<your-container-app-name>.azurecontainerapps.io/api/orchestrators/HelloCities"
146+
```
147+
5. You should see the orchestration created in the Durable Task Scheduler dashboard.
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+
}

0 commit comments

Comments
 (0)