This commit is contained in:
ngfrolov 2022-06-01 17:52:26 +05:00
parent b89ea13c78
commit 1e7f5fe654
22 changed files with 272 additions and 462 deletions

View File

@ -3,8 +3,10 @@ using System.Collections.Generic;
namespace AsbCloudApp.Comparators
{
/// <inheritdoc/>
public class TelemetryUserDtoComparer : IEqualityComparer<TelemetryUserDto>
{
/// <inheritdoc/>
public bool Equals(TelemetryUserDto prevUser, TelemetryUserDto nextUser)
{
if (prevUser is null || nextUser is null)
@ -15,6 +17,7 @@ namespace AsbCloudApp.Comparators
return false;
}
/// <inheritdoc/>
public int GetHashCode(TelemetryUserDto user) => user.Id.GetHashCode();
}
}

View File

@ -2,15 +2,43 @@
namespace AsbCloudApp.Data
{
#nullable enable
/// <summary>
/// DTO кустов
/// </summary>
public class ClusterDto : IMapPoint, IId
{
/// <inheritdoc/>
public int Id { get; set; }
public string Caption { get; set; }
/// <summary>
/// Название
/// </summary>
public string Caption { get; set; } = null!;
/// <inheritdoc/>
public double? Latitude { get; set; }
/// <inheritdoc/>
public double? Longitude { get; set; }
public SimpleTimezoneDto Timezone { get; set; }
/// <inheritdoc/>
public SimpleTimezoneDto Timezone { get; set; } = null!;
/// <summary>
/// ИД месторождения, необязательный
/// </summary>
public int? IdDeposit { get; set; }
public DepositBaseDto Deposit { get; set; }
public IEnumerable<WellDto> Wells { get; set; }
/// <summary>
/// DTO месторождения
/// </summary>
public DepositBaseDto? Deposit { get; set; }
/// <summary>
/// Список скважин куста
/// </summary>
public IEnumerable<WellDto> Wells { get; set; } = null!;
}
#nullable disable
}

View File

@ -1,8 +1,18 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// Статистика механической скорости проходки (МСП) по кусту
/// </summary>
public class ClusterRopStatDto
{
/// <summary>
/// Макс. механическая скорость проходки по кусту
/// </summary>
public double RopMax { get; set; }
/// <summary>
/// Средняя механическая скорость проходки по кусту
/// </summary>
public double RopAverage { get; set; }
}
}

View File

@ -1,11 +1,26 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// DTO компании
/// </summary>
public class CompanyDto : IId
{
/// <inheritdoc/>
public int Id { get; set; }
/// <summary>
/// Название
/// </summary>
public string Caption { get; set; }
/// <summary>
/// ИД типа компании
/// </summary>
public int IdCompanyType { get; set; }
/// <summary>
/// Название типа компании
/// </summary>
public string CompanyTypeCaption { get; set; }
}
}

View File

@ -1,8 +1,16 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// DTO тип компании
/// </summary>
public class CompanyTypeDto : IId
{
/// <inheritdoc/>
public int Id { get; set; }
/// <summary>
/// Название типа компании
/// </summary>
public string Caption { get; set; }
}

View File

@ -2,9 +2,19 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// Диапазон дат
/// </summary>
public class DatesRangeDto
{
/// <summary>
/// Дата начала диапазона
/// </summary>
public DateTime From { get; set; }
/// <summary>
/// Дата окончания диапазона
/// </summary>
public DateTime To { get; set; }
}
}

View File

@ -2,17 +2,37 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// DTO Месторождения
/// </summary>
public class DepositBaseDto : IMapPoint, IId
{
/// <inheritdoc/>
public int Id { get; set; }
/// <summary>
/// Название
/// </summary>
public string Caption { get; set; }
/// <inheritdoc/>
public double? Latitude { get; set; }
/// <inheritdoc/>
public double? Longitude { get; set; }
/// <inheritdoc/>
public SimpleTimezoneDto Timezone { get; set; }
}
/// <summary>
/// DTO Месторождения с кустами
/// </summary>
public class DepositDto : DepositBaseDto
{
/// <summary>
/// Кусты месторождения
/// </summary>
public IEnumerable<ClusterDto> Clusters { get; set; }
}
}

