forked from ddrilling/AsbCloudServer
Doc
This commit is contained in:
parent
b89ea13c78
commit
1e7f5fe654
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ namespace AsbCloudApp.Data
|
||||
/// </summary>
|
||||
public class DrillFlowChartDto : IId
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс данных с Id
|
||||
/// </summary>
|
||||
public interface IId
|
||||
{
|
||||
/// <summary>
|
||||
/// Идентификатор БД
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,19 @@
|
||||
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс записи данных телеметрии
|
||||
/// </summary>
|
||||
public interface ITelemetryData
|
||||
{
|
||||
/// <summary>
|
||||
/// ИД телеметрии
|
||||
/// </summary>
|
||||
int IdTelemetry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Отметка времени для этой записи
|
||||
/// </summary>
|
||||
DateTime DateTime { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -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 = "----",
|
||||
|
@ -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 = "---",
|
||||
|
@ -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 = "----",
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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>();
|
||||
|
8
ConsoleApp1/Properties/launchSettings.json
Normal file
8
ConsoleApp1/Properties/launchSettings.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"WSL": {
|
||||
"commandName": "WSL2",
|
||||
"distributionName": ""
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user