post: more work on rough draft
This commit is contained in:
+183
-3
@@ -245,7 +245,6 @@ main {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,22 +270,203 @@ app
|
|||||||
})
|
})
|
||||||
.WithName("AddTask")
|
.WithName("AddTask")
|
||||||
.WithDisplayName("Add Task")
|
.WithDisplayName("Add Task")
|
||||||
.WithDescription("Add a new task to the queue")
|
.WithDescription("Add a new task to the queue");
|
||||||
.WithOpenApi();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Run client and server. Click the add task button. You should see a success message with id of the task just added.
|
Run client and server. Click the add task button. You should see a success message with id of the task just added.
|
||||||
|
|
||||||
### Implement the task queue
|
### Implement the task queue
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class BackgroundTask
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class BackgroundTaskQueue
|
||||||
|
{
|
||||||
|
private readonly Channel<BackgroundTask> _channel = Channel.CreateUnbounded<BackgroundTask>();
|
||||||
|
|
||||||
|
public async Task EnqueueAsync(BackgroundTask task)
|
||||||
|
{
|
||||||
|
await _channel.Writer.WriteAsync(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BackgroundTask> DequeueAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await _channel.Reader.ReadAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Implement the task service
|
### Implement the task service
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class TaskService(BackgroundTaskQueue taskQueue) : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly BackgroundTaskQueue _taskQueue = taskQueue;
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var task = await _taskQueue.DequeueAsync(stoppingToken);
|
||||||
|
|
||||||
|
// Execute the task
|
||||||
|
Console.WriteLine($"Task {task.Id} is starting");
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||||
|
|
||||||
|
Console.WriteLine($"Task {task.Id} is complete");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Update add task endpoint to add task to queue
|
### Update add task endpoint to add task to queue
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
app
|
||||||
|
.MapPost("/add-task", async (BackgroundTaskQueue queue) =>
|
||||||
|
{
|
||||||
|
var task = new BackgroundTask();
|
||||||
|
await queue.EnqueueAsync(task);
|
||||||
|
return Results.Json(data: task, statusCode: (int)HttpStatusCode.Created);
|
||||||
|
})
|
||||||
|
.WithName("AddTask")
|
||||||
|
.WithDisplayName("Add Task")
|
||||||
|
.WithDescription("Add a new task to the queue");
|
||||||
|
```
|
||||||
|
|
||||||
### Let's add signal r hub
|
### Let's add signal r hub
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
builder.Services.AddSignalR();
|
||||||
|
|
||||||
|
app.MapHub<TaskHub>("/task-hub");
|
||||||
|
```
|
||||||
|
|
||||||
### Add client code to establish connection to server hub
|
### Add client code to establish connection to server hub
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install @microsoft/signalr
|
npm install @microsoft/signalr
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
import { HubConnectionBuilder } from '@microsoft/signalr'
|
||||||
|
|
||||||
|
const updates = ref<string[]>([])
|
||||||
|
|
||||||
|
const connection = new HubConnectionBuilder().withUrl('https://localhost:7138/task-hub').build()
|
||||||
|
|
||||||
|
connection.on('ReceiveMessage', (message: string) => {
|
||||||
|
console.log(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
try {
|
||||||
|
connection.start()
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
try {
|
||||||
|
connection.stop()
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update server CORS policy to allow signal r connections
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
...
|
||||||
|
builder.Services.AddCors(
|
||||||
|
options =>
|
||||||
|
options.AddDefaultPolicy(
|
||||||
|
builder => builder
|
||||||
|
.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials()
|
||||||
|
.WithOrigins("https://localhost:5173")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update server to actually send updates to client as tasks are processed
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class TaskService(
|
||||||
|
BackgroundTaskQueue taskQueue,
|
||||||
|
IHubContext<TaskHub> taskHub
|
||||||
|
) : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly BackgroundTaskQueue _taskQueue = taskQueue;
|
||||||
|
private readonly IHubContext<TaskHub> _taskHub = taskHub;
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var task = await _taskQueue.DequeueAsync(stoppingToken);
|
||||||
|
|
||||||
|
// Execute the task
|
||||||
|
await _taskHub.Clients.All.SendAsync("ReceiveMessage", $"Task {task.Id} is starting", cancellationToken: stoppingToken);
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||||
|
|
||||||
|
await _taskHub.Clients.All.SendAsync("ReceiveMessage", $"Task {task.Id} is complete", cancellationToken: stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update client to display updates instead of logging them
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
import { HubConnectionBuilder } from '@microsoft/signalr'
|
||||||
|
|
||||||
|
const updates = ref<string[]>([])
|
||||||
|
|
||||||
|
const connection = new HubConnectionBuilder().withUrl('https://localhost:7138/task-hub').build()
|
||||||
|
|
||||||
|
connection.on('ReceiveMessage', (message: string) => {
|
||||||
|
updates.value = [...updates.value, message]
|
||||||
|
})
|
||||||
|
...
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
...
|
||||||
|
<div class="updates-container">
|
||||||
|
<h2>Updates</h2>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(update, index) in updates" :key="index">{{ update }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Now you can run the client and server and see the updates as tasks are processed
|
||||||
|
|
||||||
|
Pretty cool huh?
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Probably mentioned typed hubs here.
|
||||||
|
|||||||
Reference in New Issue
Block a user