Integrate SignalR to provide real-time dashboard update notifications. - Added DashboardsHub and DashboardChangeNotifier on the backend. - Modified SqlDashboardStorage to trigger notifications on changes. - Registered SignalR services and mapped the hub endpoint. - Updated Blazor clients to connect to the hub and refresh dashboards on change. - Added SignalR client packages and necessary DI/configuration.
139 lines
5.7 KiB
C#
139 lines
5.7 KiB
C#
using System.Data;
|
|
using System.Text;
|
|
using System.Xml.Linq;
|
|
using DevExpress.DashboardWeb;
|
|
using Microsoft.Data.SqlClient;
|
|
|
|
namespace DbFirst.API.Dashboards;
|
|
|
|
public sealed class SqlDashboardStorage : IEditableDashboardStorage
|
|
{
|
|
private readonly string _connectionString;
|
|
private readonly string _tableName;
|
|
private readonly Func<string?>? _userProvider;
|
|
private readonly IDashboardChangeNotifier? _notifier;
|
|
|
|
public SqlDashboardStorage(string connectionString, string tableName = "TBDD_SMF_CONFIG", Func<string?>? userProvider = null, IDashboardChangeNotifier? notifier = null)
|
|
{
|
|
_connectionString = connectionString;
|
|
_tableName = tableName;
|
|
_userProvider = userProvider;
|
|
_notifier = notifier;
|
|
}
|
|
|
|
public IEnumerable<DashboardInfo> GetAvailableDashboardsInfo()
|
|
{
|
|
var dashboards = new List<DashboardInfo>();
|
|
|
|
using var connection = new SqlConnection(_connectionString);
|
|
using var command = new SqlCommand($"SELECT DashboardId, DashboardName FROM dbo.[{_tableName}] WHERE ACTIVE = 1 ORDER BY DashboardName", connection);
|
|
|
|
connection.Open();
|
|
using var reader = command.ExecuteReader();
|
|
while (reader.Read())
|
|
{
|
|
var id = reader.GetString(0);
|
|
var name = reader.GetString(1);
|
|
dashboards.Add(new DashboardInfo { ID = id, Name = name });
|
|
}
|
|
|
|
return dashboards;
|
|
}
|
|
|
|
public XDocument LoadDashboard(string dashboardId)
|
|
{
|
|
using var connection = new SqlConnection(_connectionString);
|
|
using var command = new SqlCommand($"SELECT DashboardData FROM dbo.[{_tableName}] WHERE DashboardId = @Id AND ACTIVE = 1", connection);
|
|
command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = dashboardId });
|
|
|
|
connection.Open();
|
|
var data = command.ExecuteScalar() as byte[];
|
|
if (data == null)
|
|
{
|
|
throw new ArgumentException($"Dashboard '{dashboardId}' not found.");
|
|
}
|
|
|
|
var xml = Encoding.UTF8.GetString(data);
|
|
var doc = XDocument.Parse(xml);
|
|
NormalizeCatalogDateDimensions(doc);
|
|
return doc;
|
|
}
|
|
|
|
private static void NormalizeCatalogDateDimensions(XDocument doc)
|
|
{
|
|
var dateMembers = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"AddedWhen",
|
|
"ChangedWhen"
|
|
};
|
|
|
|
foreach (var dimension in doc.Descendants("Dimension"))
|
|
{
|
|
var member = dimension.Attribute("DataMember")?.Value;
|
|
if (member == null || !dateMembers.Contains(member))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var interval = dimension.Attribute("DateTimeGroupInterval")?.Value;
|
|
if (string.IsNullOrWhiteSpace(interval) || string.Equals(interval, "Year", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
dimension.SetAttributeValue("DateTimeGroupInterval", "DayMonthYear");
|
|
}
|
|
}
|
|
}
|
|
|
|
public string AddDashboard(XDocument dashboard, string dashboardName)
|
|
{
|
|
var id = string.IsNullOrWhiteSpace(dashboardName)
|
|
? Guid.NewGuid().ToString("N")
|
|
: dashboardName;
|
|
var payload = Encoding.UTF8.GetBytes(dashboard.ToString(SaveOptions.DisableFormatting));
|
|
var userName = _userProvider?.Invoke();
|
|
|
|
using var connection = new SqlConnection(_connectionString);
|
|
using var command = new SqlCommand($"INSERT INTO dbo.[{_tableName}] (ACTIVE, DashboardId, DashboardName, DashboardData, ADDED_WHO, ADDED_WHEN) VALUES (1, @Id, @Name, @Data, COALESCE(@User, SUSER_SNAME()), SYSUTCDATETIME())", connection);
|
|
command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = id });
|
|
command.Parameters.Add(new SqlParameter("@Name", SqlDbType.NVarChar, 256) { Value = string.IsNullOrWhiteSpace(dashboardName) ? id : dashboardName });
|
|
command.Parameters.Add(new SqlParameter("@Data", SqlDbType.VarBinary, -1) { Value = payload });
|
|
command.Parameters.Add(new SqlParameter("@User", SqlDbType.NVarChar, 50) { Value = (object?)userName ?? DBNull.Value });
|
|
|
|
connection.Open();
|
|
command.ExecuteNonQuery();
|
|
_notifier?.NotifyChanged();
|
|
return id;
|
|
}
|
|
|
|
public void SaveDashboard(string dashboardId, XDocument dashboard)
|
|
{
|
|
var payload = Encoding.UTF8.GetBytes(dashboard.ToString(SaveOptions.DisableFormatting));
|
|
var userName = _userProvider?.Invoke();
|
|
|
|
using var connection = new SqlConnection(_connectionString);
|
|
using var command = new SqlCommand($"UPDATE dbo.[{_tableName}] SET DashboardData = @Data, CHANGED_WHO = COALESCE(@User, SUSER_SNAME()), CHANGED_WHEN = SYSUTCDATETIME() WHERE DashboardId = @Id", connection);
|
|
command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = dashboardId });
|
|
command.Parameters.Add(new SqlParameter("@Data", SqlDbType.VarBinary, -1) { Value = payload });
|
|
command.Parameters.Add(new SqlParameter("@User", SqlDbType.NVarChar, 50) { Value = (object?)userName ?? DBNull.Value });
|
|
|
|
connection.Open();
|
|
var rows = command.ExecuteNonQuery();
|
|
if (rows == 0)
|
|
{
|
|
throw new ArgumentException($"Dashboard '{dashboardId}' not found.");
|
|
}
|
|
|
|
_notifier?.NotifyChanged();
|
|
}
|
|
|
|
public void DeleteDashboard(string dashboardId)
|
|
{
|
|
using var connection = new SqlConnection(_connectionString);
|
|
using var command = new SqlCommand($"DELETE FROM dbo.[{_tableName}] WHERE DashboardId = @Id", connection);
|
|
command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = dashboardId });
|
|
|
|
connection.Open();
|
|
command.ExecuteNonQuery();
|
|
_notifier?.NotifyChanged();
|
|
}
|
|
}
|