Compare commits

...

1 Commits

Author SHA1 Message Date
2c66adfae7 Наработки для спецификаций 2025-02-10 16:41:31 +05:00
24 changed files with 650 additions and 90 deletions

View File

@ -9,7 +9,7 @@ using DD.Persistence.Models.Common;
namespace DD.Persistence.API.Controllers;
[ApiController]
[Authorize]
//[Authorize]
[Route("api/[controller]")]
public class ChangeLogController : ControllerBase, IChangeLogApi
{

View File

@ -12,7 +12,7 @@ namespace DD.Persistence.API.Controllers;
/// Работа с уставками
/// </summary>
[ApiController]
[Authorize]
//[Authorize]
[Route("api/[controller]")]
public class SetpointController : ControllerBase, ISetpointApi
{
@ -105,8 +105,8 @@ public class SetpointController : ControllerBase, ISetpointApi
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> Add(Guid setpointKey, object newValue, CancellationToken token)
{
var userId = User.GetUserId<Guid>();
await setpointRepository.Add(setpointKey, (JsonElement)newValue, userId, token);
//var userId = User.GetUserId<Guid>();
await setpointRepository.Add(setpointKey, (JsonElement)newValue, Guid.NewGuid(), token);
return CreatedAtAction(nameof(Add), true);
}

View File

@ -1,10 +1,10 @@
{
"DbConnection": {
"Host": "postgres",
"Host": "localhost",
"Port": 5432,
"Database": "persistence",
"Username": "postgres",
"Password": "postgres"
"Password": "q"
},
"NeedUseKeyCloak": false,
"AuthUser": {

View File

@ -6,7 +6,7 @@
}
},
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
"DefaultConnection": "Host=localhost:5462;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
},
"AllowedHosts": "*",
"NeedUseKeyCloak": false,

View File

@ -0,0 +1,230 @@
// <auto-generated />
using System;
using System.Text.Json;
using DD.Persistence.Database.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace DD.Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistencePostgresContext))]
[Migration("20250210104036_IdDiscriminatorUpdateToDiscriminatorId")]
partial class IdDiscriminatorUpdateToDiscriminatorId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Ключ записи");
b.Property<DateTimeOffset>("Creation")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания записи");
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");
b.Property<Guid?>("IdNext")
.HasColumnType("uuid")
.HasComment("Id заменяющей записи");
b.Property<DateTimeOffset?>("Obsolete")
.HasColumnType("timestamp with time zone")
.HasComment("Дата устаревания (например при удалении)");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Значение");
b.HasKey("Id");
b.ToTable("change_log");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
{
b.Property<Guid>("SystemId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id системы - источника данных");
b.Property<string>("Description")
.HasColumnType("text")
.HasComment("Описание системы - источника данных");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Наименование системы - источника данных");
b.HasKey("SystemId");
b.ToTable("data_source_system");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор системы");
b.Property<int>("ParameterId")
.HasColumnType("integer")
.HasComment("Id параметра");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Значение параметра в виде строки");
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
b.ToTable("parameter_data");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.SchemeProperty", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Идентификатор схемы данных");
b.Property<int>("Index")
.HasColumnType("integer")
.HasComment("Индекс поля");
b.Property<byte>("PropertyKind")
.HasColumnType("smallint")
.HasComment("Тип индексируемого поля");
b.Property<string>("PropertyName")
.IsRequired()
.HasColumnType("text")
.HasComment("Наименования индексируемого поля");
b.HasKey("DiscriminatorId", "Index");
b.ToTable("scheme_property");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Ключ");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания уставки");
b.Property<Guid>("IdUser")
.HasColumnType("uuid")
.HasComment("Id автора последнего изменения");
b.Property<JsonElement>("Value")
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("setpoint");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id события");
b.Property<int>("CategoryId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<int>("EventState")
.HasColumnType("integer")
.HasComment("Статус события");
b.Property<Guid>("SystemId")
.HasColumnType("uuid")
.HasComment("Id системы, к которой относится сообщение");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("varchar(512)")
.HasComment("Текст сообщения");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.HasKey("EventId");
b.HasIndex("SystemId");
b.ToTable("tech_message");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор системы");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Values")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Данные");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("timestamped_values");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
.WithMany()
.HasForeignKey("SystemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("System");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DD.Persistence.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class IdDiscriminatorUpdateToDiscriminatorId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Key",
table: "setpoint",
newName: "DiscriminatorId");
migrationBuilder.RenameColumn(
name: "IdDiscriminator",
table: "change_log",
newName: "DiscriminatorId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "DiscriminatorId",
table: "setpoint",
newName: "Key");
migrationBuilder.RenameColumn(
name: "DiscriminatorId",
table: "change_log",
newName: "IdDiscriminator");
}
}
}