View File

@ -7,6 +7,9 @@ namespace AsbCloudApp.Data
/// </summary>
public class DrillFlowChartDto : IId
{
/// <summary>
/// <inheritdoc/>
/// </summary>
public int Id { get; set; }
/// <summary>

View File

@ -2,9 +2,19 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// Часть программы бурения
/// </summary>
public class DrillingProgramPartDto
{
/// <summary>
/// Название
/// </summary>
public string Name { get; set; }
/// <summary>
/// ИД категории файла
/// </summary>
public int IdFileCategory { get; set; }
/// <summary>
@ -13,10 +23,30 @@ namespace AsbCloudApp.Data
/// 2 - completely approved
/// </summary>
public int IdState { get; set; }
/// <summary>
/// Публикаторы. Могут загружать файл этой категории
/// </summary>
public IEnumerable<UserDto> Publishers { get; set; }
/// <summary>
/// Согласованты. Могут согласовывать загруженные файлы этой категории
/// </summary>
public IEnumerable<UserDto> Approvers { get; set; }
/// <summary>
/// Разрешение для текущего пользователя согласовывать документ
/// </summary>
public bool PermissionToApprove { get; set; }
/// <summary>
/// Разрешение для текущего пользователя загружать документ
/// </summary>
public bool PermissionToUpload { get; set; }
/// <summary>
/// Ссылка на документ.
/// </summary>
public FileInfoDto File { get; set; }
}
}

View File

@ -1,7 +1,13 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// Интерфейс данных с Id
/// </summary>
public interface IId
{
/// <summary>
/// Идентификатор БД
/// </summary>
public int Id { get; set; }
}
}

View File

@ -1,9 +1,23 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// точка на карте
/// </summary>
public interface IMapPoint
{
/// <summary>
/// Широта
/// </summary>
double? Latitude { get; set; }
/// <summary>
/// Широта
/// </summary>
double? Longitude { get; set; }
/// <summary>
/// Часовой пояс
/// </summary>
SimpleTimezoneDto Timezone { get; set; }
}
}

View File

@ -2,10 +2,19 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// Интерфейс записи данных телеметрии
/// </summary>
public interface ITelemetryData
{
/// <summary>
/// ИД телеметрии
/// </summary>
int IdTelemetry { get; set; }
/// <summary>
/// Отметка времени для этой записи
/// </summary>
DateTime DateTime { get; set; }
}
}

View File

@ -2,16 +2,38 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// DTO телеметрии панели
/// </summary>
public class TelemetryBaseDto : IId
{
/// <inheritdoc/>
public int Id { get; set; }
/// <summary>
/// уникальный идентификатор телеметрии по которому панель оператора присылает данные
/// </summary>
public string RemoteUid { get; set; }
/// <summary>
/// информация о бурении, панели оператора и контроллерах
/// </summary>
public TelemetryInfoDto Info { get; set; }
}
/// <summary>
/// DTO телеметрии панели с скважиной
/// </summary>
public class TelemetryDto : TelemetryBaseDto
{
/// <summary>
/// ИД скважины
/// </summary>
public int? IdWell { get; set; }
/// <summary>
/// DTO скважины
/// </summary>
public WellInfoDto Well { get; set; }
}
}

View File

