TourIAm/TIAMWebApp/Shared/Utility/SignalRDataSource.cs

381 lines
12 KiB
C#

using System.Collections;
using System.Collections.ObjectModel;
using System.Diagnostics;
using AyCode.Core.Enums;
using AyCode.Core.Extensions;
using AyCode.Core.Interfaces;
using AyCode.Services.SignalRs;
using TIAM.Services;
using TIAMWebApp.Shared.Application.Services;
namespace TIAMWebApp.Shared.Application.Utility
{
public class ChangeTracking<T>(DataChangeMode dataChangeMode, T newItem, T originalItem = default(T))where T: class, IId<Guid>
{
public DataChangeMode DataChangeMode { get; init; } = dataChangeMode;
public T NewItem { get; init; } = newItem;
public T OriginalItem { get; init; } = originalItem;
}
[Serializable]
[DebuggerDisplay("Count = {Count}")]
public class SignalRDataSource<T> : IList<T>, IList, IReadOnlyList<T> where T: class, IId<Guid>
{
protected readonly List<T> InnerList = [];
private readonly object _syncRoot = new();
protected Guid? ContextId;
protected AcSignalRClientBase SignalRClient;
protected readonly SignalRCrudTags SignalRCrudTags;
public SignalRDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, Guid? contextId = null, bool autoLoadDataSource = true)
{
ContextId = contextId;
SignalRCrudTags = signalRCrudTags;
SignalRClient = signalRClient;
if (autoLoadDataSource) LoadDataSource();
}
public bool IsSynchronized => true;
public object SyncRoot => _syncRoot;
public bool IsFixedSize => false;
/// <summary>
/// GetAllMessageTag
/// </summary>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="NullReferenceException"></exception>
public void LoadDataSource()
{
if (SignalRCrudTags.GetAllMessageTag == SignalRTags.None) throw new ArgumentException($"_signalRCrudTags.GetAllMessageTag == SignalRTags.None;");
lock (_syncRoot)
{
var resultList = SignalRClient.GetAllAsync<List<T>>(SignalRCrudTags.GetAllMessageTag, ContextId).GetAwaiter().GetResult() ?? throw new NullReferenceException();
Clear();
InnerList.AddRange(resultList);
}
}
/// <summary>
/// set: UpdateMessageTag
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public T this[int index]
{
get
{
if ((uint)index >= (uint)Count) throw new ArgumentOutOfRangeException(nameof(index));
lock (_syncRoot)
{
return InnerList[index];
}
}
set
{
lock (_syncRoot)
{
Update(index, value);
}
}
}
public int Count
{
get
{
lock (_syncRoot) return InnerList.Count;
}
}
/// <summary>
/// AddMessageTag
/// </summary>
/// <param name="item"></param>
/// <exception cref="ArgumentException"></exception>
public void Add(T item)
{
lock (_syncRoot)
{
if (Contains(item))
throw new ArgumentException($@"It already contains this Id! Id: {item.Id}", nameof(item));
UnsafeAdd(item);
}
}
/// <summary>
/// AddMessageTag or UpdateMessageTag
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public T AddOrUpdate(T item)
{
lock (_syncRoot)
{
var index = IndexOf(item);
return index > -1 ? Update(index, item) : UnsafeAdd(item);
}
}
//public void AddRange(IEnumerable<T> collection)
//{
// lock (_syncRoot)
// {
// }
//}
private T UnsafeAdd(T item)
{
if (SignalRCrudTags.AddMessageTag == SignalRTags.None) throw new ArgumentException($"_signalRCrudTags.AddMessageTag == SignalRTags.None;");
var result = SignalRClient.PostDataAsync(SignalRCrudTags.AddMessageTag, item).GetAwaiter().GetResult() ?? throw new NullReferenceException();
InnerList.Add(result);
return result;
}
/// <summary>
/// AddMessageTag
/// </summary>
/// <param name="index"></param>
/// <param name="item"></param>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="NullReferenceException"></exception>
public void Insert(int index, T item)
{
if (SignalRCrudTags.AddMessageTag == SignalRTags.None) throw new ArgumentException($"_signalRCrudTags.AddMessageTag == SignalRTags.None;");
lock (_syncRoot)
{
if (Contains(item))
throw new ArgumentException($@"It already contains this Id! Id: {item.Id}", nameof(item));
var result = SignalRClient.PostDataAsync(SignalRCrudTags.AddMessageTag, item).GetAwaiter().GetResult() ?? throw new NullReferenceException();
InnerList.Insert(index, result);
}
}
/// <summary>
/// UpdateMessageTag
/// </summary>
/// <param name="item"></param>
public T Update(T item) => Update(IndexOf(item), item);
/// <summary>
/// UpdateMessageTag
/// </summary>
/// <param name="index"></param>
/// <param name="item"></param>
/// /// <exception cref="ArgumentException"></exception>
/// /// <exception cref="NullReferenceException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public T Update(int index, T item)
{
if (SignalRCrudTags.UpdateMessageTag == SignalRTags.None) throw new ArgumentException($"_signalRCrudTags.UpdateMessageTag == SignalRTags.None;");
if (default(T) != null && item == null) throw new NullReferenceException(nameof(item));
if (item.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(item), "Update->item.Id.IsNullOrEmpty()");
if ((uint)index >= (uint)Count) throw new ArgumentOutOfRangeException(nameof(index));
lock (_syncRoot)
{
if (InnerList[index].Id != item.Id)
throw new ArgumentException($@"_list[index].Id != item.Id! Id: {item.Id}", nameof(item));
var result = SignalRClient.PostDataAsync(SignalRCrudTags.UpdateMessageTag, item).GetAwaiter().GetResult() ?? throw new NullReferenceException();
InnerList[index] = result;
return result;
}
}
/// <summary>
/// RemoveMessageTag
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Remove(T item)
{
lock (_syncRoot)
{
var index = IndexOf(item);
if (index < 0) return false;
RemoveAt(index);
return true;
}
}
/// <summary>
/// RemoveMessageTag
/// </summary>
/// <param name="index"></param>
/// <exception cref="ArgumentException"></exception>
/// /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="NullReferenceException"></exception>
public void RemoveAt(int index)
{
if (SignalRCrudTags.RemoveMessageTag == SignalRTags.None) throw new ArgumentException($"_signalRCrudTags.RemoveMessageTag == SignalRTags.None;");
lock (_syncRoot)
{
var item = InnerList[index];
if (item.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(item), $@"RemoveAt->item.Id.IsNullOrEmpty(); index: {index}");
var result = SignalRClient.PostDataAsync(SignalRCrudTags.RemoveMessageTag, item).GetAwaiter().GetResult() ?? throw new NullReferenceException();
InnerList.RemoveAt(index);
}
}
public void Clear()
{
lock (_syncRoot) InnerList.Clear();
}
public int IndexOf(T item)
{
lock (_syncRoot)
return InnerList.FindIndex(x => x.Id == item.Id);
}
public bool Contains(T item)
{
lock (_syncRoot)
return IndexOf(item) > -1;
}
public void CopyTo(T[] array) => CopyTo(array, 0);
public void CopyTo(T[] array, int arrayIndex)
{
lock (_syncRoot) InnerList.CopyTo(array, arrayIndex);
}
public int BinarySearch(int index, int count, T item, IComparer<T>? comparer)
{
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
if (Count - index < count)
throw new ArgumentException("Invalid length");
lock (_syncRoot)
return InnerList.BinarySearch(index, count, item, comparer);
}
public int BinarySearch(T item) => BinarySearch(0, Count, item, null);
public int BinarySearch(T item, IComparer<T>? comparer) => BinarySearch(0, Count, item, comparer);
public IEnumerator<T> GetEnumerator()
{
lock (_syncRoot)
return InnerList.ToList().GetEnumerator();
}
public ReadOnlyCollection<T> AsReadOnly() => new(this);
private static bool IsCompatibleObject(object? value) => (value is T) || (value == null && default(T) == null);
#region IList, ICollection
bool IList.IsReadOnly => false;
object? IList.this[int index]
{
get => this[index];
set
{
if (default(T) != null && value == null) throw new NullReferenceException(nameof(value));
try
{
this[index] = (T)value!;
}
catch (InvalidCastException)
{
throw new InvalidCastException(nameof(value));
}
}
}
int IList.Add(object? item)
{
if (default(T) != null && item == null) throw new NullReferenceException(nameof(item));
try
{
Add((T)item!);
}
catch (InvalidCastException)
{
throw new InvalidCastException(nameof(item));
}
return Count - 1;
}
void IList.Clear() => Clear();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
bool IList.Contains(object? item) => IsCompatibleObject(item) && Contains((T)item!);
int IList.IndexOf(object? item) => (IsCompatibleObject(item)) ? IndexOf((T)item!) : -1;
void IList.Insert(int index, object? item)
{
if (default(T) != null && item == null) throw new NullReferenceException(nameof(item));
try
{
Insert(index, (T)item!);
}
catch (InvalidCastException)
{
throw new InvalidCastException(nameof(item));
}
}
void IList.Remove(object? item)
{
if (IsCompatibleObject(item)) Remove((T)item!);
}
void ICollection<T>.Clear() => Clear();
void ICollection.CopyTo(Array array, int arrayIndex)
{
if ((array != null) && (array.Rank != 1))
{
throw new ArgumentException();
}
try
{
//TODO: _list.ToArray() - ez nem az igazi... - J.
Array.Copy(InnerList.ToArray(), 0, array!, arrayIndex, InnerList.Count);
}
catch (ArrayTypeMismatchException)
{
throw new ArrayTypeMismatchException();
}
}
int ICollection.Count => Count;
int ICollection<T>.Count => Count;
bool ICollection<T>.IsReadOnly => false;
void IList<T>.RemoveAt(int index) => RemoveAt(index);
int IReadOnlyCollection<T>.Count => Count;
#endregion IList, ICollection
}
}