View File

@ -34,14 +34,14 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания записи");
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid>("IdDiscriminator")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");
@ -135,7 +135,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
{
b.Property<Guid>("Key")
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Ключ");
@ -151,7 +151,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("Key", "Timestamp");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("setpoint");
});

View File

@ -11,13 +11,13 @@ namespace DD.Persistence.Database.Entity;
/// Часть записи, описывающая изменение
/// </summary>
[Table("change_log")]
public class ChangeLog : IChangeLog
public class ChangeLog : IChangeLog, IDiscriminatorItem
{
[Key, Comment("Ключ записи")]
public Guid Id { get; set; }
[Comment("Дискриминатор таблицы")]
public Guid IdDiscriminator { get; set; }
public Guid DiscriminatorId { get; set; }
[Comment("Автор изменения")]
public Guid IdAuthor { get; set; }

View File

@ -6,11 +6,11 @@ using System.Text.Json;
namespace DD.Persistence.Database.Entity;
[Table("setpoint")]
[PrimaryKey(nameof(Key), nameof(Timestamp))]
public class Setpoint : ITimestampedItem
[PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))]
public class Setpoint : ITimestampedItem, IDiscriminatorItem
{
[Comment("Ключ")]
public Guid Key { get; set; }
public Guid DiscriminatorId { get; set; }
[Column(TypeName = "jsonb"), Comment("Значение уставки")]
public required JsonElement Value { get; set; }

View File

@ -38,7 +38,7 @@ public interface IChangeLog
/// <summary>
/// Дискриминатор таблицы
/// </summary>
public Guid IdDiscriminator { get; set; }
public Guid DiscriminatorId { get; set; }
/// <summary>
/// Значение

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.EntityAbstractions;
public interface IDiscriminatorItem
{
/// <summary>
/// Дискриминатор
/// </summary>
Guid DiscriminatorId { get; set; }
}

View File

@ -1,5 +1,9 @@
using DD.Persistence.Database.Entity;
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Postgres.Helpers;
using DD.Persistence.Database.Specifications.ChangeLog;
using DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Models.Requests;
@ -12,10 +16,13 @@ namespace DD.Persistence.Database.Repositories;
public class ChangeLogRepository : IChangeLogRepository
{
private readonly DbContext db;
private readonly ObsoleteIsNullSpec<ChangeLog> obsoleteIsNullSpecification;
public ChangeLogRepository(DbContext db)
{
this.db = db;
this.obsoleteIsNullSpecification = new ObsoleteIsNullSpec<ChangeLog>();
}
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
@ -35,9 +42,11 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token)
{
var containsIdsSpecification = new IdContainsSpec<ChangeLog>(ids);
var query = db.Set<ChangeLog>()
.Where(s => ids.Contains(s.Id))
.Where(s => s.Obsolete == null);
.WithSpecification(containsIdsSpecification)
.WithSpecification(obsoleteIsNullSpecification);
if (query.Count() != ids.Count())
{
@ -53,9 +62,11 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token)
{
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.Where(s => s.IdDiscriminator == idDiscriminator)
.Where(e => e.Obsolete == null);
.WithSpecification(specDiscriminatorEqual)
.WithSpecification(obsoleteIsNullSpecification);
var entities = await query.ToArrayAsync(token);
@ -97,8 +108,10 @@ public class ChangeLogRepository : IChangeLogRepository
var dbSet = db.Set<ChangeLog>();
var updatedIds = dtos.Select(d => d.Id);
var containsIdsSpecification = new IdContainsSpec<ChangeLog>(updatedIds);
var updatedEntities = dbSet
.Where(s => updatedIds.Contains(s.Id))
.WithSpecification(containsIdsSpecification)
.ToDictionary(s => s.Id);
var result = 0;
@ -112,7 +125,7 @@ public class ChangeLogRepository : IChangeLogRepository
throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto));
}
var newEntity = CreateEntityFromDto(idEditor, updatedEntity.IdDiscriminator, dto);
var newEntity = CreateEntityFromDto(idEditor, updatedEntity.DiscriminatorId, dto);
dbSet.Add(newEntity);
updatedEntity.IdNext = newEntity.Id;
@ -134,7 +147,11 @@ public class ChangeLogRepository : IChangeLogRepository
PaginationRequest paginationRequest,
CancellationToken token)
{
var query = CreateQuery(idDiscriminator);
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
query = query.Apply(momentUtc);
var result = await query.ApplyPagination(paginationRequest, Convert, token);
@ -142,22 +159,21 @@ public class ChangeLogRepository : IChangeLogRepository
return result;
}
private IQueryable<ChangeLog> CreateQuery(Guid idDiscriminator)
{
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
return query;
}
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
{
var query = db.Set<ChangeLog>().Where(s => s.IdDiscriminator == idDiscriminator);
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
var min = new DateTimeOffset(dateBegin.ToUniversalTime().Date, TimeSpan.Zero);
var max = new DateTimeOffset(dateEnd.ToUniversalTime().Date, TimeSpan.Zero);
var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max);
var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
var specCreatedByDateRange = new FromCreationDateRangeSpec<ChangeLog>(min, max);
var specHistoryByDateRange = new FromObsoleteDateRangeSpece<ChangeLog>(min, max);
var createdQuery = query.WithSpecification(specCreatedByDateRange);
var editedQuery = query.WithSpecification(specHistoryByDateRange);
query = createdQuery.Union(editedQuery);
var entities = await query.ToArrayAsync(token);
@ -171,7 +187,11 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token)
{
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var specObsoleteNotNull = new ObsoleteNotNullSpec<ChangeLog>();
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
var datesCreateQuery = query
.Select(e => e.Creation)
@ -180,7 +200,7 @@ public class ChangeLogRepository : IChangeLogRepository
var datesCreate = await datesCreateQuery.ToArrayAsync(token);
var datesUpdateQuery = query
.Where(e => e.Obsolete != null)
.WithSpecification(specObsoleteNotNull)
.Select(e => e.Obsolete!.Value)
.Distinct();
@ -202,7 +222,7 @@ public class ChangeLogRepository : IChangeLogRepository
Id = Uuid7.Guid(),
Creation = DateTimeOffset.UtcNow,
IdAuthor = idAuthor,
IdDiscriminator = idDiscriminator,
DiscriminatorId = idDiscriminator,
IdEditor = idAuthor,
Value = dto.Value
@ -214,9 +234,13 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
{
var date = dateBegin.ToUniversalTime();
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var specCreatedOrUpdatedGeDate = new CreatedOrUpdatedGeDateSpec<ChangeLog>(date);
var query = db.Set<ChangeLog>()
.Where(e => e.IdDiscriminator == idDiscriminator)
.Where(e => e.Creation >= date || e.Obsolete >= date);
.WithSpecification(specDiscriminatorEqual)
.WithSpecification(specCreatedOrUpdatedGeDate);
var entities = await query.ToArrayAsync(token);
@ -227,29 +251,34 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
{
var query = db.Set<ChangeLog>()
.Where(e => e.IdDiscriminator == idDiscriminator)
.GroupBy(e => 1)
.Select(group => new
{
Min = group.Min(e => e.Creation),
Max = group.Max(e => e.Obsolete.HasValue && e.Obsolete > e.Creation
? e.Obsolete.Value
: e.Creation),
});
return null;
//var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
//var evaluator =
// SpecificationEvaluator.Default
//var query = db.Set<ChangeLog>()
// //.Where(e => e.IdDiscriminator == idDiscriminator)
// .GroupBy(e => 1)
// .WithSpecification(specDiscriminatorEqual)
// .Select(group => new
// {
// Min = group.Min(e => e.Creation),
// Max = group.Max(e => e.Obsolete.HasValue && e.Obsolete > e.Creation
// ? e.Obsolete.Value
// : e.Creation),
// });
var values = await query.FirstOrDefaultAsync(token);
//var values = await query.FirstOrDefaultAsync(token);
if (values is null)
{
return null;
}
//if (values is null)
//{
// return null;
//}
return new DatesRangeDto
{
From = values.Min,
To = values.Max,
};
//return new DatesRangeDto
//{
// From = values.Min,
// To = values.Max,
//};
}
private ChangeLogValuesDto Convert(ChangeLog entity) => entity.Adapt<ChangeLogValuesDto>();

View File

@ -0,0 +1,62 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.EntityAbstractions;
using DD.Persistence.Models.Common;
namespace DD.Persistence.Database.Postgres.Repositories;
public class MyPartialEvaluator : IEvaluator
{
private MyPartialEvaluator() { }
public static MyPartialEvaluator Instance { get; } = new MyPartialEvaluator();
public bool IsCriteriaEvaluator { get; } = true;
public IQueryable<T> GetQuery<T>(IQueryable<T> query, ISpecification<T> specification) where T : class
{
// Проверяем, есть ли свойство Timestamp в типе T
var timestampProperty = typeof(T).GetProperty("Timestamp");
if (timestampProperty != null && timestampProperty.PropertyType == typeof(DateTimeOffset))
{
// Если свойство существует и имеет тип DateTime, выполняем группировку и выборку
var t = query
.GroupBy(x => 1)
.Select(group => new
{
Min = group.Min(e => (DateTimeOffset)timestampProperty.GetValue(e)),
Max = group.Max(e => (DateTimeOffset)timestampProperty.GetValue(e)),
})
.AsQueryable() as IQueryable<T>;
return t;
}
return query;
}
}
public class Test
{
public Test()
{
}
public DateTimeOffset Min { get; set; }
public DateTimeOffset Max { get; set; }
}
public class MySpecificationEvaluator : SpecificationEvaluator
{
public static MySpecificationEvaluator GroupBy { get; } = new MySpecificationEvaluator();
private MySpecificationEvaluator() : base()
{
Evaluators.Add(MyPartialEvaluator.Instance);
}
}

View File

@ -1,4 +1,8 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Specifications.Common;
using DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Repositories;
@ -19,14 +23,16 @@ namespace DD.Persistence.Database.Postgres.Repositories
protected virtual IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(
IEnumerable<Guid> setpointKeys,
CancellationToken token)
IEnumerable<Guid> setpointKeys,
CancellationToken token)
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.GroupBy(e => e.Key)
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.GroupBy(e => e.DiscriminatorId)
.Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
.ToArrayAsync(token);
@ -37,23 +43,27 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.GroupBy(e => e.Key)
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.GroupBy(e => e.DiscriminatorId)
.Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
.ToDictionaryAsync(x=> x.Key, x => (object)x.Value, token);
.ToDictionaryAsync(x=> x.DiscriminatorId, x => (object)x.Value, token);
return entities;
}
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.ToArrayAsync(token);
var filteredEntities = entities
.GroupBy(e => e.Key)
.GroupBy(e => e.DiscriminatorId)
.Select(e => e.OrderBy(o => o.Timestamp))
.Select(e => e.Where(e => e.Timestamp <= historyMoment).Last());
var dtos = filteredEntities
@ -77,31 +87,35 @@ namespace DD.Persistence.Database.Postgres.Repositories
public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
{
var query = GetQueryReadOnly()
var spec = new DatesRangeSpec<Setpoint>();
var query = db.Set<Setpoint>()
.WithSpecification(spec, MySpecificationEvaluator.GroupBy);
var query2 = GetQueryReadOnly()
.GroupBy(e => 1)
.Select(group => new
{
Min = group.Min(e => e.Timestamp),
Max = group.Max(e => e.Timestamp),
});
var values = await query.FirstOrDefaultAsync(token);
var result = new DatesRangeDto()
{
From = values?.Min ?? DateTimeOffset.MinValue,
To = values?.Max ?? DateTimeOffset.MaxValue
};
return result;
var values = await query.FirstOrDefaultAsync(token);
return null;
}
public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token)
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.ToArrayAsync(token);
var dtos = entities
.GroupBy(e => e.Key)
.GroupBy(e => e.DiscriminatorId)
.ToDictionary(e => e.Key, v => v.Select(z => z.Adapt<SetpointLogDto>()));
return dtos;
@ -111,7 +125,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var entity = new Setpoint()
{
Key = setpointKey,
DiscriminatorId = setpointKey,
Value = newValue,
IdUser = idUser,
Timestamp = DateTimeOffset.UtcNow.ToUniversalTime()

View File

@ -0,0 +1,17 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.Specifications.ChangeLog;
public class CreatedOrUpdatedGeDateSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public CreatedOrUpdatedGeDateSpec(DateTimeOffset date)
{
Query.Where(e => e.Creation >= date || e.Obsolete >= date);
}
}