@ -14,7 +14,7 @@ namespace AsbCloudApp.Data.WITS
/// LongMnemonic = "DEPTBITM",
/// ShortMnemonic = "DBTM",
/// Description = "Depth Bit (meas)",
/// Description2 = "Code indicating what activity is currently being performed on the rig. IT IS ESSENTIAL that this information be as accurate and current as possible. Acceptible codes are shownhere",
/// Description2 = "Code indicating what activity is currently being performed on the rig. IT IS ESSENTIAL that this information be as accurate and current as possible. Acceptable codes are shown here",
/// FPSUnits = "F",
/// MetricUnits = "M",
/// Length = 4,
@ -103,7 +103,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 14,
/// LongMnemonic = "HKLA",
/// ShortMnemonic = "HKLA",
/// Description = "Hookload (avg)",
/// Description = "Hook-load (avg)",
/// Description2 = "",
/// FPSUnits = "KLB",
/// MetricUnits = "KDN",
@ -118,7 +118,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 15,
/// LongMnemonic = "HKLX",
/// ShortMnemonic = "HKLX",
/// Description = "Hookload (max)",
/// Description = "Hook-load (max)",
/// Description2 = "",
/// FPSUnits = "KLB",
/// MetricUnits = "KDN",
@ -508,7 +508,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 41,
/// LongMnemonic = "SPARE1",
/// ShortMnemonic = "SPR1",
/// Description = "< SPARE 1>",
/// Description = "SPARE 1",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -523,7 +523,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 42,
/// LongMnemonic = "SPARE2",
/// ShortMnemonic = "SPR2",
/// Description = "< SPARE 2>",
/// Description = "SPARE 2",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -538,7 +538,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 43,
/// LongMnemonic = "SPARE3",
/// ShortMnemonic = "SPR3",
/// Description = "< SPARE 3>",
/// Description = "SPARE 3",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -553,7 +553,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 44,
/// LongMnemonic = "SPARE4",
/// ShortMnemonic = "SPR4",
/// Description = "< SPARE 4>",
/// Description = "SPARE 4",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -568,7 +568,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 45,
/// LongMnemonic = "SPARE5",
/// ShortMnemonic = "SPR5",
/// Description = "< SPARE 5>",
/// Description = "SPARE 5",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",

View File

@ -224,7 +224,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 22,
/// LongMnemonic = "SPARE1",
/// ShortMnemonic = "SPR1",
/// Description = "< SPARE 1>",
/// Description = "SPARE 1",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -239,7 +239,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 23,
/// LongMnemonic = "SPARE2",
/// ShortMnemonic = "SPR2",
/// Description = "< SPARE 2>",
/// Description = "SPARE 2",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -254,7 +254,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 24,
/// LongMnemonic = "SPARE3",
/// ShortMnemonic = "SPR3",
/// Description = "< SPARE 3>",
/// Description = "SPARE 3",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -269,7 +269,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 25,
/// LongMnemonic = "SPARE4",
/// ShortMnemonic = "SPR4",
/// Description = "< SPARE 4>",
/// Description = "SPARE 4",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -284,7 +284,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 26,
/// LongMnemonic = "SPARE5",
/// ShortMnemonic = "SPR5",
/// Description = "< SPARE 5>",
/// Description = "SPARE 5",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "---",

View File

@ -599,7 +599,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 47,
/// LongMnemonic = "SPARE1",
/// ShortMnemonic = "SPR1",
/// Description = "< SPARE 1>",
/// Description = "SPARE 1",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -614,7 +614,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 48,
/// LongMnemonic = "SPARE2",
/// ShortMnemonic = "SPR2",
/// Description = "< SPARE 2>",
/// Description = "SPARE 2",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -629,7 +629,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 49,
/// LongMnemonic = "SPARE3",
/// ShortMnemonic = "SPR3",
/// Description = "< SPARE 3>",
/// Description = "SPARE 3",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -644,7 +644,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 50,
/// LongMnemonic = "SPARE4",
/// ShortMnemonic = "SPR4",
/// Description = "< SPARE 4>",
/// Description = "SPARE 4",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -659,7 +659,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 51,
/// LongMnemonic = "SPARE5",
/// ShortMnemonic = "SPR5",
/// Description = "< SPARE 5>",
/// Description = "SPARE 5",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "---",
@ -674,7 +674,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 52,
/// LongMnemonic = "SPARE6",
/// ShortMnemonic = "SPR6",
/// Description = "< SPARE 6>",
/// Description = "SPARE 6",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -689,7 +689,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 53,
/// LongMnemonic = "SPARE7",
/// ShortMnemonic = "SPR7",
/// Description = "< SPARE 7>",
/// Description = "SPARE 7",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -704,7 +704,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 54,
/// LongMnemonic = "SPARE8",
/// ShortMnemonic = "SPR8",
/// Description = "< SPARE 8>",
/// Description = "SPARE 8",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",
@ -719,7 +719,7 @@ namespace AsbCloudApp.Data.WITS
/// ItemId = 55,
/// LongMnemonic = "SPARE9",
/// ShortMnemonic = "SPR9",
/// Description = "< SPARE 9>",
/// Description = "SPARE 9",
/// Description2 = "",
/// FPSUnits = "----",
/// MetricUnits = "----",

