From 96ccf6d86f5748c6e11efcf0767ca3c39e19033b Mon Sep 17 00:00:00 2001 From: Loretta Date: Sun, 9 Jun 2024 11:13:55 +0200 Subject: [PATCH] SignalRDataSource, TiamGrid imrpovements, fixes... --- .../Pages/User/MyServiceProviders.razor | 11 +- .../AddressDetailGridComponent.razor | 6 +- .../User/SysAdmins/ManageTransfers.razor | 23 +- .../TransferToDriverGridComponent.razor | 6 +- .../Shared/Components/Grids/TiamGrid.cs | 163 +++++--- .../Shared/Utility/SignalRDataSource.cs | 388 +++++++++++++++--- .../Shared/Utility/SignalRDataSourceAsync.cs | 65 ++- 7 files changed, 483 insertions(+), 179 deletions(-) diff --git a/TIAMSharedUI/Pages/User/MyServiceProviders.razor b/TIAMSharedUI/Pages/User/MyServiceProviders.razor index 87e89e70..d3ecd46e 100644 --- a/TIAMSharedUI/Pages/User/MyServiceProviders.razor +++ b/TIAMSharedUI/Pages/User/MyServiceProviders.razor @@ -82,7 +82,8 @@ - + + @{ @@ -147,19 +148,13 @@ } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - await _gridCompany.StartEditRowAsync(0); - } - void Grid_CustomizeEditModel(GridCustomizeEditModelEventArgs e) { if (e.IsNew) { var newEmployee = (Company)e.EditModel; newEmployee.Name = "John"; - newEmployee.OwnerId = Guid.NewGuid(); + newEmployee.AffiliateId = Guid.NewGuid(); } } diff --git a/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor index 68cfa3c5..3a092984 100644 --- a/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor +++ b/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor @@ -26,9 +26,9 @@ DataSource="DataSource" Logger="_logger" SignalRClient="AdminSignalRClient" - OnEditModelSaving="DataItemSaving" - OnDataItemDeleting="DataItemDeleting" - OnDataItemChanged="DataItemChanged" + OnGridEditModelSaving="DataItemSaving" + OnGridItemDeleting="DataItemDeleting" + OnGridItemChanged="DataItemChanged" PageSize="5" AutoExpandAllGroupRows="true" KeyboardNavigationEnabled="KeyboardNavigationEnabled" diff --git a/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor b/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor index 8cb19deb..2a5d97ec 100644 --- a/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor +++ b/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor @@ -17,6 +17,8 @@ @using AyCode.Core.Extensions @using AyCode.Core.Consts @using AyCode.Core +@using AyCode.Core.Helpers +@using TIAM.Entities.Emails @layout AdminLayout @inject IEnumerable LogWriters @inject IStringLocalizer localizer @@ -89,10 +91,10 @@ Logger="_logger" SignalRClient="AdminSignalRClient" OnDataSourceChanged="DataSourceChanged" - OnDataItemChanging="DataSourceItemChanging" - OnDataItemChanged="DataSourceItemChanged" - OnDataItemDeleting="DataItemDeleting" - OnEditModelSaving="DataItemSaving" + OnGridItemChanging="DataSourceItemChanging" + OnGridItemChanged="DataSourceItemChanged" + OnGridItemDeleting="DataItemDeleting" + OnGridEditModelSaving="DataItemSaving" CustomizeElement="Grid_CustomizeElement" CustomizeEditModel="Grid_CustomizeEditModel" @@ -163,7 +165,18 @@ - + + + @System.Text.RegularExpressions.Regex.Replace((displayTextContext.Value as string)!, "<(.|\n)*?>", string.Empty) + + + + @{ + var value = ((EmailMessage)editTextContext.EditModel).Text; + + } + + diff --git a/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor index 83b676ac..9744e98a 100644 --- a/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor +++ b/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor @@ -21,9 +21,9 @@ DataSource="ParentData.TransferToDrivers" Logger="_logger" SignalRClient="AdminSignalRClient" - OnEditModelSaving="DataItemSaving" - OnDataItemDeleting="DataItemDeleting" - OnDataItemChanged="DataItemChanged" + OnGridEditModelSaving="DataItemSaving" + OnGridItemDeleting="DataItemDeleting" + OnGridItemChanged="DataItemChanged" PageSize="5" AutoExpandAllGroupRows="true" KeyboardNavigationEnabled="KeyboardNavigationEnabled" diff --git a/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs b/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs index 0fc18741..545fddc3 100644 --- a/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs +++ b/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.Data.Common; using AyCode.Core; using AyCode.Core.Enums; using AyCode.Core.Extensions; @@ -8,8 +9,10 @@ using AyCode.Interfaces.Entities; using AyCode.Services.SignalRs; using AyCode.Utils.Extensions; using DevExpress.Blazor; +using DevExpress.Blazor.Internal; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; +using TIAM.Services; using TIAMWebApp.Shared.Application.Services; using TIAMWebApp.Shared.Application.Utility; @@ -41,7 +44,8 @@ namespace TIAMSharedUI.Shared.Components.Grids public class TiamGrid : DxGrid where TDataItem : class, IId { protected bool IsFirstInitializeParameters; - private IList _dataSource = null!; + private SignalRDataSource _dataSource = null!; + private IList _dataSourceParam = []; private string _gridLogName; public TiamGrid() : base() @@ -60,14 +64,17 @@ namespace TIAMSharedUI.Shared.Components.Grids [Parameter] public int RemoveMessageTag { get; set; } protected new EventCallback DataItemDeleting { get; set; } - [Parameter] public EventCallback OnDataItemDeleting { get; set; } + [Parameter] public EventCallback OnGridItemDeleting { get; set; } protected new EventCallback EditModelSaving { get; set; } - [Parameter] public EventCallback OnEditModelSaving { get; set; } + [Parameter] public EventCallback OnGridEditModelSaving { get; set; } [Parameter] public EventCallback> OnDataSourceChanged { get; set; } - [Parameter] public EventCallback> OnDataItemChanging { get; set; } - [Parameter] public EventCallback> OnDataItemChanged { get; set; } + [Parameter] public EventCallback> OnGridItemChanging { get; set; } + /// + /// After server response! + /// + [Parameter] public EventCallback> OnGridItemChanged { get; set; } [Parameter] [DefaultValue(null)] @@ -86,26 +93,65 @@ namespace TIAMSharedUI.Shared.Components.Grids } set { - if (value == null) throw new ArgumentNullException(nameof(value)); + if (value == null) return; + _dataSourceParam = value; - var equals = Equals(_dataSource, value); + //bool equals; - _dataSource = value; - Data = _dataSource; + //if ((equals = Equals(_dataSource, value)) == false) + //{ + // if (value is SignalRDataSource dataSource) + // _dataSource = dataSource; + // else + // { + // var crudTags = new SignalRCrudTags(GetAllMessageTag, SignalRTags.None, AddMessageTag, UpdateMessageTag, RemoveMessageTag); + // _dataSource = new SignalRDataSource(value, SignalRClient, crudTags, ContextId); + // } + //} - if (!equals) OnDataSourceChanged.InvokeAsync(_dataSource); + //Data = _dataSource; + //if (!equals) OnDataSourceChanged.InvokeAsync(_dataSource); } } - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { if (Logger == null) throw new NullReferenceException($"[{GetType().Name}] Logger == null"); if (SignalRClient == null) + { Logger.Error($"[{GetType().Name}] SignalRClient == null"); + throw new NullReferenceException($"[{GetType().Name}] SignalRClient == null"); + } - base.OnInitialized(); + var crudTags = new SignalRCrudTags(GetAllMessageTag, SignalRTags.None, AddMessageTag, UpdateMessageTag, RemoveMessageTag); + _dataSource = new SignalRDataSource(SignalRClient, crudTags, ContextId); + + Data = _dataSource; + + _dataSource.OnDataSourceLoaded += OnDataSourceLoaded; + _dataSource.OnDataSourceItemChanged += OnDataSourceItemChanged; + + await base.OnInitializedAsync(); + } + + private Task OnDataSourceItemChanged(ItemChangedEventArgs args) + { + if (args.TrackingState is TrackingState.GetAll or TrackingState.None) return Task.CompletedTask; + + Logger.Info($"{_gridLogName} OnItemLoaded; trackingState: {args.TrackingState}"); + + var changedEventArgs = new GridDataItemChangedEventArgs(this, args.Item, args.TrackingState); + return OnGridItemChanged.InvokeAsync(changedEventArgs); + } + + private Task OnDataSourceLoaded() + { + Logger.Info($"{_gridLogName} OnDataSourceLoaded"); + + Reload(); + return OnDataSourceChanged.InvokeAsync(_dataSource); } protected override async Task OnAfterRenderAsync(bool firstRender) @@ -114,9 +160,8 @@ namespace TIAMSharedUI.Shared.Components.Grids if (firstRender) { - if (_dataSource == null || _dataSource.Count == 0) RefreshDataSourceAsync().Forget(); - - //AutoFitColumnWidths(); + if (_dataSourceParam.Count > 0) await _dataSource.LoadDataSource(_dataSourceParam); + else _dataSource.LoadDataSourceAsync(true).Forget(); } } @@ -131,21 +176,29 @@ namespace TIAMSharedUI.Shared.Components.Grids public Task RemoveDataItem(Guid id) => RemoveDataItem(id, RemoveMessageTag); - public Task RemoveDataItem(Guid id, int messageTag) + public async Task RemoveDataItem(Guid id, int messageTag) { var dataItem = _dataSource.FirstOrDefault(x => x.Id == id); + if (dataItem != null) + { + _dataSource.Remove(dataItem); + await _dataSource.SaveChanges(); + //await RemoveDataItem(dataItem); + } - return dataItem == null ? Task.CompletedTask : RemoveDataItem(dataItem); + await InvokeAsync(StateHasChanged); } private async Task OnItemSaving(GridEditModelSavingEventArgs e) { var dataItem = (e.EditModel as TDataItem)!; + if (e.IsNew && dataItem.Id.IsNullOrEmpty()) dataItem.Id = Guid.NewGuid(); + var logText = e.IsNew ? "add" : "update"; Logger.Info($"{_gridLogName} OnItemSaving {logText}; Id: {dataItem.Id}"); - await OnEditModelSaving.InvokeAsync(e); + await OnGridEditModelSaving.InvokeAsync(e); if (e.Cancel) { @@ -153,15 +206,31 @@ namespace TIAMSharedUI.Shared.Components.Grids return; } - if (e.IsNew) await AddDataItem(dataItem); - else await UpdateDataItem(dataItem); + if (!e.IsNew) _dataSource.SetTrackingStateToUpdate(dataItem); + else + { + if (dataItem.Id.IsNullOrEmpty()) dataItem.Id = Guid.NewGuid(); + _dataSource.Add(dataItem); + } + + try + { + var unsavedItems = await _dataSource.SaveChanges(); + + if (unsavedItems.Count > 0) + Logger.Error($"OnItemSaving->TrySaveChanges error! unsavedCount: {unsavedItems.Count}"); + } + catch (Exception ex) + { + Logger.Error($"{_gridLogName} OnItemSaving", ex); + } } private async Task OnItemDeleting(GridDataItemDeletingEventArgs e) { Logger.Info($"{_gridLogName} OnItemDeleting"); - await OnDataItemDeleting.InvokeAsync(e); + await OnGridItemDeleting.InvokeAsync(e); if (e.Cancel) { @@ -170,32 +239,19 @@ namespace TIAMSharedUI.Shared.Components.Grids } var dataItem = (e.DataItem as TDataItem)!; - await RemoveDataItem(dataItem); - } + _dataSource.Remove(dataItem); - public virtual Task RefreshDataSourceAsync() - { - if (GetAllMessageTag == 0) return Task.CompletedTask; - - Logger.Info($"{_gridLogName} RefreshDataSourceAsync called"); - - return SignalRClient.GetAllAsync>(GetAllMessageTag, ContextId, response => - { - if (response.Status == SignalResponseStatus.Error) - return; - - BeginUpdate(); - DataSource = response.ResponseData ?? []; - EndUpdate(); - - InvokeAsync(StateHasChanged).Forget(); - }); + var unsavedItems = await _dataSource.SaveChanges(); + if (unsavedItems.Count > 0) + Logger.Error($"OnItemDeleting->TrySaveChanges error! unsavedCount: {unsavedItems.Count}"); } protected virtual async Task PostDataToServerAsync(TDataItem dataItem, int messageTag, TrackingState trackingState) { + return; + var changingEventArgs = new GridDataItemChangingEventArgs(this, dataItem, trackingState); - await OnDataItemChanging.InvokeAsync(changingEventArgs); + await OnGridItemChanging.InvokeAsync(changingEventArgs); if (changingEventArgs.IsCanceled) { @@ -211,21 +267,21 @@ namespace TIAMSharedUI.Shared.Components.Grids _dataSource.UpdateCollection(dataItem, trackingState == TrackingState.Remove); //egyből látszódik a változás a grid-ben, nem csak a callback lefutásakor! felhasználóbarátabb... - J. - SignalRClient.PostDataAsync(messageTag, dataItem, async response => - { - if (response.Status != SignalResponseStatus.Success || response.ResponseData == null) - { - RefreshDataSourceAsync().Forget(); - return; - } + //SignalRClient.PostDataAsync(messageTag, dataItem, async response => + //{ + // if (response.Status != SignalResponseStatus.Success || response.ResponseData == null) + // { + // RefreshDataSourceAsync().Forget(); + // return; + // } - _dataSource.UpdateCollection(response.ResponseData, trackingState == TrackingState.Remove); + // _dataSource.UpdateCollection(response.ResponseData, trackingState == TrackingState.Remove); - var changedEventArgs = new GridDataItemChangedEventArgs(this, response.ResponseData, trackingState); - await OnDataItemChanged.InvokeAsync(changedEventArgs); + // var changedEventArgs = new GridDataItemChangedEventArgs(this, response.ResponseData, trackingState); + // await OnDataItemChanged.InvokeAsync(changedEventArgs); - InvokeAsync(StateHasChanged).Forget(); - }).Forget(); + // InvokeAsync(StateHasChanged).Forget(); + //}).Forget(); //transfer = await devAdminSignalClient.PostDataAsync(SignalRTags.UpdateTransferAsync, transfer); } @@ -250,6 +306,7 @@ namespace TIAMSharedUI.Shared.Components.Grids { base.DataItemDeleting = EventCallback.Factory.Create(this, OnItemDeleting); base.EditModelSaving = EventCallback.Factory.Create(this, OnItemSaving); + CustomizeElement += OnCustomizeElement; //ShowFilterRow = true; diff --git a/TIAMWebApp/Shared/Utility/SignalRDataSource.cs b/TIAMWebApp/Shared/Utility/SignalRDataSource.cs index 19b2c826..1a6ce0d3 100644 --- a/TIAMWebApp/Shared/Utility/SignalRDataSource.cs +++ b/TIAMWebApp/Shared/Utility/SignalRDataSource.cs @@ -7,6 +7,7 @@ using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using AyCode.Core.Enums; using AyCode.Core.Extensions; +using AyCode.Core.Helpers; using AyCode.Core.Interfaces; using AyCode.Services.SignalRs; using TIAM.Services; @@ -18,7 +19,7 @@ namespace TIAMWebApp.Shared.Application.Utility { public TrackingState TrackingState { get; internal set; } = trackingState; public T CurrentValue { get; internal set; } = currentValue; - public T? OriginalValue { get; init; } = originalValue; //originalValue == null ? null : TrackingItemHelpers.Clone(originalValue); + public T? OriginalValue { get; init; } = originalValue; internal TrackingItem UpdateItem(TrackingState trackingState, T newValue) { @@ -32,7 +33,7 @@ namespace TIAMWebApp.Shared.Application.Utility } - public class ChangeTracking where T : class, IId + public class ChangeTracking /*: IEnumerable>*/ where T : class, IId { private readonly List> _trackingItems = []; //TODO: Dictionary... - J. @@ -58,7 +59,7 @@ namespace TIAMWebApp.Shared.Application.Utility } if (originalValue != null && Equals(newValue, originalValue)) - originalValue = TrackingItemHelpers.ReflectionClone(originalValue); + originalValue = TrackingItemHelpers.JsonClone(originalValue); trackingItem = new TrackingItem(trackingState, newValue, originalValue); _trackingItems.Add(trackingItem); @@ -77,6 +78,16 @@ namespace TIAMWebApp.Shared.Application.Utility } internal void Remove(TrackingItem trackingItem) => _trackingItems.Remove(trackingItem); + + //public IEnumerator> GetEnumerator() + //{ + // return _trackingItems.GetEnumerator(); + //} + + //IEnumerator IEnumerable.GetEnumerator() + //{ + // return GetEnumerator(); + //} } @@ -91,17 +102,18 @@ namespace TIAMWebApp.Shared.Application.Utility protected readonly ChangeTracking TrackingItems = new(); protected readonly Guid? ContextId; - protected readonly AcSignalRClientBase SignalRClient; + public AcSignalRClientBase SignalRClient; protected readonly SignalRCrudTags SignalRCrudTags; - public SignalRDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, Guid? contextId = null, bool autoLoadDataSource = true) + public Func, Task>? OnDataSourceItemChanged; + public Func? OnDataSourceLoaded; + + public SignalRDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, Guid? contextId = null) { ContextId = contextId; SignalRCrudTags = signalRCrudTags; SignalRClient = signalRClient; - - if (autoLoadDataSource) LoadDataSource(false); } public bool IsSynchronized => true; @@ -113,38 +125,79 @@ namespace TIAMWebApp.Shared.Application.Utility /// /// /// - public void LoadDataSource(bool clearChangeTracking = true) + public async Task LoadDataSource(bool clearChangeTracking = true) { if (SignalRCrudTags.GetAllMessageTag == SignalRTags.None) throw new ArgumentException($"SignalRCrudTags.GetAllMessageTag == SignalRTags.None"); - lock (_syncRoot) - { - var resultList = SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId).GetAwaiter().GetResult() ?? throw new NullReferenceException(); + var resultList = (await SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId)) ?? throw new NullReferenceException(); - Clear(clearChangeTracking); - InnerList.AddRange(resultList); - } + LoadDataSource(resultList); } - public T? LoadItem(Guid id) + public Task LoadDataSourceAsync(bool clearChangeTracking = true) + { + if (SignalRCrudTags.GetAllMessageTag == SignalRTags.None) throw new ArgumentException($"SignalRCrudTags.GetAllMessageTag == SignalRTags.None"); + + return SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId, result=> + { + if (result.Status != SignalResponseStatus.Success || result.ResponseData == null) + throw new NullReferenceException($"result.Status != SignalResponseStatus.Success || result.ResponseData == null; Status: {SignalResponseStatus.Success}"); + + LoadDataSource(result.ResponseData); + }); + } + + public async Task LoadDataSource(IList fromSource, bool clearChangeTracking = true) + { + Monitor.Enter(_syncRoot); + + try + { + Clear(clearChangeTracking); + + foreach (var item in fromSource) + { + InnerList.Add(item); + + var eventArgs = new ItemChangedEventArgs(item, TrackingState.GetAll); + if (OnDataSourceItemChanged != null) await OnDataSourceItemChanged.Invoke(eventArgs); + } + } + finally + { + Monitor.Exit(_syncRoot); + } + + if (OnDataSourceLoaded != null) await OnDataSourceLoaded.Invoke(); + } + + public async Task LoadItem(Guid id) { if (SignalRCrudTags.GetItemMessageTag == SignalRTags.None) throw new ArgumentException($"SignalRCrudTags.GetItemMessageTag == SignalRTags.None"); T? resultitem = null; - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { - resultitem = SignalRClient.GetByIdAsync(SignalRCrudTags.GetItemMessageTag, id).GetAwaiter().GetResult(); + resultitem = await SignalRClient.GetByIdAsync(SignalRCrudTags.GetItemMessageTag, id); if (resultitem == null) return null; if (TryGetIndex(id, out var index)) InnerList[index] = resultitem; else InnerList.Add(resultitem); + + var eventArgs = new ItemChangedEventArgs(resultitem, TrackingState.Get); + if (OnDataSourceItemChanged != null) await OnDataSourceItemChanged.Invoke(eventArgs); + } + finally + { + Monitor.Exit(_syncRoot); } return resultitem; } - /// /// set: UpdateMessageTag /// @@ -157,17 +210,28 @@ namespace TIAMWebApp.Shared.Application.Utility { if ((uint)index >= (uint)Count) throw new ArgumentOutOfRangeException(nameof(index)); - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try { return InnerList[index]; } + finally + { + Monitor.Exit(_syncRoot); + } } set { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try { Update(index, value); } + finally + { + Monitor.Exit(_syncRoot); + } + } } @@ -180,13 +244,20 @@ namespace TIAMWebApp.Shared.Application.Utility { if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"Add->newValue.Id.IsNullOrEmpty()"); - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { if (Contains(newValue)) throw new ArgumentException($@"It already contains this Id! Id: {newValue.Id}", nameof(newValue)); UnsafeAdd(newValue); } + finally + { + Monitor.Exit(_syncRoot); + } + } /// @@ -198,12 +269,19 @@ namespace TIAMWebApp.Shared.Application.Utility { if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"AddOrUpdate->newValue.Id.IsNullOrEmpty()"); - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { var index = IndexOf(newValue); return index > -1 ? Update(index, newValue) : UnsafeAdd(newValue); } + finally + { + Monitor.Exit(_syncRoot); + } + } //public void AddRange(IEnumerable collection) @@ -233,7 +311,9 @@ namespace TIAMWebApp.Shared.Application.Utility { if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"Insert->newValue.Id.IsNullOrEmpty()"); - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { if (Contains(newValue)) throw new ArgumentException($@"It already contains this Id! Id: {newValue.Id}", nameof(newValue)); @@ -241,6 +321,11 @@ namespace TIAMWebApp.Shared.Application.Utility TrackingItems.AddTrackingItem(TrackingState.Add, newValue); InnerList.Insert(index, newValue); } + finally + { + Monitor.Exit(_syncRoot); + } + } /// @@ -264,7 +349,9 @@ namespace TIAMWebApp.Shared.Application.Utility if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"Update->newValue.Id.IsNullOrEmpty()"); if ((uint)index >= (uint)Count) throw new ArgumentOutOfRangeException(nameof(index)); - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { var currentItem = InnerList[index]; @@ -276,6 +363,11 @@ namespace TIAMWebApp.Shared.Application.Utility return newValue; } + finally + { + Monitor.Exit(_syncRoot); + } + } /// @@ -285,7 +377,9 @@ namespace TIAMWebApp.Shared.Application.Utility /// public bool Remove(T item) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { var index = IndexOf(item); @@ -294,6 +388,11 @@ namespace TIAMWebApp.Shared.Application.Utility RemoveAt(index); return true; } + finally + { + Monitor.Exit(_syncRoot); + } + } /// @@ -304,10 +403,17 @@ namespace TIAMWebApp.Shared.Application.Utility /// public bool TryRemove(Guid id, out T? item) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { return TryGetValue(id, out item) && Remove(item); } + finally + { + Monitor.Exit(_syncRoot); + } + } /// @@ -319,7 +425,9 @@ namespace TIAMWebApp.Shared.Application.Utility /// public void RemoveAt(int index) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { var currentItem = InnerList[index]; if (currentItem.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(currentItem), $@"RemoveAt->item.Id.IsNullOrEmpty(); index: {index}"); @@ -327,6 +435,11 @@ namespace TIAMWebApp.Shared.Application.Utility TrackingItems.AddTrackingItem(TrackingState.Remove, currentItem, currentItem); InnerList.RemoveAt(index); } + finally + { + Monitor.Exit(_syncRoot); + } + } /// @@ -335,21 +448,39 @@ namespace TIAMWebApp.Shared.Application.Utility /// public List> GetTrackingItems() { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try + { return TrackingItems.ToList(); + } + finally + { + Monitor.Exit(_syncRoot); + } } public void SetTrackingStateToUpdate(T item) { - if (TrackingItems.TryGetTrackingItem(item.Id, out var trackingItem)) + Monitor.Enter(_syncRoot); + + try { - if (trackingItem.TrackingState != TrackingState.Add) - trackingItem.TrackingState = TrackingState.Update; + if (TrackingItems.TryGetTrackingItem(item.Id, out var trackingItem)) + { + if (trackingItem.TrackingState != TrackingState.Add) + trackingItem.TrackingState = TrackingState.Update; - return; + return; + } + + if (!TryGetValue(item.Id, out var originalItem)) return; + TrackingItems.AddTrackingItem(TrackingState.Update, item, originalItem); + } + finally + { + Monitor.Exit(_syncRoot); } - - TrackingItems.AddTrackingItem(TrackingState.Update, item, item); } /// @@ -360,33 +491,49 @@ namespace TIAMWebApp.Shared.Application.Utility /// public bool TryGetTrackingItem(Guid id, [NotNullWhen(true)] out TrackingItem? trackingItem) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try + { return TrackingItems.TryGetTrackingItem(id, out trackingItem); + } + finally + { + Monitor.Exit(_syncRoot); + } + } /// /// /// /// Unsaved items - public bool SaveChanges(out List> unsavedItems) + public async Task>> SaveChanges() { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + + try { foreach (var trackingItem in TrackingItems.ToList()) { try { - SaveTrackingItemUnsafe(trackingItem); + //throw new Exception(); + await SaveTrackingItemUnsafe(trackingItem); } - catch + catch(Exception ex) { - // ignored + TryRollbackItem(trackingItem.CurrentValue.Id, out _); } } - unsavedItems = TrackingItems.ToList(); - return unsavedItems.Count == 0; + return TrackingItems.ToList(); } + finally + { + Monitor.Exit(_syncRoot); + } + } /// @@ -395,34 +542,55 @@ namespace TIAMWebApp.Shared.Application.Utility /// /// /// - public bool TrySaveItem(Guid id, [NotNullWhen(true)] out T? resultItem) + public async Task SaveItem(Guid id) { - resultItem = null; + Monitor.Enter(_syncRoot); - if (TryGetTrackingItem(id, out var trackingItem)) - resultItem = SaveTrackingItemUnsafe(trackingItem); + try + { + T? resultItem = null; - return resultItem != null; + if (TryGetTrackingItem(id, out var trackingItem)) + resultItem = await SaveTrackingItemUnsafe(trackingItem); + + return resultItem; + } + finally + { + Monitor.Exit(_syncRoot); + } } - public bool TrySaveItem(Guid id, TrackingState trackingState, [NotNullWhen(true)] out T? resultItem) - => TryGetValue(id, out resultItem) && TrySaveItem(resultItem, trackingState, out resultItem); - - public bool TrySaveItem(T item, TrackingState trackingState, [NotNullWhen(true)] out T? resultItem) + public async Task SaveItem(Guid id, TrackingState trackingState) { - resultItem = SaveItemUnsafe(item, trackingState); - return resultItem != null; + Monitor.Enter(_syncRoot); + + try + { + T? resultItem = null; + + if (TryGetValue(id, out var item)) + resultItem = await SaveItem(item, trackingState); + + return resultItem; + } + finally + { + Monitor.Exit(_syncRoot); + } } - protected T? SaveTrackingItemUnsafe(TrackingItem trackingItem) + public Task SaveItem(T item, TrackingState trackingState) => SaveItemUnsafe(item, trackingState); + + protected Task SaveTrackingItemUnsafe(TrackingItem trackingItem) => SaveItemUnsafe(trackingItem.CurrentValue, trackingItem.TrackingState); - protected T? SaveItemUnsafe(T item, TrackingState trackingState) + protected async Task SaveItemUnsafe(T item, TrackingState trackingState) { var messageTag = SignalRCrudTags.GetMessageTagByTrackingState(trackingState); if (messageTag == SignalRTags.None) return null; //throw new ArgumentException($"messageTag == SignalRTags.None"); - var result = SignalRClient.PostDataAsync(messageTag, item).GetAwaiter().GetResult(); + var result = await SignalRClient.PostDataAsync(messageTag, item); if (result == null) return null; //throw new NullReferenceException($"result == null"); if (TryGetTrackingItem(item.Id, out var trackingItem)) @@ -431,6 +599,9 @@ namespace TIAMWebApp.Shared.Application.Utility if (TryGetIndex(result.Id, out var index)) InnerList[index] = result; + var eventArgs = new ItemChangedEventArgs(result, trackingState); + if (OnDataSourceItemChanged != null) await OnDataSourceItemChanged.Invoke(eventArgs); + return result; } @@ -449,7 +620,8 @@ namespace TIAMWebApp.Shared.Application.Utility public bool TryRollbackItem(Guid id, out T? originalValue) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try { if (TryGetTrackingItem(id, out var trackingItem)) { @@ -462,22 +634,40 @@ namespace TIAMWebApp.Shared.Application.Utility originalValue = null; return false; } + finally + { + Monitor.Exit(_syncRoot); + } } public void Rollback() { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try { foreach (var trackingItem in TrackingItems.ToList()) RollbackItemUnsafe(trackingItem); } + finally + { + Monitor.Exit(_syncRoot); + } } public int Count { get { - lock (_syncRoot) return InnerList.Count; + Monitor.Enter(_syncRoot); + try + { + return InnerList.Count; + } + finally + { + Monitor.Exit(_syncRoot); + } + } } @@ -485,17 +675,30 @@ namespace TIAMWebApp.Shared.Application.Utility public void Clear(bool clearChangeTracking) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try { if (clearChangeTracking) TrackingItems.Clear(); InnerList.Clear(); } + finally + { + Monitor.Exit(_syncRoot); + } } public int IndexOf(Guid id) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try + { return InnerList.FindIndex(x => x.Id == id); + } + finally + { + Monitor.Exit(_syncRoot); + } + } public int IndexOf(T item) => IndexOf(item.Id); @@ -505,22 +708,37 @@ namespace TIAMWebApp.Shared.Application.Utility public bool TryGetValue(Guid id, [NotNullWhen(true)] out T? item) { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try { item = InnerList.FirstOrDefault(x => x.Id == id); return item != null; } + finally + { + Monitor.Exit(_syncRoot); + } } public void CopyTo(T[] array) => CopyTo(array, 0); public void CopyTo(T[] array, int arrayIndex) { - lock (_syncRoot) InnerList.CopyTo(array, arrayIndex); + Monitor.Enter(_syncRoot); + try + { + InnerList.CopyTo(array, arrayIndex); + } + finally + { + Monitor.Exit(_syncRoot); + } + } public int BinarySearch(int index, int count, T item, IComparer? comparer) { + throw new NotImplementedException($"BinarySearch"); if (index < 0) throw new ArgumentOutOfRangeException(nameof(index)); if (count < 0) @@ -528,8 +746,15 @@ namespace TIAMWebApp.Shared.Application.Utility if (Count - index < count) throw new ArgumentException("Invalid length"); - lock (_syncRoot) - return InnerList.BinarySearch(index, count, item, comparer); + //Monitor.Enter(_syncRoot); + //try + //{ + // return InnerList.BinarySearch(index, count, item, comparer); + //} + //finally + //{ + // Monitor.Exit(_syncRoot); + //} } public int BinarySearch(T item) => BinarySearch(0, Count, item, null); @@ -537,8 +762,17 @@ namespace TIAMWebApp.Shared.Application.Utility public IEnumerator GetEnumerator() { - lock (_syncRoot) - return InnerList.ToList().GetEnumerator(); + Monitor.Enter(_syncRoot); + try + { + //return InnerList.ToList().GetEnumerator(); + return InnerList.GetEnumerator(); + } + finally + { + Monitor.Exit(_syncRoot); + } + } public ReadOnlyCollection AsReadOnly() => new(this); @@ -618,11 +852,17 @@ namespace TIAMWebApp.Shared.Application.Utility try { - lock (_syncRoot) + Monitor.Enter(_syncRoot); + try { //TODO: _list.ToArray() - ez nem az igazi... - J. Array.Copy(InnerList.ToArray(), 0, array!, arrayIndex, InnerList.Count); } + finally + { + Monitor.Exit(_syncRoot); + } + } catch (ArrayTypeMismatchException) { @@ -638,4 +878,16 @@ namespace TIAMWebApp.Shared.Application.Utility #endregion IList, ICollection } + + public class ItemChangedEventArgs where T : IId + { + internal ItemChangedEventArgs(T item, TrackingState trackingState) + { + Item = item; + TrackingState = trackingState; + } + + public T Item { get; } + public TrackingState TrackingState { get; } + } } diff --git a/TIAMWebApp/Shared/Utility/SignalRDataSourceAsync.cs b/TIAMWebApp/Shared/Utility/SignalRDataSourceAsync.cs index e6361fa2..d435cb51 100644 --- a/TIAMWebApp/Shared/Utility/SignalRDataSourceAsync.cs +++ b/TIAMWebApp/Shared/Utility/SignalRDataSourceAsync.cs @@ -16,7 +16,7 @@ public class SignalRDataSourceAsync : SignalRDataSource where T : class, I public Action>? OnDataSourceLoaded; public SignalRDataSourceAsync(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, Guid? contextId = null, Action>? onDataSourceLoaded = null, bool autoLoadDataSource = false) - : base(signalRClient, signalRCrudTags, contextId, false) + : base(signalRClient, signalRCrudTags, contextId) { OnDataSourceLoaded = onDataSourceLoaded; @@ -27,34 +27,34 @@ public class SignalRDataSourceAsync : SignalRDataSource where T : class, I { if (SignalRCrudTags.GetAllMessageTag == SignalRTags.None) throw new ArgumentException($"_signalRCrudTags.GetAllMessageTag == SignalRTags.None;"); - Monitor.Exit(SyncRoot); //Exception test - J. + //Monitor.Exit(SyncRoot); //Exception test - J. - Monitor.Enter(SyncRoot); - try - { - SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId, response => - { - try - { - if (response.Status == SignalResponseStatus.Error) throw new Exception($"LoadDataSourceAsync; response.Status == SignalResponseStatus.Error"); - if (response.ResponseData == null) throw new NullReferenceException($"response.ResponseData == null"); + //Monitor.Enter(SyncRoot); + //try + //{ + // SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId, response => + // { + // try + // { + // if (response.Status == SignalResponseStatus.Error) throw new Exception($"LoadDataSourceAsync; response.Status == SignalResponseStatus.Error"); + // if (response.ResponseData == null) throw new NullReferenceException($"response.ResponseData == null"); - Clear(clearChangeTracking); - InnerList.AddRange(response.ResponseData); - } - finally - { - Monitor.Exit(SyncRoot); - } + // Clear(clearChangeTracking); + // InnerList.AddRange(response.ResponseData); + // } + // finally + // { + // Monitor.Exit(SyncRoot); + // } - OnDataSourceLoaded?.Invoke(this); - }).Forget(); - } - catch (Exception) - { - Monitor.Exit(SyncRoot); - throw; - } + // OnDataSourceLoaded?.Invoke(this); + // }).Forget(); + //} + //catch (Exception) + //{ + // Monitor.Exit(SyncRoot); + // throw; + //} } @@ -101,17 +101,4 @@ public class SignalRDataSourceAsync : SignalRDataSource where T : class, I // return Task.CompletedTask; //} - - public class ItemChangedEventArgs where T : IId - { - internal ItemChangedEventArgs(T item, TrackingState trackingState) - { - Item = item; - TrackingState = trackingState; - } - - public T Item { get; } - public TrackingState TrackingState { get; } - } - } \ No newline at end of file