Compare commits

...

4 Commits

37 changed files with 903 additions and 93 deletions

View File

@ -0,0 +1,48 @@
using Microsoft.AspNetCore.Mvc;
using Persistence.Models;
using Persistence.Repositories;
namespace Persistence.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class SetpointController : ControllerBase, ISetpointApi
{
private readonly ISetpointRepository setpointRepository;
public SetpointController(ISetpointRepository setpointRepository)
{
this.setpointRepository = setpointRepository;
}
[HttpPost("current")]
public Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrentAsync(IEnumerable<Guid> setpointKeys, CancellationToken token)
{
throw new NotImplementedException();
}
[HttpPost("history")]
public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistoryAsync(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{
var result = await setpointRepository.GetHistoryAsync(setpointKeys, historyMoment, token);
return Ok(result);
}
[HttpPost("log")]
public async Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLogAsync([FromBody] IEnumerable<Guid> setpointKeys, CancellationToken token)
{
var result = await setpointRepository.GetLogAsync(setpointKeys, token);
return Ok(result);
}
[HttpPost("save")]
public async Task<ActionResult<int>> SaveAsync(Guid setpointKey, object newValue, CancellationToken token)
{
var result = await setpointRepository.SaveAsync(setpointKey, newValue, token);
return Ok(result);
}
}
}

View File

@ -13,6 +13,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Persistence.Database.Postgres\Persistence.Database.Postgres.csproj" />
<ProjectReference Include="..\Persistence.Repository\Persistence.Repository.csproj" /> <ProjectReference Include="..\Persistence.Repository\Persistence.Repository.csproj" />
<ProjectReference Include="..\Persistence\Persistence.csproj" /> <ProjectReference Include="..\Persistence\Persistence.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -1,11 +1,4 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Persistence.Repositories;
using Persistence.Repository;
using Persistence.Repository.Data;
using Persistence.Repository.Repositories;
namespace Persistence.API; namespace Persistence.API;
public class Program public class Program
@ -13,7 +6,7 @@ public class Program
public static void Main(string[] args) public static void Main(string[] args)
{ {
var host = CreateHostBuilder(args).Build(); var host = CreateHostBuilder(args).Build();
Persistence.Repository.Startup.BeforeRunHandler(host); Startup.BeforeRunHandler(host);
host.Run(); host.Run();
} }

View File

@ -1,4 +1,6 @@
using Persistence.Repository; using Persistence.Repository;
using Persistence.Database.Model;
using Persistence.Database.Postgres;
namespace Persistence.API; namespace Persistence.API;
@ -18,8 +20,8 @@ public class Startup
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
services.AddEndpointsApiExplorer(); services.AddEndpointsApiExplorer();
services.AddSwaggerGen(); services.AddSwaggerGen();
services.AddPersistenceDbContext(Configuration);
services.AddInfrastructure(Configuration); services.AddInfrastructure();
} }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
@ -37,4 +39,15 @@ public class Startup
endpoints.MapControllers(); endpoints.MapControllers();
}); });
} }
public static void BeforeRunHandler(IHost host)
{
using var scope = host.Services.CreateScope();
var provider = scope.ServiceProvider;
var context = provider.GetRequiredService<PersistenceDbContext>();
context.Database.EnsureCreatedAndMigrated();
}
} }

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Persistence.Database;
using Persistence.Database.Model;
namespace Persistence.Database.Model;
public static class DependencyInjection
{
public static IServiceCollection AddPersistenceDbContext(this IServiceCollection services, IConfiguration configuration)
{
string connectionStringName = "DefaultConnection";
services.AddDbContext<PersistenceDbContext>(options =>
options.UseNpgsql(configuration.GetConnectionString(connectionStringName)));
services.AddScoped<DbContext>(provider => provider.GetRequiredService<PersistenceDbContext>());
return services;
}
}

View File

