diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/Persistence.API/Controllers/DataSaubController.cs b/Persistence.API/Controllers/DataSaubController.cs new file mode 100644 index 0000000..aaeaf1e --- /dev/null +++ b/Persistence.API/Controllers/DataSaubController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using Persistence.API; +using Persistence.Repositories; +using Persistence.Repository.Data; + +namespace Persistence.API.Controllers; +[ApiController] +[Route("api/[controller]")] +public class DataSaubController : TimeSeriesController +{ + public DataSaubController(ITimeSeriesDataRepository timeSeriesDataRepository) : base(timeSeriesDataRepository) + { + + } +} diff --git a/Persistence.API/Controllers/TimeSeriesController.cs b/Persistence.API/Controllers/TimeSeriesController.cs new file mode 100644 index 0000000..ade2b48 --- /dev/null +++ b/Persistence.API/Controllers/TimeSeriesController.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Mvc; +using Persistence.Models; +using Persistence.Repositories; + +namespace Persistence.API.Controllers; +[ApiController] +[Route("api/[controller]")] +public class TimeSeriesController : ControllerBase, ITimeSeriesDataApi + where TDto : class, ITimeSeriesAbstractDto, new() +{ + private ITimeSeriesDataRepository timeSeriesDataRepository; + + public TimeSeriesController(ITimeSeriesDataRepository timeSeriesDataRepository) + { + this.timeSeriesDataRepository = timeSeriesDataRepository; + } + + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) + { + var result = await this.timeSeriesDataRepository.GetAsync(dateBegin, dateEnd, token); + return Ok(result); + } + + [HttpGet("datesRange")] + public Task GetDatesRangeAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + [HttpPost] + public async Task InsertRangeAsync(IEnumerable dtos, CancellationToken token) + { + var result = await this.timeSeriesDataRepository.InsertRange(dtos, token); + return Ok(result); + } +} diff --git a/Persistence.API/Dockerfile b/Persistence.API/Dockerfile new file mode 100644 index 0000000..bb9a0d6 --- /dev/null +++ b/Persistence.API/Dockerfile @@ -0,0 +1,24 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["Persistence.API/Persistence.API.csproj", "Persistence.API/"] +RUN dotnet restore "./Persistence.API/Persistence.API.csproj" +COPY . . +WORKDIR "/src/Persistence.API" +RUN dotnet build "./Persistence.API.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Persistence.API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Persistence.API.dll"] \ No newline at end of file diff --git a/Persistence.API/Persistence.API.csproj b/Persistence.API/Persistence.API.csproj new file mode 100644 index 0000000..f71c59d --- /dev/null +++ b/Persistence.API/Persistence.API.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + Linux + + + + + + + + + + + + + diff --git a/Persistence.API/Persistence.API.http b/Persistence.API/Persistence.API.http new file mode 100644 index 0000000..0eba8fa --- /dev/null +++ b/Persistence.API/Persistence.API.http @@ -0,0 +1,6 @@ +@Persistence.API_HostAddress = http://localhost:5032 + +GET {{Persistence.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Persistence.API/Program.cs b/Persistence.API/Program.cs new file mode 100644 index 0000000..51f3e13 --- /dev/null +++ b/Persistence.API/Program.cs @@ -0,0 +1,26 @@ + +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; + +public class Program +{ + public static void Main(string[] args) + { + var host = CreateHostBuilder(args).Build(); + Persistence.Repository.Startup.BeforeRunHandler(host); + host.Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} diff --git a/Persistence.API/Properties/launchSettings.json b/Persistence.API/Properties/launchSettings.json new file mode 100644 index 0000000..c2ccc25 --- /dev/null +++ b/Persistence.API/Properties/launchSettings.json @@ -0,0 +1,40 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5032" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:13616", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/Persistence.API/Startup.cs b/Persistence.API/Startup.cs new file mode 100644 index 0000000..d559627 --- /dev/null +++ b/Persistence.API/Startup.cs @@ -0,0 +1,40 @@ +using Persistence.Repository; + +namespace Persistence.API; + +public class Startup +{ + public IConfiguration Configuration { get; } + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public void ConfigureServices(IServiceCollection services) + { + // Add services to the container. + + services.AddControllers(); + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(); + + services.AddInfrastructure(Configuration); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + + app.UseSwagger(); + app.UseSwaggerUI(); + + app.UseRouting(); + + //app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } +} diff --git a/Persistence.API/WeatherForecast.cs b/Persistence.API/WeatherForecast.cs new file mode 100644 index 0000000..816dec8 --- /dev/null +++ b/Persistence.API/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace Persistence.API; + +public class WeatherForecast +{ + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} diff --git a/Persistence.API/appsettings.Development.json b/Persistence.API/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Persistence.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Persistence.API/appsettings.Tests.json b/Persistence.API/appsettings.Tests.json new file mode 100644 index 0000000..c1329f9 --- /dev/null +++ b/Persistence.API/appsettings.Tests.json @@ -0,0 +1,8 @@ +{ + "DbConnection": { + "Host": "localhost", + "Port": 5432, + "Username": "postgres", + "Password": "q" + } +} diff --git a/Persistence.API/appsettings.json b/Persistence.API/appsettings.json new file mode 100644 index 0000000..14eabf9 --- /dev/null +++ b/Persistence.API/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=q;Persist Security Info=True", + }, + "AllowedHosts": "*" +} diff --git a/Persistence.Database/EFExtensionsInitialization.cs b/Persistence.Database/EFExtensionsInitialization.cs new file mode 100644 index 0000000..3b28f61 --- /dev/null +++ b/Persistence.Database/EFExtensionsInitialization.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Database; +public static class EFExtensionsInitialization +{ + public static void EnsureCreatedAndMigrated(this DatabaseFacade db) + { + var connectionString = db.GetConnectionString(); + Trace.TraceInformation($"connectionString: {connectionString}"); + db.SetCommandTimeout(TimeSpan.FromMinutes(5)); + if (db.EnsureCreated()) + { + Trace.TraceInformation("Creating DB"); + Console.WriteLine("Creating DB"); + db.CreateMigrationTable(); + db.WriteMigrationsInfo(); + } + else + { + Trace.TraceInformation("Migrating DB"); + db.SetCommandTimeout(TimeSpan.FromMinutes(20)); + Console.WriteLine("db.Migrate()"); + db.Migrate(); + } + } + + private static void CreateMigrationTable(this DatabaseFacade db) + { + var sqlCreateMigrationTable = + $"CREATE TABLE public.\"__EFMigrationsHistory\" " + + $"(\"MigrationId\" varchar(150) NOT NULL, " + + $" \"ProductVersion\" varchar(32) NOT NULL, " + + $" CONSTRAINT \"PK___EFMigrationsHistory\" PRIMARY KEY (\"MigrationId\"));"; + db.ExecuteSqlRaw(sqlCreateMigrationTable); + } + + private static void WriteMigrationsInfo(this DatabaseFacade db) + { + var efVersion = db.GetType().Assembly.GetName().Version!; + var efVersionString = $"{efVersion.Major}.{efVersion.Minor}.{efVersion.Build}"; + + var migrations = db.GetPendingMigrations() + .Select(migration => $" ('{migration}', '{efVersionString}')"); + + var sqlAddLastMigration = + $"INSERT INTO public.\"__EFMigrationsHistory\" " + + $"(\"MigrationId\", \"ProductVersion\") " + + $"VALUES {string.Join(',', migrations)};"; + db.ExecuteSqlRaw(sqlAddLastMigration); + } +} diff --git a/Persistence.Database/Model/DataSaub.cs b/Persistence.Database/Model/DataSaub.cs new file mode 100644 index 0000000..4f1ad9d --- /dev/null +++ b/Persistence.Database/Model/DataSaub.cs @@ -0,0 +1,65 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Persistence.Database.Model; +public class DataSaub : ITimestampedData +{ + [Column("id")] + public int Id { get; set; } + + [Column("timestamp")] + public int TimeStamp { get; set; } + + [Column("mode")] + public int? Mode { get; set; } + + [Column("user")] + public string? User { get; set; } + + [Column("wellDepth")] + public double? WellDepth { get; set; } + + [Column("bitDepth")] + public double? BitDepth { get; set; } + + [Column("blockPosition")] + public double? BlockPosition { get; set; } + + [Column("blockSpeed")] + public double? BlockSpeed { get; set; } + + [Column("pressure")] + public double? Pressure { get; set; } + + [Column("axialLoad")] + public double? AxialLoad { get; set; } + + [Column("hookWeight")] + public double? HookWeight { get; set; } + + [Column("rotorTorque")] + public double? RotorTorque { get; set; } + + [Column("rotorSpeed")] + public double? RotorSpeed { get; set; } + + [Column("flow")] + public double? Flow { get; set; } + + [Column("mseState")] + public short MseState { get; set; } + + [Column("idFeedRegulator")] + public int IdFeedRegulator { get; set; } + + [Column("mse")] + public double? Mse { get; set; } + + [Column("pump0Flow")] + public double? Pump0Flow { get; set; } + + [Column("pump1Flow")] + public double? Pump1Flow { get; set; } + + [Column("pump2Flow")] + public double? Pump2Flow { get; set; } +} diff --git a/Persistence.Database/Model/IDbContextManager.cs b/Persistence.Database/Model/IDbContextManager.cs new file mode 100644 index 0000000..a0a6491 --- /dev/null +++ b/Persistence.Database/Model/IDbContextManager.cs @@ -0,0 +1,21 @@ +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); +} diff --git a/Persistence.Database/Model/IPersistenceDbContext.cs b/Persistence.Database/Model/IPersistenceDbContext.cs new file mode 100644 index 0000000..af837d6 --- /dev/null +++ b/Persistence.Database/Model/IPersistenceDbContext.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Database.Model; +public interface IPersistenceDbContext : IDisposable +{ + DbSet DataSaub { get; } + DatabaseFacade Database { get; } + Task SaveChangesAsync(CancellationToken cancellationToken); +} diff --git a/Persistence.Database/Model/ITimestampedData.cs b/Persistence.Database/Model/ITimestampedData.cs new file mode 100644 index 0000000..e9fb1da --- /dev/null +++ b/Persistence.Database/Model/ITimestampedData.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Database.Model; +public interface ITimestampedData +{ + int TimeStamp { get; set; } +} diff --git a/Persistence.Database/Model/PersistenceDbContext.cs b/Persistence.Database/Model/PersistenceDbContext.cs new file mode 100644 index 0000000..3baf334 --- /dev/null +++ b/Persistence.Database/Model/PersistenceDbContext.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using System; +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; +public partial class PersistenceDbContext : DbContext, IPersistenceDbContext +{ + public DbSet DataSaub => Set(); + + public PersistenceDbContext(DbContextOptions options) + { + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (!optionsBuilder.IsConfigured) + optionsBuilder.UseNpgsql("Host=localhost;Database=persistence;Username=postgres;Password=q;Persist Security Info=True;Include Error Detail=True;" + //, builder=>builder.EnableRetryOnFailure(2, System.TimeSpan.FromMinutes(1)) + ); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.HasPostgresExtension("adminpack") + .HasAnnotation("Relational:Collation", "Russian_Russia.1251"); + } + + +} diff --git a/Persistence.Database/Persistence.Database.csproj b/Persistence.Database/Persistence.Database.csproj new file mode 100644 index 0000000..4dca930 --- /dev/null +++ b/Persistence.Database/Persistence.Database.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/Persistence.IntegrationTests/ApiTokenHelper.cs b/Persistence.IntegrationTests/ApiTokenHelper.cs new file mode 100644 index 0000000..3c1fda2 --- /dev/null +++ b/Persistence.IntegrationTests/ApiTokenHelper.cs @@ -0,0 +1,43 @@ +namespace Persistence.IntegrationTests; +public static class ApiTokenHelper +{ + //public static string GetAdminUserToken() + //{ + // var user = new User() + // { + // Id = 1, + // IdCompany = 1, + // Login = "test_user" + // }; + // var roles = new[] { "root" }; + + // return CreateToken(user, roles); + //} + + //private static string CreateToken(User user, IEnumerable roles) + //{ + // var claims = new List + // { + // new("id", user.Id.ToString()), + // new(ClaimsIdentity.DefaultNameClaimType, user.Login), + // new("idCompany", user.IdCompany.ToString()), + // }; + + // claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); + + // const string secret = "супер секретный ключ для шифрования"; + + // var key = Encoding.ASCII.GetBytes(secret); + // var tokenDescriptor = new SecurityTokenDescriptor + // { + // Issuer = "a", + // Audience = "a", + // Subject = new ClaimsIdentity(claims), + // Expires = DateTime.UtcNow.AddHours(1), + // SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + // }; + // var tokenHandler = new JwtSecurityTokenHandler(); + // var token = tokenHandler.CreateToken(tokenDescriptor); + // return tokenHandler.WriteToken(token); + //} +} diff --git a/Persistence.IntegrationTests/BaseIntegrationTest.cs b/Persistence.IntegrationTests/BaseIntegrationTest.cs new file mode 100644 index 0000000..a0edda9 --- /dev/null +++ b/Persistence.IntegrationTests/BaseIntegrationTest.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; +using Persistence.Database.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Persistence.IntegrationTests; +public abstract class BaseIntegrationTest : IClassFixture, + IDisposable +{ + protected readonly IServiceScope scope; + + protected readonly PersistenceDbContext dbContext; + + protected BaseIntegrationTest(WebAppFactoryFixture factory) + { + //scope = factory.Services.CreateScope(); + + //dbContext = scope.ServiceProvider.GetRequiredService(); + } + + public void Dispose() + { + scope.Dispose(); + dbContext.Dispose(); + } +} diff --git a/Persistence.IntegrationTests/Clients/ITimeSeriesClient.cs b/Persistence.IntegrationTests/Clients/ITimeSeriesClient.cs new file mode 100644 index 0000000..7c78be4 --- /dev/null +++ b/Persistence.IntegrationTests/Clients/ITimeSeriesClient.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Mvc; +using Persistence.Repository.Data; +using Refit; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.IntegrationTests.Clients; +public interface ITimeSeriesClient + where TDto : class, new() +{ + [Post("/api/dataSaub")] + Task> InsertRangeAsync(IEnumerable dtos); +} diff --git a/Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs b/Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs new file mode 100644 index 0000000..fa23e4b --- /dev/null +++ b/Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs @@ -0,0 +1,45 @@ +using Persistence.Repository.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Persistence.IntegrationTests.Controllers; +public class DataSaubControllerTest : TimeSeriesBaseControllerTest +{ + private readonly DataSaubDto dto = new DataSaubDto() + { + AxialLoad = 1, + BitDepth = 2, + BlockPosition = 3, + BlockSpeed = 4, + Date = DateTimeOffset.Now, + Flow = 5, + HookWeight = 6, + Id = 7, + IdFeedRegulator = 8, + Mode = 9, + Mse = 10, + MseState = 11, + Pressure = 12, + Pump0Flow = 13, + Pump1Flow = 14, + Pump2Flow = 15, + RotorSpeed = 16, + RotorTorque = 17, + User = string.Empty, + WellDepth = 18, + }; + + public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory) + { + } + + [Fact] + public async Task InsertRange_returns_success() + { + await InsertRangeSuccess(dto); + } +} diff --git a/Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs b/Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs new file mode 100644 index 0000000..3277010 --- /dev/null +++ b/Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs @@ -0,0 +1,51 @@ +using Mapster; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Persistence.IntegrationTests.Clients; +using Persistence.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Persistence.IntegrationTests.Controllers; +public abstract class TimeSeriesBaseControllerTest : BaseIntegrationTest + where TDto : class, new() +{ + private ITimeSeriesClient client; + + public TimeSeriesBaseControllerTest(WebAppFactoryFixture factory) : base(factory) + { + //dbContext.CleanupDbSet(); + client = factory.GetHttpClient>(string.Empty); + } + + public async Task InsertRangeSuccess(TDto dto) + { + //arrange + var expected = dto.Adapt(); + + //act + var response = await client.InsertRangeAsync(new TDto[] { expected }); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(1, response.Content); + + //var entity = GetByWellId(); + + //Assert.NotNull(entity); + + //var actual = entity.Adapt>(); + //Assert.Equal(ProcessMapPlanBase.IdStateActual, actual.IdState); + + //var excludeProps = new[] { + // nameof(ProcessMapPlanBaseDto.Id), + // nameof(ProcessMapPlanBaseDto.Section) + //}; + //MatchHelper.Match(expected, actual.Item, excludeProps); + } +} diff --git a/Persistence.IntegrationTests/DbConnection.cs b/Persistence.IntegrationTests/DbConnection.cs new file mode 100644 index 0000000..f9c7623 --- /dev/null +++ b/Persistence.IntegrationTests/DbConnection.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.IntegrationTests; +public class DbConnection +{ + public string Host { get; set; } = null!; + + public int Port { get; set; } + + public string Username { get; set; } = null!; + + public string Password { get; set; } = null!; + + public string GetConnectionString() => + $"Host={Host};Database={Guid.NewGuid()};Port={Port};Username={Username};Password={Password};Persist Security Info=True;Include Error Detail=True"; +} diff --git a/Persistence.IntegrationTests/Persistence.IntegrationTests.csproj b/Persistence.IntegrationTests/Persistence.IntegrationTests.csproj new file mode 100644 index 0000000..2b793de --- /dev/null +++ b/Persistence.IntegrationTests/Persistence.IntegrationTests.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/Persistence.IntegrationTests/WebAppFactoryFixture.cs b/Persistence.IntegrationTests/WebAppFactoryFixture.cs new file mode 100644 index 0000000..ea6016d --- /dev/null +++ b/Persistence.IntegrationTests/WebAppFactoryFixture.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Persistence.Database.Model; +using Persistence.API; +using Refit; +using System.Net.Http.Headers; +using System.Text.Json; + +namespace Persistence.IntegrationTests; +public class WebAppFactoryFixture : WebApplicationFactory +{ + private static readonly JsonSerializerOptions JsonSerializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + //Converters = { new ValidationResultConverter() } + }; + + private static readonly RefitSettings RefitSettings = new(new SystemTextJsonContentSerializer(JsonSerializerOptions)); + + private readonly string connectionString; + + public WebAppFactoryFixture() + { + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.Tests.json") + .Build(); + + var dbConnection = configuration.GetSection("DbConnection").Get()!; + connectionString = dbConnection.GetConnectionString(); + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.ConfigureServices(services => + { + var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions)); + + if (descriptor != null) + services.Remove(descriptor); + + services.AddDbContext(options => + options.UseNpgsql(connectionString)); + + var serviceProvider = services.BuildServiceProvider(); + + using var scope = serviceProvider.CreateScope(); + var scopedServices = scope.ServiceProvider; + var dbContext = scopedServices.GetRequiredService(); + + //dbContext.Database.EnsureCreatedAndMigrated(); + //dbContext.Deposits.AddRange(Data.Defaults.Deposits); + dbContext.SaveChanges(); + }); + } + + public override async ValueTask DisposeAsync() + { + var dbContext = new PersistenceDbContext( + new DbContextOptionsBuilder() + .UseNpgsql(connectionString) + .Options); + + await dbContext.Database.EnsureDeletedAsync(); + } + + public T GetHttpClient(string uriSuffix) + { + var httpClient = CreateClient(); + if (string.IsNullOrEmpty(uriSuffix)) + return RestService.For(httpClient, RefitSettings); + + if (httpClient.BaseAddress is not null) + httpClient.BaseAddress = new Uri(httpClient.BaseAddress, uriSuffix); + + return RestService.For(httpClient, RefitSettings); + } + + //public T GetAuthorizedHttpClient(string uriSuffix) + //{ + // var httpClient = GetAuthorizedHttpClient(); + // if (string.IsNullOrEmpty(uriSuffix)) + // return RestService.For(httpClient, RefitSettings); + + // if (httpClient.BaseAddress is not null) + // httpClient.BaseAddress = new Uri(httpClient.BaseAddress, uriSuffix); + + // return RestService.For(httpClient, RefitSettings); + //} + + //private HttpClient GetAuthorizedHttpClient() + //{ + // var httpClient = CreateClient(); + // var jwtToken = ApiTokenHelper.GetAdminUserToken(); + // httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken); + // return httpClient; + //} +} diff --git a/Persistence.Repository/Data/DataSaubDto.cs b/Persistence.Repository/Data/DataSaubDto.cs new file mode 100644 index 0000000..bb9f74f --- /dev/null +++ b/Persistence.Repository/Data/DataSaubDto.cs @@ -0,0 +1,46 @@ +using Persistence.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Persistence.Repository.Data; +public class DataSaubDto : ITimeSeriesAbstractDto +{ + public int Id { get; set; } + + public DateTimeOffset Date { get; set; } = DateTimeOffset.UtcNow; + + public int? Mode { get; set; } + + public string? User { get; set; } + + public double? WellDepth { get; set; } + + public double? BitDepth { get; set; } + + public double? BlockPosition { get; set; } + + public double? BlockSpeed { get; set; } + + public double? Pressure { get; set; } + + public double? AxialLoad { get; set; } + + public double? HookWeight { get; set; } + + public double? RotorTorque { get; set; } + + public double? RotorSpeed { get; set; } + + public double? Flow { get; set; } + + public short MseState { get; set; } + + public int IdFeedRegulator { get; set; } + + public double? Mse { get; set; } + + public double? Pump0Flow { get; set; } + + public double? Pump1Flow { get; set; } + + public double? Pump2Flow { get; set; } +} diff --git a/Persistence.Repository/DependencyInjection.cs b/Persistence.Repository/DependencyInjection.cs new file mode 100644 index 0000000..17904c8 --- /dev/null +++ b/Persistence.Repository/DependencyInjection.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Persistence.Repositories; +using Persistence.Database.Model; +using Persistence.Repository.Data; +using Persistence.Repository.Repositories; + +namespace Persistence.Repository; +public static class DependencyInjection +{ + public static void MapsterSetup() + { + } + public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) + { + MapsterSetup(); + + string connectionStringName = "DefaultConnection"; + + services.AddDbContext(options => + options.UseNpgsql(configuration.GetConnectionString(connectionStringName))); + + services.AddScoped(provider => provider.GetRequiredService()); + + services.AddTransient, TimeSeriesDataRepository>(); + + return services; + } +} diff --git a/Persistence.Repository/Persistence.Repository.csproj b/Persistence.Repository/Persistence.Repository.csproj new file mode 100644 index 0000000..55bd8ea --- /dev/null +++ b/Persistence.Repository/Persistence.Repository.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/Persistence.Repository/Repositories/TimeSeriesDataRepository.cs b/Persistence.Repository/Repositories/TimeSeriesDataRepository.cs new file mode 100644 index 0000000..97dd358 --- /dev/null +++ b/Persistence.Repository/Repositories/TimeSeriesDataRepository.cs @@ -0,0 +1,50 @@ +using Mapster; +using Microsoft.EntityFrameworkCore; +using Persistence.Models; +using Persistence.Repositories; +using Persistence.Database.Model; +using Persistence.Repository.Data; + +namespace Persistence.Repository.Repositories; +public abstract class TimeSeriesDataRepository : ITimeSeriesDataRepository + where TEntity : class + where TDto : class, ITimeSeriesAbstractDto, new() +{ + private DbContext db; + + public TimeSeriesDataRepository(DbContext db) + { + this.db = db; + } + + protected virtual IQueryable GetQueryReadOnly() => db.Set(); + + public async Task> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) + { + var query = GetQueryReadOnly(); + var entities = await query.ToArrayAsync(token); + var dtos = entities.Select(e => e.Adapt()); + + return dtos; + } + + public Task GetDatesRangeAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + + public Task> GetGtDate(DateTimeOffset date, CancellationToken token) + { + throw new NotImplementedException(); + } + + public async Task InsertRange(IEnumerable dtos, CancellationToken token) + { + var entities = dtos.Select(d => d.Adapt()); + + await db.Set().AddRangeAsync(entities, token); + var result = await db.SaveChangesAsync(token); + + return result; + } +} diff --git a/Persistence.Repository/Startup.cs b/Persistence.Repository/Startup.cs new file mode 100644 index 0000000..1d57cbb --- /dev/null +++ b/Persistence.Repository/Startup.cs @@ -0,0 +1,19 @@ +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(); + context.Database.EnsureCreatedAndMigrated(); + + } +} diff --git a/Persistence.sln b/Persistence.sln index ea65a82..a3e9f69 100644 --- a/Persistence.sln +++ b/Persistence.sln @@ -3,7 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34714.143 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence", "Persistence\Persistence.csproj", "{417177AE-A27E-445B-B3B9-D5EFCEC812A0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence", "Persistence\Persistence.csproj", "{417177AE-A27E-445B-B3B9-D5EFCEC812A0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.API", "Persistence.API\Persistence.API.csproj", "{8650A227-929E-45F0-AEF7-2C91F45FE884}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Repository", "Persistence.Repository\Persistence.Repository.csproj", "{493D6D92-231B-4CB6-831B-BE13884B0DE4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Database", "Persistence.Database\Persistence.Database.csproj", "{F77475D1-D074-407A-9D69-2FADDDAE2056}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Persistence.IntegrationTests", "Persistence.IntegrationTests\Persistence.IntegrationTests.csproj", "{10752C25-3773-4081-A1F2-215A1D950126}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +23,22 @@ Global {417177AE-A27E-445B-B3B9-D5EFCEC812A0}.Debug|Any CPU.Build.0 = Debug|Any CPU {417177AE-A27E-445B-B3B9-D5EFCEC812A0}.Release|Any CPU.ActiveCfg = Release|Any CPU {417177AE-A27E-445B-B3B9-D5EFCEC812A0}.Release|Any CPU.Build.0 = Release|Any CPU + {8650A227-929E-45F0-AEF7-2C91F45FE884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8650A227-929E-45F0-AEF7-2C91F45FE884}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8650A227-929E-45F0-AEF7-2C91F45FE884}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8650A227-929E-45F0-AEF7-2C91F45FE884}.Release|Any CPU.Build.0 = Release|Any CPU + {493D6D92-231B-4CB6-831B-BE13884B0DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {493D6D92-231B-4CB6-831B-BE13884B0DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {493D6D92-231B-4CB6-831B-BE13884B0DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {493D6D92-231B-4CB6-831B-BE13884B0DE4}.Release|Any CPU.Build.0 = Release|Any CPU + {F77475D1-D074-407A-9D69-2FADDDAE2056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F77475D1-D074-407A-9D69-2FADDDAE2056}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F77475D1-D074-407A-9D69-2FADDDAE2056}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F77475D1-D074-407A-9D69-2FADDDAE2056}.Release|Any CPU.Build.0 = Release|Any CPU + {10752C25-3773-4081-A1F2-215A1D950126}.Debug|Any CPU.ActiveCfg = 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.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Persistence/API/IApiChangeLog.cs b/Persistence/API/IChangeLogApi.cs similarity index 98% rename from Persistence/API/IApiChangeLog.cs rename to Persistence/API/IChangeLogApi.cs index 0690bf7..716944e 100644 --- a/Persistence/API/IApiChangeLog.cs +++ b/Persistence/API/IChangeLogApi.cs @@ -6,7 +6,7 @@ namespace Persistence.API; /// /// Интерфейс для работы с API журнала изменений /// -public interface IApiChangeLog +public interface IChangeLogApi where TDto : class, new() where TChangeLogDto : ChangeLogDto { diff --git a/Persistence/API/IApiDictionaryElement.cs b/Persistence/API/IDictionaryElementApi.cs similarity index 96% rename from Persistence/API/IApiDictionaryElement.cs rename to Persistence/API/IDictionaryElementApi.cs index de36f9e..540d454 100644 --- a/Persistence/API/IApiDictionaryElement.cs +++ b/Persistence/API/IDictionaryElementApi.cs @@ -5,7 +5,7 @@ namespace Persistence.API; /// /// Интерфейс для API, предназначенного для работы с элементами справочников /// -public interface IApiDictionaryElement where TDto : class, new() +public interface IDictionaryElementApi where TDto : class, new() { /// /// Получить все данные справочника diff --git a/Persistence/API/IGraphData.cs b/Persistence/API/IGraphDataApi.cs similarity index 96% rename from Persistence/API/IGraphData.cs rename to Persistence/API/IGraphDataApi.cs index 6911412..b609f2f 100644 --- a/Persistence/API/IGraphData.cs +++ b/Persistence/API/IGraphDataApi.cs @@ -6,7 +6,7 @@ namespace Persistence.API; /// /// Интерфейс для работы с API графиков /// -public interface IGraphData +public interface IGraphDataApi { /// /// Получить список объектов с прореживанием, удовлетворящий диапазону дат diff --git a/Persistence/API/IApiSetpoint.cs b/Persistence/API/ISetpointApi.cs similarity index 98% rename from Persistence/API/IApiSetpoint.cs rename to Persistence/API/ISetpointApi.cs index 14fa8d2..d086557 100644 --- a/Persistence/API/IApiSetpoint.cs +++ b/Persistence/API/ISetpointApi.cs @@ -6,7 +6,7 @@ namespace Persistence.API; /// /// Интерфейс для API, предназначенного для работы с уставками /// -public interface IApiSetpoint +public interface ISetpointApi { /// /// Получить актуальные значения уставок diff --git a/Persistence/API/IApiSync.cs b/Persistence/API/ISyncApi.cs similarity index 94% rename from Persistence/API/IApiSync.cs rename to Persistence/API/ISyncApi.cs index cb3d50d..9df4301 100644 --- a/Persistence/API/IApiSync.cs +++ b/Persistence/API/ISyncApi.cs @@ -6,7 +6,7 @@ namespace Persistence.API; /// /// Интерфейс для API, предназначенного для синхронизации данных /// -public interface IApiSync where TDto : class, new() +public interface ISyncApi where TDto : class, new() { /// /// Получить порцию записей, начиная с заданной даты diff --git a/Persistence/API/IApiTableData.cs b/Persistence/API/ITableDataApi.cs similarity index 94% rename from Persistence/API/IApiTableData.cs rename to Persistence/API/ITableDataApi.cs index f3f0364..ae7099c 100644 --- a/Persistence/API/IApiTableData.cs +++ b/Persistence/API/ITableDataApi.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace Persistence.API; /// Интерфейс для API, предназначенного для работы с табличными данными -public interface IApiTableData +public interface ITableDataApi where TDto : class, new() where TRequest : RequestDto { diff --git a/Persistence/API/IApiTimeSeriesData.cs b/Persistence/API/ITimeSeriesDataApi.cs similarity index 66% rename from Persistence/API/IApiTimeSeriesData.cs rename to Persistence/API/ITimeSeriesDataApi.cs index f00aa7f..a6a0363 100644 --- a/Persistence/API/IApiTimeSeriesData.cs +++ b/Persistence/API/ITimeSeriesDataApi.cs @@ -11,24 +11,24 @@ namespace Persistence.API; /// /// Интерфейс для работы с API временных данных /// -public interface IApiTimeSeriesData - where TDto : class, new() +public interface ITimeSeriesDataApi + where TDto : class, ITimeSeriesAbstractDto, new() { /// - /// Получить спискок объектов, удовлетворяющий диапазон дат + /// Получить список объектов, удовлетворяющий диапазон дат /// /// дата начала /// дата окончания /// /// - Task>> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token); + Task GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token); /// /// Получить диапазон дат, для которых есть данные в репозитории /// /// /// - Task> GetDatesRangeAsync(CancellationToken token); + Task GetDatesRangeAsync(CancellationToken token); /// /// Добавление записей @@ -36,7 +36,7 @@ public interface IApiTimeSeriesData /// /// /// - Task> InsertRange(IEnumerable dtos, CancellationToken token); + Task InsertRangeAsync(IEnumerable dtos, CancellationToken token); } diff --git a/Persistence/Repositories/AbstractChangeLogRepository.cs b/Persistence/Repositories/AbstractChangeLogRepository.cs index 1e1db12..88cf511 100644 --- a/Persistence/Repositories/AbstractChangeLogRepository.cs +++ b/Persistence/Repositories/AbstractChangeLogRepository.cs @@ -4,162 +4,162 @@ using Persistence.Models; using System.Linq; namespace Persistence.Repositories; -public abstract class AbstractChangeLogRepository : IChangeLogRepository - where TDto : class, new() - where TEntity : class, IChangeLogAbstract - where TChangeLogDto : ChangeLogDto -{ - private readonly DbContext dbContext; +//public abstract class AbstractChangeLogRepository : IChangeLogRepository +// where TDto : class, new() +// where TEntity : class, IChangeLogAbstract +// where TChangeLogDto : ChangeLogDto +//{ +// private readonly DbContext dbContext; - protected AbstractChangeLogRepository(DbContext dbContext) - { - this.dbContext = dbContext; - } +// protected AbstractChangeLogRepository(DbContext dbContext) +// { +// this.dbContext = dbContext; +// } - public abstract TEntity Convert(TDto entity); - public async Task Clear(int idUser,CancellationToken token) - { - throw new NotImplementedException(); +// public abstract TEntity Convert(TDto entity); +// public async Task Clear(int idUser,CancellationToken token) +// { +// throw new NotImplementedException(); - //var updateTime = DateTimeOffset.UtcNow; +// //var updateTime = DateTimeOffset.UtcNow; - ////todo - //var query = BuildQuery(request); - //query = query.Where(e => e.Obsolete == null); +// ////todo +// //var query = BuildQuery(request); +// //query = query.Where(e => e.Obsolete == null); - //var entitiesToDelete = await query.ToArrayAsync(token); +// //var entitiesToDelete = await query.ToArrayAsync(token); - //foreach (var entity in entitiesToDelete) - //{ - // entity.IdState = IChangeLogAbstract.IdCleared; - // entity.Obsolete = updateTime; - // entity.IdEditor = idUser; - //} +// //foreach (var entity in entitiesToDelete) +// //{ +// // entity.IdState = IChangeLogAbstract.IdCleared; +// // entity.Obsolete = updateTime; +// // entity.IdEditor = idUser; +// //} - //var result = await SaveChangesWithExceptionHandling(token); - //return result; - } +// //var result = await SaveChangesWithExceptionHandling(token); +// //return result; +// } - public async Task ClearAndInsertRange(int idUser, IEnumerable dtos, CancellationToken token) - { - var result = 0; - using var transaction = await dbContext.Database.BeginTransactionAsync(token); - try - { - result += await Clear(idUser, token); - result += await InsertRangeWithoutTransaction(idUser, dtos, token); +// public async Task ClearAndInsertRange(int idUser, IEnumerable dtos, CancellationToken token) +// { +// var result = 0; +// using var transaction = await dbContext.Database.BeginTransactionAsync(token); +// try +// { +// result += await Clear(idUser, token); +// result += await InsertRangeWithoutTransaction(idUser, dtos, token); - await transaction.CommitAsync(token); - return result; - } - catch - { - await transaction.RollbackAsync(token); - throw; - } - } +// await transaction.CommitAsync(token); +// return result; +// } +// catch +// { +// await transaction.RollbackAsync(token); +// throw; +// } +// } - public Task> GetCurrent(DateTimeOffset moment, CancellationToken token) - { - throw new NotImplementedException(); - } +// public Task> GetCurrent(DateTimeOffset moment, CancellationToken token) +// { +// throw new NotImplementedException(); +// } - public Task> GetDatesChange(CancellationToken token) - { - throw new NotImplementedException(); - } +// public Task> GetDatesChange(CancellationToken token) +// { +// throw new NotImplementedException(); +// } - public Task> GetGtDate(DateTimeOffset date, CancellationToken token) - { - throw new NotImplementedException(); - } +// public Task> GetGtDate(DateTimeOffset date, CancellationToken token) +// { +// throw new NotImplementedException(); +// } - public async Task InsertRange(int idUser, IEnumerable dtos, CancellationToken token) - { - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var result = await InsertRangeWithoutTransaction(idUser, dtos, token); - await transaction.CommitAsync(token); - return result; - } - catch - { - await transaction.RollbackAsync(token); - throw; - } - } +// public async Task InsertRange(int idUser, IEnumerable dtos, CancellationToken token) +// { +// using var transaction = dbContext.Database.BeginTransaction(); +// try +// { +// var result = await InsertRangeWithoutTransaction(idUser, dtos, token); +// await transaction.CommitAsync(token); +// return result; +// } +// catch +// { +// await transaction.RollbackAsync(token); +// throw; +// } +// } - protected abstract DatabaseFacade GetDataBase(); +// protected abstract DatabaseFacade GetDataBase(); - public Task MarkAsDeleted(int idUser, IEnumerable ids, CancellationToken token) - { - throw new NotImplementedException(); - } +// public Task MarkAsDeleted(int idUser, IEnumerable ids, CancellationToken token) +// { +// throw new NotImplementedException(); +// } - public Task UpdateOrInsertRange(int idUser, IEnumerable dtos, CancellationToken token) - { - throw new NotImplementedException(); - } +// public Task UpdateOrInsertRange(int idUser, IEnumerable dtos, CancellationToken token) +// { +// throw new NotImplementedException(); +// } - public Task UpdateRange(int idUser, IEnumerable dtos, CancellationToken token) - { - throw new NotImplementedException(); - } +// public Task UpdateRange(int idUser, IEnumerable dtos, CancellationToken token) +// { +// throw new NotImplementedException(); +// } - public Task> GetChangeLogForDate(DateTimeOffset? updateFrom, CancellationToken token) - { - throw new NotImplementedException(); - } +// public Task> GetChangeLogForDate(DateTimeOffset? updateFrom, CancellationToken token) +// { +// throw new NotImplementedException(); +// } - private async Task InsertRangeWithoutTransaction(int idUser, IEnumerable dtos, CancellationToken token) - { - var result = 0; - if (dtos.Any()) - { - var entities = dtos.Select(Convert); - var creation = DateTimeOffset.UtcNow; - var dbSet = dbContext.Set(); - foreach (var entity in entities) - { - entity.Id = default; - entity.IdAuthor = idUser; - entity.Creation = creation; - entity.IdState = IChangeLogAbstract.IdStateActual; - entity.IdEditor = null; - entity.IdPrevious = null; - entity.Obsolete = null; - dbSet.Add(entity); - } +// private async Task InsertRangeWithoutTransaction(int idUser, IEnumerable dtos, CancellationToken token) +// { +// var result = 0; +// if (dtos.Any()) +// { +// var entities = dtos.Select(Convert); +// var creation = DateTimeOffset.UtcNow; +// var dbSet = dbContext.Set(); +// foreach (var entity in entities) +// { +// entity.Id = default; +// entity.IdAuthor = idUser; +// entity.Creation = creation; +// entity.IdState = IChangeLogAbstract.IdStateActual; +// entity.IdEditor = null; +// entity.IdPrevious = null; +// entity.Obsolete = null; +// dbSet.Add(entity); +// } - result += await SaveChangesWithExceptionHandling(token); - } +// result += await SaveChangesWithExceptionHandling(token); +// } - return result; - } +// return result; +// } - private async Task SaveChangesWithExceptionHandling(CancellationToken token) - { - var result = await dbContext.SaveChangesAsync(token); - return result; - //try - //{ - // var result = await dbContext.SaveChangesAsync(token); - // return result; - //} - //catch (DbUpdateException ex) - //{ - // if (ex.InnerException is PostgresException pgException) - // TryConvertPostgresExceptionToValidateException(pgException); - // throw; - //} - } +// private async Task SaveChangesWithExceptionHandling(CancellationToken token) +// { +// var result = await dbContext.SaveChangesAsync(token); +// return result; +// //try +// //{ +// // var result = await dbContext.SaveChangesAsync(token); +// // return result; +// //} +// //catch (DbUpdateException ex) +// //{ +// // if (ex.InnerException is PostgresException pgException) +// // TryConvertPostgresExceptionToValidateException(pgException); +// // throw; +// //} +// } - //private static void TryConvertPostgresExceptionToValidateException(PostgresException pgException) - //{ - // if (pgException.SqlState == PostgresErrorCodes.ForeignKeyViolation) - // throw new ArgumentInvalidException("dtos", pgException.Message + "\r\n" + pgException.Detail); - //} -} +// //private static void TryConvertPostgresExceptionToValidateException(PostgresException pgException) +// //{ +// // if (pgException.SqlState == PostgresErrorCodes.ForeignKeyViolation) +// // throw new ArgumentInvalidException("dtos", pgException.Message + "\r\n" + pgException.Detail); +// //} +//} diff --git a/Persistence/Repositories/AbstractTimeSeriesDataRepository.cs b/Persistence/Repositories/AbstractTimeSeriesDataRepository.cs deleted file mode 100644 index c947dbc..0000000 --- a/Persistence/Repositories/AbstractTimeSeriesDataRepository.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Persistence.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Persistence.Repositories; -public abstract class AbstractTimeSeriesDataRepository : ITimeSeriesDataRepository - where TDto : class, ITimeSeriesAbstractDto - where TEntity : class, IChangeLogAbstract -{ - private readonly DbContext dbContext; - - protected AbstractTimeSeriesDataRepository(DbContext dbContext) - { - this.dbContext = dbContext; - } - - public Task> GetAsync(DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) - { - throw new NotImplementedException(); - } - - public Task GetDatesRangeAsync(CancellationToken token) - { - throw new NotImplementedException(); - } - - public Task> GetGtDate(DateTimeOffset date, CancellationToken token) - { - throw new NotImplementedException(); - } - - public Task InsertRange(IEnumerable dtos, CancellationToken token) - { - throw new NotImplementedException(); - } -} diff --git a/Persistence/Repositories/IChangeLogRepository.cs b/Persistence/Repositories/IChangeLogRepository.cs index d85beb6..c12c61b 100644 --- a/Persistence/Repositories/IChangeLogRepository.cs +++ b/Persistence/Repositories/IChangeLogRepository.cs @@ -7,7 +7,7 @@ namespace Persistence.Repositories; /// /// public interface IChangeLogRepository : ISyncRepository - where TDto : class + where TDto : class, ITimeSeriesAbstractDto, new() where TChangeLogDto : ChangeLogDto { /// diff --git a/Persistence/Repositories/ISyncRepository.cs b/Persistence/Repositories/ISyncRepository.cs index 8fea16f..e18c5f5 100644 --- a/Persistence/Repositories/ISyncRepository.cs +++ b/Persistence/Repositories/ISyncRepository.cs @@ -5,6 +5,7 @@ /// /// public interface ISyncRepository + where TDto : class, new() { /// /// Получить данные, начиная с определенной даты diff --git a/Persistence/Repositories/ITimeSeriesDataRepository.cs b/Persistence/Repositories/ITimeSeriesDataRepository.cs index e450c87..16aae46 100644 --- a/Persistence/Repositories/ITimeSeriesDataRepository.cs +++ b/Persistence/Repositories/ITimeSeriesDataRepository.cs @@ -7,7 +7,7 @@ namespace Persistence.Repositories; /// /// public interface ITimeSeriesDataRepository : ISyncRepository - where TDto : class, ITimeSeriesAbstractDto + where TDto : class, ITimeSeriesAbstractDto, new() { /// /// Получить страницу списка объектов