View File

@ -0,0 +1,25 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска созданных за определённый период записей IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class FromCreationDateRangeSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public FromCreationDateRangeSpec(DateTimeOffset? min, DateTimeOffset? max)
{
if (min.HasValue)
{
Query.Where(e => e.Creation >= min);
}
if (max.HasValue){
Query.Where(e => e.Creation <= max);
}
//Query.Where(e => e.Creation >= min && e.Creation <= max);
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска исторических записей IChangeLog за определённый период
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class FromObsoleteDateRangeSpece<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public FromObsoleteDateRangeSpece(DateTimeOffset min, DateTimeOffset max)
{
Query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
}
}

View File

@ -0,0 +1,22 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска записей IChangeLog по массиву ключей
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class IdContainsSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public IdContainsSpec()
{
}
public IdContainsSpec(IEnumerable<Guid> ids)
{
Query.Where(s => ids.Contains(s.Id));
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для актуальных значений IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ObsoleteIsNullSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public ObsoleteIsNullSpec()
{
Query.Where(e => e.Obsolete == null);
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска не актуальных значений IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ObsoleteNotNullSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public ObsoleteNotNullSpec()
{
Query.Where(e => e.Obsolete != null);
}
}

View File

@ -0,0 +1,19 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common;
public class DatesRangeSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DatesRangeSpec()
{
//Query;
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
public class DiscriminatorContainsSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DiscriminatorContainsSpec(IEnumerable<Guid> discriminatorIds)
{
Query.Where(e => discriminatorIds.Contains(e.DiscriminatorId));
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
public class DiscriminatorEqualSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DiscriminatorEqualSpec(Guid discriminatorId)
{
Query.Where(e => e.DiscriminatorId == discriminatorId);
}
}

View File

@ -102,7 +102,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
var result = await client.Add(idDiscriminator, dto, new CancellationToken());
var entity = dbContext.ChangeLog
.Where(x => x.IdDiscriminator == idDiscriminator)
.Where(x => x.DiscriminatorId == idDiscriminator)
.FirstOrDefault();
dto = entity.Adapt<ChangeLogValuesDto>();
@ -318,7 +318,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
var entities = dtos.Select(d =>
{
var entity = d.Adapt<ChangeLog>();
entity.IdDiscriminator = idDiscriminator;
entity.DiscriminatorId = idDiscriminator;
entity.Creation = DateTimeOffset.UtcNow.AddDays(generatorRandomDigits.Next(minDayCount, maxDayCount));
return entity;