View File

@ -8,24 +8,16 @@ namespace AsbCloudApp.Data
/// </summary>
public class WellDto : WellInfoDto, IMapPoint, IId
{
/// <summary>
/// ID в БД
/// </summary>
/// <inheritdoc/>
public int Id { get; set; }
/// <summary>
/// Широта
/// </summary>
/// <inheritdoc/>
public double? Latitude { get; set; }
/// <summary>
/// долгота
/// </summary>
/// <inheritdoc/>
public double? Longitude { get; set; }
/// <summary>
/// Упрощенная временная зона
/// </summary>
/// <inheritdoc/>
public SimpleTimezoneDto Timezone { get; set; }
/// <summary>

View File

@ -1,19 +1,71 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services
{
#nullable enable
/// <summary>
/// Сервис получения, добавления, изменения, удаления данных
/// </summary>
/// <typeparam name="Tdto"></typeparam>
public interface ICrudService<Tdto>
where Tdto : Data.IId
{
/// <summary>
/// Включение связных данных
/// </summary>
ISet<string> Includes { get; }
/// <summary>
/// Добавление новой записи
/// </summary>
/// <param name="newItem"></param>
/// <param name="token"></param>
/// <returns>Id новой записи</returns>
Task<int> InsertAsync(Tdto newItem, CancellationToken token = default);
/// <summary>
/// Добавление нескольких записей
/// </summary>
/// <param name="newItems"></param>
/// <param name="token"></param>
/// <returns>количество добавленных</returns>
Task<int> InsertRangeAsync(IEnumerable<Tdto> newItems, CancellationToken token = default);
/// <summary>
/// Получение всех записей
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
[Obsolete("Небезопасный метод, может выполняться бесконечно долго")]
Task<IEnumerable<Tdto>> GetAllAsync(CancellationToken token = default);
Task<Tdto> GetAsync(int id, CancellationToken token = default);
/// <summary>
/// Получить запись по id
/// </summary>
/// <param name="id"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<Tdto?> GetAsync(int id, CancellationToken token = default);
/// <summary>
/// Отредактировать запись
/// </summary>
/// <param name="id"></param>
/// <param name="item"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateAsync(int id, Tdto item, CancellationToken token = default);
/// <summary>
/// Удалить запись
/// </summary>
/// <param name="id"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteAsync(int id, CancellationToken token = default);
Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token = default);
}
#nullable disable
}

View File