@ -7,7 +7,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Persistence.Database; namespace Persistence.Database.Postgres;
public static class EFExtensionsInitialization public static class EFExtensionsInitialization
{ {
public static void EnsureCreatedAndMigrated(this DatabaseFacade db) public static void EnsureCreatedAndMigrated(this DatabaseFacade db)

View File

@ -0,0 +1,122 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Persistence.Database.Model;
#nullable disable
namespace Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistenceDbContext))]
[Migration("20241115105149_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.UseCollation("Russian_Russia.1251")
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double?>("AxialLoad")
.HasColumnType("double precision")
.HasColumnName("axialLoad");
b.Property<double?>("BitDepth")
.HasColumnType("double precision")
.HasColumnName("bitDepth");
b.Property<double?>("BlockPosition")
.HasColumnType("double precision")
.HasColumnName("blockPosition");
b.Property<double?>("BlockSpeed")
.HasColumnType("double precision")
.HasColumnName("blockSpeed");
b.Property<double?>("Flow")
.HasColumnType("double precision")
.HasColumnName("flow");
b.Property<double?>("HookWeight")
.HasColumnType("double precision")
.HasColumnName("hookWeight");
b.Property<int>("IdFeedRegulator")
.HasColumnType("integer")
.HasColumnName("idFeedRegulator");
b.Property<int?>("Mode")
.HasColumnType("integer")
.HasColumnName("mode");
b.Property<double?>("Mse")
.HasColumnType("double precision")
.HasColumnName("mse");
b.Property<short>("MseState")
.HasColumnType("smallint")
.HasColumnName("mseState");
b.Property<double?>("Pressure")
.HasColumnType("double precision")
.HasColumnName("pressure");
b.Property<double?>("Pump0Flow")
.HasColumnType("double precision")
.HasColumnName("pump0Flow");
b.Property<double?>("Pump1Flow")
.HasColumnType("double precision")
.HasColumnName("pump1Flow");
b.Property<double?>("Pump2Flow")
.HasColumnType("double precision")
.HasColumnName("pump2Flow");
b.Property<double?>("RotorSpeed")
.HasColumnType("double precision")
.HasColumnName("rotorSpeed");
b.Property<double?>("RotorTorque")
.HasColumnType("double precision")
.HasColumnName("rotorTorque");
b.Property<int>("TimeStamp")
.HasColumnType("integer")
.HasColumnName("timestamp");
b.Property<string>("User")
.HasColumnType("text")
.HasColumnName("user");
b.Property<double?>("WellDepth")
.HasColumnType("double precision")
.HasColumnName("wellDepth");
b.HasKey("Id");
b.ToTable("DataSaub");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,56 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Persistence.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:PostgresExtension:adminpack", ",,");
migrationBuilder.CreateTable(
name: "DataSaub",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
timestamp = table.Column<int>(type: "integer", nullable: false),
mode = table.Column<int>(type: "integer", nullable: true),
user = table.Column<string>(type: "text", nullable: true),
wellDepth = table.Column<double>(type: "double precision", nullable: true),
bitDepth = table.Column<double>(type: "double precision", nullable: true),
blockPosition = table.Column<double>(type: "double precision", nullable: true),
blockSpeed = table.Column<double>(type: "double precision", nullable: true),
pressure = table.Column<double>(type: "double precision", nullable: true),
axialLoad = table.Column<double>(type: "double precision", nullable: true),
hookWeight = table.Column<double>(type: "double precision", nullable: true),
rotorTorque = table.Column<double>(type: "double precision", nullable: true),
rotorSpeed = table.Column<double>(type: "double precision", nullable: true),
flow = table.Column<double>(type: "double precision", nullable: true),
mseState = table.Column<short>(type: "smallint", nullable: false),
idFeedRegulator = table.Column<int>(type: "integer", nullable: false),
mse = table.Column<double>(type: "double precision", nullable: true),
pump0Flow = table.Column<double>(type: "double precision", nullable: true),
pump1Flow = table.Column<double>(type: "double precision", nullable: true),
pump2Flow = table.Column<double>(type: "double precision", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_DataSaub", x => x.id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "DataSaub");
}
}
}

View File

@ -0,0 +1,146 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Persistence.Database.Model;
#nullable disable
namespace Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistenceDbContext))]
[Migration("20241118052225_SetpointMigration")]
partial class SetpointMigration
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.UseCollation("Russian_Russia.1251")
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double?>("AxialLoad")
.HasColumnType("double precision")
.HasColumnName("axialLoad");
b.Property<double?>("BitDepth")
.HasColumnType("double precision")
.HasColumnName("bitDepth");
b.Property<double?>("BlockPosition")
.HasColumnType("double precision")
.HasColumnName("blockPosition");
b.Property<double?>("BlockSpeed")
.HasColumnType("double precision")
.HasColumnName("blockSpeed");
b.Property<double?>("Flow")
.HasColumnType("double precision")
.HasColumnName("flow");
b.Property<double?>("HookWeight")
.HasColumnType("double precision")
.HasColumnName("hookWeight");
b.Property<int>("IdFeedRegulator")
.HasColumnType("integer")
.HasColumnName("idFeedRegulator");
b.Property<int?>("Mode")
.HasColumnType("integer")
.HasColumnName("mode");
b.Property<double?>("Mse")
.HasColumnType("double precision")
.HasColumnName("mse");
b.Property<short>("MseState")
.HasColumnType("smallint")
.HasColumnName("mseState");
b.Property<double?>("Pressure")
.HasColumnType("double precision")
.HasColumnName("pressure");
b.Property<double?>("Pump0Flow")
.HasColumnType("double precision")
.HasColumnName("pump0Flow");
b.Property<double?>("Pump1Flow")
.HasColumnType("double precision")
.HasColumnName("pump1Flow");
b.Property<double?>("Pump2Flow")
.HasColumnType("double precision")
.HasColumnName("pump2Flow");
b.Property<double?>("RotorSpeed")
.HasColumnType("double precision")
.HasColumnName("rotorSpeed");
b.Property<double?>("RotorTorque")
.HasColumnType("double precision")
.HasColumnName("rotorTorque");
b.Property<int>("TimeStamp")
.HasColumnType("integer")
.HasColumnName("timestamp");
b.Property<string>("User")
.HasColumnType("text")
.HasColumnName("user");
b.Property<double?>("WellDepth")
.HasColumnType("double precision")
.HasColumnName("wellDepth");
b.HasKey("Id");
b.ToTable("DataSaub");
});
modelBuilder.Entity("Persistence.Database.Model.Setpoint", b =>
{
b.Property<Guid>("Key")
.HasColumnType("uuid")
.HasComment("Ключ");
b.Property<DateTimeOffset>("Created")
.HasColumnType("timestamp with time zone")
.HasComment("Дата изменения уставки");
b.Property<int>("IdUser")
.HasColumnType("integer")
.HasComment("Id автора последнего изменения");
b.Property<object>("Value")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("Key", "Created");
b.ToTable("Setpoint");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Persistence.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class SetpointMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Setpoint",
columns: table => new
{
Key = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ"),
Created = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата изменения уставки"),
Value = table.Column<object>(type: "jsonb", nullable: false, comment: "Значение уставки"),
IdUser = table.Column<int>(type: "integer", nullable: false, comment: "Id автора последнего изменения")
},
constraints: table =>
{
table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Created });
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Setpoint");
}
}
}

