我正在和SignalR一起研究实时数据网格我有一个SignalR集线器
- 客户端向服务器发送
GetStocks
消息。 - 服务器以初始项目列表作为响应。
- 客户端订阅
GetStockTickerStream
流。此流仅用于更新。
现在,我想用实际数据替换随机生成的股票报价机数据,并在数据库中插入/更新/删除某个项目时,将该更新推送到SignalR GetStockTickerStream
。
概括地说,是否有一种方法可以在Azure SQL数据库上放置触发器,以便在数据库中插入/更新/删除项时触发Azure函数?
一个很好的解释如何做到这一点和一个小片段将是可怕的。
public sealed class StockTickerHub : Hub
{
private readonly IStockTickerService _stockTickerService;
public StockTickerHub(IStockTickerService stockTickerService)
{
_stockTickerService = stockTickerService;
}
public ChannelReader<Stock> GetStockTickerStream(CancellationToken cancellationToken)
{
var channel = Channel.CreateUnbounded<Stock>();
_ = _stockTickerService.SubscribeAsync(channel.Writer, cancellationToken);
return channel.Reader;
}
public async Task GetStocks()
{
var stocks = await _stockTickerService.GetStocksAsync();
await Clients.Caller.SendAsync("ReceiveStocks", stocks);
}
}
public class Stock
{
public required long Id { get; init; }
public required string Symbol { get; init; }
public required double Price { get; init; }
}
public interface IStockTickerService
{
Task<IEnumerable<Stock>> GetStocksAsync();
Task SubscribeAsync(ChannelWriter<Stock> writer, CancellationToken cancellationToken);
}
public sealed class StockTickerService : IStockTickerService
{
private static readonly string[] Stocks =
{
"TESLA", "S&P 500", "DAX", "NASDAQ", "DOW"
};
public Task<IEnumerable<Stock>> GetStocksAsync()
{
return Task.FromResult(Enumerable.Range(1, 10).Select(index => new Stock
{
Id = index,
Symbol = Stocks[Random.Shared.Next(Stocks.Length)],
Price = Math.Round(Random.Shared.NextDouble() * 100, 2)
}));
}
public async Task SubscribeAsync(ChannelWriter<Stock> writer, CancellationToken cancellationToken)
{
try
{
while (true)
{
var stock = new Stock
{
Id = 1,
Symbol = "TESLA",
Price = Math.Round(Random.Shared.NextDouble() * 100, 2)
};
await writer.WriteAsync(stock, cancellationToken);
await Task.Delay(1000, cancellationToken);
}
}
finally
{
writer.Complete();
}
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddDataGrid(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);
services.AddSingleton<IStockTickerService, StockTickerService>();
return services;
}
public static IEndpointRouteBuilder UseDataGrid(this IEndpointRouteBuilder app)
{
ArgumentNullException.ThrowIfNull(app);
app.MapHub<StockTickerHub>("/stockticker");
return app;
}
}
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Table } from "antd";
import { HubConnectionState } from "redux-signalr";
import hubConnection from "../store/middlewares/signalr/signalrSlice";
import { Stock, addStock } from "../store/reducers/stockSlice";
import { RootState } from "../store";
import "./DataGrid.css";
const DataGrid = () => {
const dispatch = useDispatch();
const stocks = useSelector((state: RootState) => state.stock.stocks);
useEffect(() => {
if (hubConnection.state !== HubConnectionState.Connected) {
hubConnection
.start()
.then(() => {
console.log("Started connection via SignalR");
hubConnection.send("GetStocks");
hubConnection.stream("GetStockTickerStream").subscribe({
next: async (item: Stock) => {
console.log(item);
dispatch(addStock(item)); // Dispatch addStock action to update Redux store
},
complete: () => {
console.log("Completed");
},
error: (err) => {
console.error(err);
},
});
})
.catch((err) => console.error(`Faulted: ${err.toString()}`));
}
// return () => {
// hubConnection
// .stop()
// .then(() => {})
// .catch((err) => console.error(err.toString()));
// };
}, [dispatch]);
return (
<Table dataSource={stocks} rowKey={(record) => record.id}>
<Table.Column title="ID" dataIndex="id" key="id" />
<Table.Column title="Symbol" dataIndex="symbol" key="symbol" />
<Table.Column title="Price" dataIndex="price" key="price" />
</Table>
);
};
export default DataGrid;
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export type Stock = Readonly<{
id: number;
symbol: string;
price: number;
}>;
export type StockState = Readonly<{
stocks: Stock[];
}>;
const initialState: StockState = {
stocks: [],
};
const stockSlice = createSlice({
name: "stock",
initialState: initialState,
reducers: {
getStocks: (state, action: PayloadAction<Stock[]>) => {
state.stocks = [...state.stocks, ...action.payload];
},
addStock: (state, action: PayloadAction<Stock>) => {
const stockIndex = state.stocks.findIndex(
(stock) => stock.id === action.payload.id
);
if (stockIndex !== -1) {
// If the stock already exists, update its price
state.stocks[stockIndex].price = action.payload.price;
} else {
// If the stock doesn't exist, add it to the array
state.stocks.push(action.payload);
}
},
},
});
export const { getStocks, addStock } = stockSlice.actions;
export default stockSlice.reducer;
1条答案
按热度按时间lmvvr0a81#
经过测试,我发现Azure SQL数据库不支持SqlDependency OnChange事件。如果你想了解更多关于SqlDependency的信息,可以查看this thread。
但是我找到了Azure SQL触发器,Azure SQL触发器绑定使用轮询循环来检查更改,当检测到更改时触发用户函数。
Azure SQL bindings for Azure Functions overview (preview)