@ -1,411 +0,0 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Cache
{
#nullable enable
/// <summary>
/// Кеширование запросов EF.
/// </summary>
public static class EfCacheL2
{
/// <summary>
/// Кол-во обращений к БД.
/// </summary>
public static int CountOfRequestsToDB = 0;
private static readonly Dictionary<string, CacheItem> caches = new(16);
private static readonly TimeSpan semaphoreTimeout = TimeSpan.FromSeconds(25);
private static readonly SemaphoreSlim semaphore = new(1);
private static readonly TimeSpan minCacheTime = TimeSpan.FromSeconds(2);
private class CacheItem
{
internal IEnumerable? Data;
internal DateTime DateObsolete;
internal DateTime DateObsoleteTotal;
internal readonly SemaphoreSlim semaphore = new(1);
}
private static CacheItem GetOrAddCache(string tag, Func<IEnumerable> valueFactory, TimeSpan obsolete)
{
CacheItem cache;
while (!caches.ContainsKey(tag))
{
if (semaphore.Wait(0))
{
try {
cache = new CacheItem();
var dateObsolete = DateTime.Now + obsolete;
var dateQueryStart = DateTime.Now;
var data = valueFactory();
var queryTime = DateTime.Now - dateQueryStart;
if (dateObsolete - DateTime.Now < minCacheTime)
dateObsolete = DateTime.Now + minCacheTime;
cache.Data = data;
cache.DateObsolete = dateObsolete;
cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime;
caches.Add(tag, cache);
}
catch
{
throw;
}
finally
{
semaphore.Release();
}
break;
}
else
{
if (semaphore.Wait(semaphoreTimeout))
{
semaphore.Release();
}
else
{
semaphore.Release();
throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting cache");
}
}
}
cache = caches[tag];
if (cache.DateObsolete < DateTime.Now)
{
if (cache.semaphore.Wait(0))
{
try
{
var dateObsolete = DateTime.Now + obsolete;
var dateQueryStart = DateTime.Now;
var data = valueFactory();
var queryTime = DateTime.Now - dateQueryStart;
if (dateObsolete - DateTime.Now < minCacheTime)
dateObsolete = DateTime.Now + minCacheTime;
cache.Data = data;
cache.DateObsolete = dateObsolete;
cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime;
}
catch
{
throw;
}
finally
{
cache.semaphore.Release();
}
}
else if(cache.DateObsoleteTotal < DateTime.Now)
{
if (cache.semaphore.Wait(semaphoreTimeout))
{
cache.semaphore.Release();
}
else
{
cache.semaphore.Release();
throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting cache");
}
}
}
return cache;
}
private static async Task<CacheItem> GetOrAddCacheAsync(string tag, Func<CancellationToken, Task<IEnumerable>> valueFactoryAsync, TimeSpan obsolete, CancellationToken token)
{
CacheItem cache;
while (!caches.ContainsKey(tag))
{
if (semaphore.Wait(0))
{
try
{
cache = new CacheItem();
var dateObsolete = DateTime.Now + obsolete;
var dateQueryStart = DateTime.Now;
var data = await valueFactoryAsync(token);
var queryTime = DateTime.Now - dateQueryStart;
if (dateObsolete - DateTime.Now < minCacheTime)
dateObsolete = DateTime.Now + minCacheTime;
cache.Data = data;
cache.DateObsolete = dateObsolete;
cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime;
caches.Add(tag, cache);
}
catch
{
throw;
}
finally
{
semaphore.Release();
}
break;
}
else
{
if (await semaphore.WaitAsync(semaphoreTimeout, token))
{
semaphore.Release();
}
else
{
semaphore.Release();
throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting cache");
}
}
}
cache = caches[tag];
if (cache.DateObsolete < DateTime.Now)
{
if (cache.semaphore.Wait(0))
{
try
{
var dateObsolete = DateTime.Now + obsolete;
var dateQueryStart = DateTime.Now;
var data = await valueFactoryAsync(token);
var queryTime = DateTime.Now - dateQueryStart;
if (dateObsolete - DateTime.Now < minCacheTime)
dateObsolete = DateTime.Now + minCacheTime;
cache.Data = data;
cache.DateObsolete = dateObsolete;
cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime;
}
catch
{
throw;
}
finally
{
cache.semaphore.Release();
}
}
else if (cache.DateObsoleteTotal < DateTime.Now)
{
if (await cache.semaphore.WaitAsync(semaphoreTimeout, token))
{
cache.semaphore.Release();
}
else
{
cache.semaphore.Release();
throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting updated cache");
}
}
}
return cache;
}
private static IEnumerable<T> ConvertToIEnumerable<T>(IEnumerable? data)
{
if (data is IEnumerable<T> list)
return list;
else if (data is IDictionary dictionary)
{
System.Diagnostics.Trace.TraceWarning($"ConvertToIEnumerable. Use keyless method on keyed cache. Type: {typeof(T).Name};");
return (IEnumerable<T>)dictionary.Values;
}
else
throw new NotSupportedException("cache.Data has wrong type.");
}
private static Dictionary<TKey, T> ConvertToDictionary<TKey, T>(IEnumerable? data, Func<T, TKey> keySelector)
where TKey : notnull
{
if (data is Dictionary<TKey, T> dictionary)
return dictionary;
else if (data is IEnumerable<T> enumerable)
{
System.Diagnostics.Trace.TraceWarning($"ConvertToDictionary. Use keyed method on keyless cache. Type: {typeof(T).Name};");
return enumerable.ToDictionary(keySelector);
}
else
throw new NotSupportedException("cache.Data has wrong type.");
}
/// <summary>
/// Кешировать запрос в List\<typeparamref name="T"\>.
/// Выборки по PK будут работать медленнее, чем при кешировании в виде словаря.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="query"></param>
/// <param name="tag">Метка кеша</param>
/// <param name="obsolescence">Период устаревания данных</param>
/// <returns></returns>
public static IEnumerable<T> FromCache<T>(this IQueryable<T> query, string tag, TimeSpan obsolescence)
{
IEnumerable factory ()
{
CountOfRequestsToDB++;
return query.ToList();
}
var cache = GetOrAddCache(tag, factory, obsolescence);
return ConvertToIEnumerable<T>(cache.Data);
}
/// <summary>
/// Асинхронно кешировать запрос в List\<typeparamref name="T"\>.
/// Выборки по PK будут работать медленнее, чем при кешировании в виде словаря.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="query"></param>
/// <param name="tag">Метка кеша</param>
/// <param name="obsolescence">Период устаревания данных</param>
/// <param name="token"></param>
/// <returns></returns>
public static async Task<IEnumerable<T>> FromCacheAsync<T>(this IQueryable<T> query, string tag, TimeSpan obsolescence, CancellationToken token = default)
{
async Task<IEnumerable> factory(CancellationToken token)
{
CountOfRequestsToDB++;
return await query.ToListAsync(token);
}
var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token);
return ConvertToIEnumerable<T>(cache.Data);
}
/// <summary>
/// Кешировать запрос в Dictionary\<typeparamref name="TKey", typeparamref name="T"\>.
/// </summary>
/// <typeparam name="TKey">тип ключа</typeparam>
/// <typeparam name="T">тип значения</typeparam>
/// <param name="query"></param>
/// <param name="tag">Метка кеша</param>
/// <param name="obsolescence">Период устаревания данных</param>
/// <param name="keySelector">Делегат получения ключа из записи</param>
/// <returns></returns>
/// <example>
///
/// </example>
public static Dictionary<TKey, T> FromCache<TKey, T>(this IQueryable<T> query, string tag, TimeSpan obsolescence, Func<T, TKey> keySelector)
where TKey: notnull
{
IEnumerable factory ()
{
CountOfRequestsToDB++;
return query.ToDictionary(keySelector);
}
var cache = GetOrAddCache(tag, factory, obsolescence);
return ConvertToDictionary(cache.Data, keySelector);
}
/// <summary>
/// Асинхронно кешировать запрос в Dictionary\<typeparamref name="TKey", typeparamref name="T"\>.
/// </summary>
/// <typeparam name="TKey">тип ключа</typeparam>
/// <typeparam name="T">тип значения</typeparam>
/// <param name="query"></param>
/// <param name="tag">Метка кеша</param>
/// <param name="obsolescence">Период устаревания данных</param>
/// <param name="keySelector">Делегат получения ключа из записи</param>
/// <param name="token"></param>
/// <returns></returns>
public static async Task<Dictionary<TKey, T>> FromCacheAsync<TKey, T>(this IQueryable<T> query, string tag, TimeSpan obsolescence, Func<T, TKey> keySelector, CancellationToken token = default)
where TKey : notnull
{
async Task<IEnumerable> factory(CancellationToken token)
{
CountOfRequestsToDB++;
return await query.ToDictionaryAsync(keySelector, token);
}
var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token);
return ConvertToDictionary(cache.Data, keySelector);
}
/// <summary>
/// Получить запись из кеша по ключу.
/// При отсутствии кеша создаст его для всех записей из query.
/// </summary>
/// <typeparam name="TKey">тип ключа</typeparam>
/// <typeparam name="T">тип значения</typeparam>
/// <param name="query"></param>
/// <param name="tag">Метка кеша</param>
/// <param name="obsolescence">Период устаревания данных</param>
/// <param name="keySelector">Делегат получения ключа из записи</param>
/// <param name="key"></param>
/// <returns></returns>
/// <exception cref="NotSupportedException">if cache contains trash</exception>
public static T? FromCacheGetValueOrDefault<TKey, T>(this IQueryable<T> query, string tag, TimeSpan obsolescence, Func<T, TKey> keySelector, TKey key)
where TKey : notnull
{
IEnumerable factory()
{
CountOfRequestsToDB++;
return query.ToDictionary(keySelector);
}
var cache = GetOrAddCache(tag, factory, obsolescence);
var data = cache.Data;
if (data is Dictionary<TKey, T> dictionary)
return dictionary.GetValueOrDefault(key);
else if (data is IEnumerable<T> enumerable)
{
System.Diagnostics.Trace.TraceWarning($"Use keyed method on keyless cache. Tag: {tag}, type: {typeof(T).Name};");
return enumerable.FirstOrDefault(v => keySelector(v).Equals(key));
}
else
throw new NotSupportedException("cache.Data has wrong type.");
}
/// <summary>
/// Асинхронно получить запись из кеша по ключу.
/// При отсутствии кеша создаст его для всех записей из query.
/// </summary>
/// <typeparam name="TKey">тип ключа</typeparam>
/// <typeparam name="T">тип значения</typeparam>
/// <param name="query"></param>
/// <param name="tag">Метка кеша</param>
/// <param name="obsolescence">Период устаревания данных</param>
/// <param name="keySelector">Делегат получения ключа из записи</param>
/// <param name="key"></param>
/// <param name="token"></param>
/// <returns></returns>
/// <exception cref="NotSupportedException">if cache contains trash</exception>
public static async Task<T?> FromCacheGetValueOrDefaultAsync<TKey, T>(this IQueryable<T> query, string tag, TimeSpan obsolescence, Func<T, TKey> keySelector, TKey key, CancellationToken token = default)
where TKey : notnull
{
async Task<IEnumerable> factory(CancellationToken token)
{
CountOfRequestsToDB++;
return await query.ToDictionaryAsync(keySelector, token);
}
var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token);
var data = cache.Data;
if (data is Dictionary<TKey, T> dictionary)
return dictionary.GetValueOrDefault(key);
else if (data is IEnumerable<T> enumerable)
{
System.Diagnostics.Trace.TraceWarning($"Use keyed method on keyless cache. Tag: {tag}, type: {typeof(T).Name};");
return enumerable.FirstOrDefault(v => keySelector(v).Equals(key));
}
else
throw new NotSupportedException("cache.Data has wrong type.");
}
public static void DropCache<T>(this IQueryable<T> query, string tag)
{
caches.Remove(tag, out var _);
}
}
#nullable disable
}

View File

@ -124,15 +124,6 @@ namespace AsbCloudInfrastructure.Services
return context.SaveChangesAsync(token);
}
public virtual Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token = default)
{
var entities = dbSet.Where(e => ids.Contains(e.Id)).AsNoTracking();
if (entities == default)
return Task.FromResult(0);
dbSet.RemoveRange(entities);
return context.SaveChangesAsync(token);
}
public virtual TDto Convert(TModel src) => src.Adapt<TDto>();
public virtual TModel Convert(TDto src) => src.Adapt<TModel>();

View File

@ -0,0 +1,8 @@
{
"profiles": {
"WSL": {
"commandName": "WSL2",
"distributionName": ""
}
}
}