View File

@ -0,0 +1,143 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Persistence.Database.Model;
#nullable disable
namespace Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistenceDbContext))]
partial class PersistenceDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.UseCollation("Russian_Russia.1251")
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double?>("AxialLoad")
.HasColumnType("double precision")
.HasColumnName("axialLoad");
b.Property<double?>("BitDepth")
.HasColumnType("double precision")
.HasColumnName("bitDepth");
b.Property<double?>("BlockPosition")
.HasColumnType("double precision")
.HasColumnName("blockPosition");
b.Property<double?>("BlockSpeed")
.HasColumnType("double precision")
.HasColumnName("blockSpeed");
b.Property<double?>("Flow")
.HasColumnType("double precision")
.HasColumnName("flow");
b.Property<double?>("HookWeight")
.HasColumnType("double precision")
.HasColumnName("hookWeight");
b.Property<int>("IdFeedRegulator")
.HasColumnType("integer")
.HasColumnName("idFeedRegulator");
b.Property<int?>("Mode")
.HasColumnType("integer")
.HasColumnName("mode");
b.Property<double?>("Mse")
.HasColumnType("double precision")
.HasColumnName("mse");
b.Property<short>("MseState")
.HasColumnType("smallint")
.HasColumnName("mseState");
b.Property<double?>("Pressure")
.HasColumnType("double precision")
.HasColumnName("pressure");
b.Property<double?>("Pump0Flow")
.HasColumnType("double precision")
.HasColumnName("pump0Flow");
b.Property<double?>("Pump1Flow")
.HasColumnType("double precision")
.HasColumnName("pump1Flow");
b.Property<double?>("Pump2Flow")
.HasColumnType("double precision")
.HasColumnName("pump2Flow");
b.Property<double?>("RotorSpeed")
.HasColumnType("double precision")
.HasColumnName("rotorSpeed");
b.Property<double?>("RotorTorque")
.HasColumnType("double precision")
.HasColumnName("rotorTorque");
b.Property<int>("TimeStamp")
.HasColumnType("integer")
.HasColumnName("timestamp");
b.Property<string>("User")
.HasColumnType("text")
.HasColumnName("user");
b.Property<double?>("WellDepth")
.HasColumnType("double precision")
.HasColumnName("wellDepth");
b.HasKey("Id");
b.ToTable("DataSaub");
});
modelBuilder.Entity("Persistence.Database.Model.Setpoint", b =>
{
b.Property<Guid>("Key")
.HasColumnType("uuid")
.HasComment("Ключ");
b.Property<DateTimeOffset>("Created")
.HasColumnType("timestamp with time zone")
.HasComment("Дата изменения уставки");
b.Property<int>("IdUser")
.HasColumnType("integer")
.HasComment("Id автора последнего изменения");
b.Property<object>("Value")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("Key", "Created");
b.ToTable("Setpoint");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Persistence.Database\Persistence.Database.csproj" />
</ItemGroup>
</Project>

View File

@ -1,19 +1,34 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System.Data.Common;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
namespace Persistence.Database.Model; namespace Persistence.Database.Model;
public partial class PersistenceDbContext : DbContext, IPersistenceDbContext public partial class PersistenceDbContext : DbContext, IPersistenceDbContext
{ {
private readonly DbConnection connection = null!;
public DbSet<DataSaub> DataSaub => Set<DataSaub>(); public DbSet<DataSaub> DataSaub => Set<DataSaub>();
public PersistenceDbContext(DbContextOptions<PersistenceDbContext> options) public DbSet<Setpoint> Setpoint => Set<Setpoint>();
public PersistenceDbContext()
: base()
{ {
}
public PersistenceDbContext(DbContextOptions<PersistenceDbContext> options)
: base(options)
{
}
public PersistenceDbContext(DbConnection connection)
{
this.connection = connection;
}
protected virtual DbConnection GetConnection()
{
return connection;
} }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

View File

@ -0,0 +1,5 @@
## Создать миграцию
```
dotnet ef migrations add <MigrationName> --project Persistence.Database.Postgres
```

View File

@ -0,0 +1,10 @@
namespace Persistence.Database.Model
{
public interface ISetpointData
{
public Guid Key { get; set; }
public object Value { get; set; }
public DateTimeOffset Created { get; set; }
public int IdUser { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Persistence.Database.Model
{
[PrimaryKey(nameof(Key), nameof(Created))]
public class Setpoint : ISetpointData
{
[Comment("Ключ")]
public Guid Key { get; set; }
[Column(TypeName = "jsonb"), Comment("Значение уставки")]
public required object Value { get; set; }
[Comment("Дата изменения уставки")]
public DateTimeOffset Created { get; set; }
[Comment("Id автора последнего изменения")]
public int IdUser { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Persistence.Database.Model
{
public class SetpointDictionary
{
[Key, Comment("Ключ")]
public Guid Key { get; set; }
[Comment("Наименование")]
public required string Name { get; set; }
[Comment("Описание")]
public string? Description { get; set; }
}
}

View File

@ -0,0 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Persistence.Database.Model;
namespace Persistence.Database;
public interface IPersistenceDbContext : IDisposable
{
DbSet<DataSaub> DataSaub { get; }
}

View File

@ -1,21 +0,0 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Persistence.Database.Model;
public interface IDbContextManager
{
//IConnectionManager ConnectionManager { get; }
DbContext GetReadonlyDbContext();
DbContext GetDbContext();
DbContext CreateAndInitializeNewContext();
DbContext CreateAndInitializeNewContext(DbConnection connection);
}

View File

@ -11,6 +11,7 @@ namespace Persistence.Database.Model;
public interface IPersistenceDbContext : IDisposable public interface IPersistenceDbContext : IDisposable
{ {
DbSet<DataSaub> DataSaub { get; } DbSet<DataSaub> DataSaub { get; }
DbSet<Setpoint> Setpoint { get; }
DatabaseFacade Database { get; } DatabaseFacade Database { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken); Task<int> SaveChangesAsync(CancellationToken cancellationToken);
} }

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -17,9 +17,9 @@ public abstract class BaseIntegrationTest : IClassFixture<WebAppFactoryFixture>,
protected BaseIntegrationTest(WebAppFactoryFixture factory) protected BaseIntegrationTest(WebAppFactoryFixture factory)
{ {
//scope = factory.Services.CreateScope(); scope = factory.Services.CreateScope();
//dbContext = scope.ServiceProvider.GetRequiredService<PersistenceDbContext>(); dbContext = scope.ServiceProvider.GetRequiredService<PersistenceDbContext>();
} }
public void Dispose() public void Dispose()

View File

@ -0,0 +1,17 @@
using Persistence.Models;
using Refit;
namespace Persistence.IntegrationTests.Clients
{
public interface ISetpointClient
{
[Post("/api/Setpoint/history")]
Task<IApiResponse<IEnumerable<SetpointValueDto>>> GetHistoryAsync(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment);
[Post("/api/Setpoint/log")]
Task<IApiResponse<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLogAsync(IEnumerable<Guid> setpoitKeys);
[Post("/api/Setpoint/save")]
Task<IApiResponse<int>> SaveAsync(Guid setpointKey, object newValue);
}
}

View File

@ -0,0 +1,75 @@
using System.Net;
using Persistence.IntegrationTests.Clients;
using Xunit;
namespace Persistence.IntegrationTests.Controllers
{
public class SetpointControllerTest : BaseIntegrationTest
{
private ISetpointClient client;
private class TestObject
{
public string? value1 { get; set; }
public int value2 { get; set; }
public double value3 { get; set; }
}
public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
{
client = factory.GetHttpClient<ISetpointClient>(string.Empty);
}
[Fact]
public async Task GetHistoryAsync_returns_success()
{
//arrange
var setpointKeys = new List<Guid>()
{
Guid.NewGuid(),
Guid.NewGuid()
};
var historyMoment = DateTimeOffset.Now.ToUniversalTime();
//act
var response = await client.GetHistoryAsync(setpointKeys, historyMoment);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task GetLogAsync_returns_success()
{
//arrange
var setpointKeys = new List<Guid>()
{
Guid.NewGuid(),
Guid.NewGuid()
};
//act
var response = await client.GetLogAsync(setpointKeys);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task SaveAsync_returns_success()
{
//arrange
var setpointKey = Guid.NewGuid();
var setpointValue = new TestObject()
{
value1 = "1",
value2 = 2,
value3 = 3.3
};
//act
var response = await client.SaveAsync(setpointKey, setpointValue);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}

View File

@ -24,6 +24,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Persistence.API\Persistence.API.csproj" /> <ProjectReference Include="..\Persistence.API\Persistence.API.csproj" />
<ProjectReference Include="..\Persistence.Database.Postgres\Persistence.Database.Postgres.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -8,6 +8,7 @@ using Persistence.API;
using Refit; using Refit;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text.Json; using System.Text.Json;
using Persistence.Database.Postgres;
namespace Persistence.IntegrationTests; namespace Persistence.IntegrationTests;
public class WebAppFactoryFixture : WebApplicationFactory<Startup> public class WebAppFactoryFixture : WebApplicationFactory<Startup>
@ -51,7 +52,7 @@ public class WebAppFactoryFixture : WebApplicationFactory<Startup>
var scopedServices = scope.ServiceProvider; var scopedServices = scope.ServiceProvider;
var dbContext = scopedServices.GetRequiredService<PersistenceDbContext>(); var dbContext = scopedServices.GetRequiredService<PersistenceDbContext>();
//dbContext.Database.EnsureCreatedAndMigrated(); dbContext.Database.EnsureCreatedAndMigrated();
//dbContext.Deposits.AddRange(Data.Defaults.Deposits); //dbContext.Deposits.AddRange(Data.Defaults.Deposits);
dbContext.SaveChanges(); dbContext.SaveChanges();
}); });

View File

@ -0,0 +1,10 @@
namespace Persistence.Repository.Data
{
public class SetpointDto
{
public int Id { get; set; }
public required object Value { get; set; }
public DateTimeOffset Edit { get; set; }
public int IdUser { get; set; }
}
}

View File

@ -1,10 +1,11 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Persistence.Repositories; using Persistence.Repositories;
using Persistence.Database.Model; using Persistence.Database.Model;
using Persistence.Repository.Data; using Persistence.Repository.Data;
using Persistence.Repository.Repositories; using Persistence.Repository.Repositories;
using Persistence.Database;
namespace Persistence.Repository; namespace Persistence.Repository;
public static class DependencyInjection public static class DependencyInjection
@ -12,18 +13,13 @@ public static class DependencyInjection
public static void MapsterSetup() public static void MapsterSetup()
{ {
} }
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
{ {
MapsterSetup(); MapsterSetup();
string connectionStringName = "DefaultConnection";
services.AddDbContext<PersistenceDbContext>(options =>
options.UseNpgsql(configuration.GetConnectionString(connectionStringName)));
services.AddScoped<IPersistenceDbContext>(provider => provider.GetRequiredService<PersistenceDbContext>());
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>(); services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>();
services.AddTransient<ISetpointRepository, SetpointRepository>();
return services; return services;
} }

View File

@ -0,0 +1,69 @@
using Mapster;
using Microsoft.EntityFrameworkCore;
using Persistence.Database.Model;
using Persistence.Models;
using Persistence.Repositories;
namespace Persistence.Repository.Repositories
{
public class SetpointRepository : ISetpointRepository
{
private DbContext db;
public SetpointRepository(DbContext db)
{
this.db = db;
}
protected virtual IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
public async Task<IEnumerable<SetpointValueDto>> GetHistoryAsync(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key) && e.Created.Date == historyMoment.Date)
.ToArrayAsync(token);
var dtos = entities.Select(e => e.Adapt<SetpointValueDto>());
return dtos;
}
public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLogAsync(IEnumerable<Guid> setpointKeys, CancellationToken token)
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.ToArrayAsync(token);
var dtos = entities
.GroupBy(e => e.Key)
.Select(e => new KeyValuePair<Guid, IEnumerable<SetpointLogDto>>(
e.Key,
e.Select(s => s.Adapt<SetpointLogDto>())
)).ToDictionary();
return dtos;
}
public async Task<int> SaveAsync(Guid setpointKey, object newValue, CancellationToken token)
{
try
{
var entity = new Setpoint()
{
Key = setpointKey,
Value = newValue,
IdUser = 0, // ToDo: откуда тянуть?
Created = DateTimeOffset.Now.ToUniversalTime()
};
await db.Set<Setpoint>().AddAsync(entity, token);
var result = await db.SaveChangesAsync(token);
return result;
}
catch (Exception)
{
return 0;
}
}
}
}

View File

@ -4,9 +4,10 @@ using Persistence.Models;
using Persistence.Repositories; using Persistence.Repositories;
using Persistence.Database.Model; using Persistence.Database.Model;
using Persistence.Repository.Data; using Persistence.Repository.Data;
using Persistence.Database;
namespace Persistence.Repository.Repositories; namespace Persistence.Repository.Repositories;
public abstract class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository<TDto> public class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository<TDto>
where TEntity : class where TEntity : class
where TDto : class, ITimeSeriesAbstractDto, new() where TDto : class, ITimeSeriesAbstractDto, new()
{ {
@ -17,7 +18,7 @@ public abstract class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataR
this.db = db; this.db = db;
} }
protected virtual IQueryable<TEntity> GetQueryReadOnly() => db.Set<TEntity>(); protected virtual IQueryable<TEntity> GetQueryReadOnly() => this.db.Set<TEntity>();
public async Task<IEnumerable<TDto>> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) public async Task<IEnumerable<TDto>> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
{ {

View File

@ -1,19 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Persistence.Database;
using Persistence.Database.Model;
namespace Persistence.Repository;
public class Startup
{
public static void BeforeRunHandler(IHost host)
{
using var scope = host.Services.CreateScope();
var provider = scope.ServiceProvider;
var context = provider.GetRequiredService<DbContext>();
context.Database.EnsureCreatedAndMigrated();
}
}

View File

@ -11,7 +11,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Repository", "P
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database", "Persistence.Database\Persistence.Database.csproj", "{F77475D1-D074-407A-9D69-2FADDDAE2056}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database", "Persistence.Database\Persistence.Database.csproj", "{F77475D1-D074-407A-9D69-2FADDDAE2056}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence.IntegrationTests", "Persistence.IntegrationTests\Persistence.IntegrationTests.csproj", "{10752C25-3773-4081-A1F2-215A1D950126}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.IntegrationTests", "Persistence.IntegrationTests\Persistence.IntegrationTests.csproj", "{10752C25-3773-4081-A1F2-215A1D950126}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence.Database.Postgres", "Persistence.Database.Postgres\Persistence.Database.Postgres.csproj", "{CC284D27-162D-490C-B6CF-74D666B7C5F3}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -39,6 +41,10 @@ Global
{10752C25-3773-4081-A1F2-215A1D950126}.Debug|Any CPU.Build.0 = Debug|Any CPU {10752C25-3773-4081-A1F2-215A1D950126}.Debug|Any CPU.Build.0 = Debug|Any CPU
{10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.ActiveCfg = Release|Any CPU {10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.Build.0 = Release|Any CPU {10752C25-3773-4081-A1F2-215A1D950126}.Release|Any CPU.Build.0 = Release|Any CPU
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC284D27-162D-490C-B6CF-74D666B7C5F3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -7,6 +7,6 @@ using System.Threading.Tasks;
namespace Persistence.Models; namespace Persistence.Models;
public class SetpointLogDto : SetpointValueDto public class SetpointLogDto : SetpointValueDto
{ {
public DateTimeOffset Edit { get; set; } public DateTimeOffset Created { get; set; }
public int IdUser { get; set; } public int IdUser { get; set; }
} }

View File

@ -8,7 +8,7 @@ namespace Persistence.Models;
public class SetpointValueDto public class SetpointValueDto
{ {
public int Id { get; set; } public Guid Key { get; set; }
public object Value { get; set; } public required object Value { get; set; }
} }

View File

@ -1,10 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Persistence.Models;
using Persistence.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Persistence.Repositories; namespace Persistence.Repositories;
@ -17,19 +11,19 @@ public interface ISetpointRepository
/// <summary> /// <summary>
/// Получить значения уставок за определенный момент времени /// Получить значения уставок за определенный момент времени
/// </summary> /// </summary>
/// <param name="setpoitKeys"></param> /// <param name="setpointKeys"></param>
/// <param name="historyMoment">дата, на которую получаем данные</param> /// <param name="historyMoment">дата, на которую получаем данные</param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<SetpointValueDto>> GetHistoryAsync(IEnumerable<Guid> setpoitKeys, DateTimeOffset historyMoment, CancellationToken token); Task<IEnumerable<SetpointValueDto>> GetHistoryAsync(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token);
/// <summary> /// <summary>
/// Получить историю изменений значений уставок /// Получить историю изменений значений уставок
/// </summary> /// </summary>
/// <param name="setpoitKeys"></param> /// <param name="setpointKeys"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLogAsync(IEnumerable<Guid> setpoitKeys, CancellationToken token); Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLogAsync(IEnumerable<Guid> setpointKeys, CancellationToken token);
/// <summary> /// <summary>
/// Метод сохранения уставки /// Метод сохранения уставки
@ -41,5 +35,5 @@ public interface ISetpointRepository
/// <returns></returns> /// <returns></returns>
/// to do /// to do
/// id User учесть в соответствующем методе репозитория /// id User учесть в соответствующем методе репозитория
Task<int> SaveAsync(Guid setpointKey, int idUser, object newValue, CancellationToken token); Task<int> SaveAsync(Guid setpointKey, object newValue, CancellationToken token);
} }