Создание таблиц для цепочек подтверждения #1

Merged
gorbenko.pavel merged 9 commits from db-structure into main 2026-07-23 04:55:25 +00:00
21 changed files with 651 additions and 10 deletions
+15
View File
@@ -0,0 +1,15 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/.idea.Tnsu.ApprovalRoute.iml
/modules.xml
/projectSettingsUpdater.xml
/contentModel.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+1
View File
@@ -0,0 +1 @@
Tnsu.ApprovalRoute
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
+10 -7
View File
@@ -3,11 +3,14 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
<PackageVersion Include="MediatR" Version="12.4.1" />
<PackageVersion Include="EFCore.NamingConventions" Version="10.0.1" />
<PackageVersion Include="Extensions.Hosting.AsyncInitialization" Version="3.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.10" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
<PackageVersion Include="MediatR" Version="14.2.0" />
</ItemGroup>
</Project>
</Project>
@@ -1,3 +1,7 @@
using Extensions.Hosting.AsyncInitialization;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Tnsu.ApprovalRoute.Application;
using Tnsu.ApprovalRoute.Infrastructure;
@@ -15,6 +19,6 @@ if (app.Environment.IsDevelopment())
app.MapHealthChecks("/health");
app.Run();
await app.InitAndRunAsync();
public partial class Program;
@@ -4,8 +4,13 @@
<ProjectReference Include="..\Tnsu.ApprovalRoute.Infrastructure\Tnsu.ApprovalRoute.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Extensions.Hosting.AsyncInitialization" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="MediatR" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace Tnsu.ApprovalRoute.Domain.Entities;
public class RequestType
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
public string PageUrl { get; set; } = string.Empty;
public ICollection<Route> Routes { get; set; } = new List<Route>();
}
@@ -0,0 +1,20 @@
using Tnsu.ApprovalRoute.Domain.Enums;
namespace Tnsu.ApprovalRoute.Domain.Entities;
public class Route
{
public Guid Id { get; set; } = Guid.NewGuid();
public RouteType RouteType { get; set; } = RouteType.Sequential;
public Guid RequestTypeId { get; set; }
// Внешние справочники (1С/ZUP) — хранятся как ссылки без FK в этой БД.
public Guid DepartmentId { get; set; }
public Guid CompanyId { get; set; }
public bool ByProject { get; set; }
public bool RequiresControl { get; set; }
public RequestType? RequestType { get; set; }
public ICollection<RouteStep> Steps { get; set; } = new List<RouteStep>();
}
@@ -0,0 +1,20 @@
using Tnsu.ApprovalRoute.Domain.Enums;
namespace Tnsu.ApprovalRoute.Domain.Entities;
public class RouteStep
{
Review

Надо добавить поле Роль на проекте (enum)

Надо добавить поле Роль на проекте (enum)
Review

поправил

поправил
public Guid Id { get; set; } = Guid.NewGuid();
public int OrderNo { get; set; }
// Внешняя ссылка на пользователя (1С/ZUP) — без локальной таблицы Users.
public string ExecutorEmail { get; set; } = string.Empty;
public string Position { get; set; } = string.Empty;
public ProjectRole ProjectRole { get; set; } = ProjectRole.None;
public RouteStepStatus Status { get; set; } = RouteStepStatus.Draft;
public Guid RouteId { get; set; }
public bool AllowReassignToOtherApprovers { get; set; }
public Route? Route { get; set; }
}
@@ -0,0 +1,9 @@
namespace Tnsu.ApprovalRoute.Domain.Enums;
public enum ProjectRole
{
None,
Manager,
Engineer,
Observer
}
@@ -0,0 +1,13 @@
namespace Tnsu.ApprovalRoute.Domain.Enums;
public enum RouteStepStatus
{
None,
Draft,
OnApproval,
Approved,
InExecution,
Completed,
Returned,
Closed
}
@@ -0,0 +1,8 @@
namespace Tnsu.ApprovalRoute.Domain.Enums;
public enum RouteType
{
None,
Parallel,
Sequential
}
@@ -1,3 +1,4 @@
using Extensions.Hosting.AsyncInitialization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -11,7 +12,9 @@ public static class InfrastructureServiceCollectionExtensions
this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<ApprovalRouteDbContext>(opt =>
opt.UseNpgsql(configuration.GetConnectionString("Postgres")));
opt.UseNpgsql(configuration.GetConnectionString("Postgres"))
.UseSnakeCaseNamingConvention());
services.AddAsyncInitializer<DatabaseMigrationInitializer>();
return services;
}
@@ -1,7 +1,39 @@
using Microsoft.EntityFrameworkCore;
using Tnsu.ApprovalRoute.Domain.Entities;
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence;
public class ApprovalRouteDbContext(DbContextOptions<ApprovalRouteDbContext> options) : DbContext(options)
{
public DbSet<RequestType> RequestTypes => Set<RequestType>();
public DbSet<Route> Routes => Set<Route>();
public DbSet<RouteStep> RouteSteps => Set<RouteStep>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RequestType>(e =>
{
e.HasKey(x => x.Id);
e.Property(x => x.Name).HasMaxLength(256).IsRequired();
e.Property(x => x.PageUrl).HasMaxLength(512).IsRequired();
});
modelBuilder.Entity<Route>(e =>
{
e.HasKey(x => x.Id);
e.Property(x => x.RouteType).HasConversion<string>().HasMaxLength(32).IsRequired();
e.HasOne(x => x.RequestType).WithMany(x => x.Routes).HasForeignKey(x => x.RequestTypeId);
});
modelBuilder.Entity<RouteStep>(e =>
{
e.HasKey(x => x.Id);
e.Property(x => x.ExecutorEmail).HasMaxLength(256).IsRequired();
e.Property(x => x.Position).HasMaxLength(128).IsRequired();
e.Property(x => x.ProjectRole).HasConversion<string>().HasMaxLength(32).IsRequired();
e.Property(x => x.Status).HasConversion<string>().HasMaxLength(32).IsRequired();
e.HasOne(x => x.Route).WithMany(x => x.Steps).HasForeignKey(x => x.RouteId);
e.HasIndex(x => new { x.RouteId, x.OrderNo });
});
}
}
@@ -0,0 +1,10 @@
using Extensions.Hosting.AsyncInitialization;
using Microsoft.EntityFrameworkCore;
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence;
public sealed class DatabaseMigrationInitializer(ApprovalRouteDbContext db) : IAsyncInitializer
{
public Task InitializeAsync(CancellationToken cancellationToken) =>
db.Database.MigrateAsync(cancellationToken);
}
@@ -0,0 +1,183 @@
// <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 Tnsu.ApprovalRoute.Infrastructure.Persistence;
#nullable disable
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
{
[DbContext(typeof(ApprovalRouteDbContext))]
[Migration("20260722130455_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RequestType", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasColumnName("name");
b.Property<string>("PageUrl")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("character varying(512)")
.HasColumnName("page_url");
b.HasKey("Id")
.HasName("pk_request_types");
b.ToTable("request_types", (string)null);
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.Route", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<bool>("ByProject")
.HasColumnType("boolean")
.HasColumnName("by_project");
b.Property<Guid>("CompanyId")
.HasColumnType("uuid")
.HasColumnName("company_id");
b.Property<Guid>("DepartmentId")
.HasColumnType("uuid")
.HasColumnName("department_id");
b.Property<Guid>("RequestTypeId")
.HasColumnType("uuid")
.HasColumnName("request_type_id");
b.Property<bool>("RequiresControl")
.HasColumnType("boolean")
.HasColumnName("requires_control");
b.Property<string>("RouteType")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("route_type");
b.HasKey("Id")
.HasName("pk_routes");
b.HasIndex("RequestTypeId")
.HasDatabaseName("ix_routes_request_type_id");
b.ToTable("routes", (string)null);
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RouteStep", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<bool>("AllowReassignToOtherApprovers")
.HasColumnType("boolean")
.HasColumnName("allow_reassign_to_other_approvers");
b.Property<string>("ExecutorEmail")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasColumnName("executor_email");
b.Property<int>("OrderNo")
.HasColumnType("integer")
.HasColumnName("order_no");
b.Property<string>("Position")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasColumnName("position");
b.Property<string>("ProjectRole")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("project_role");
b.Property<Guid>("RouteId")
.HasColumnType("uuid")
.HasColumnName("route_id");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("status");
b.HasKey("Id")
.HasName("pk_route_steps");
b.HasIndex("RouteId", "OrderNo")
.HasDatabaseName("ix_route_steps_route_id_order_no");
b.ToTable("route_steps", (string)null);
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.Route", b =>
{
b.HasOne("Tnsu.ApprovalRoute.Domain.Entities.RequestType", "RequestType")
.WithMany("Routes")
.HasForeignKey("RequestTypeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_routes_request_types_request_type_id");
b.Navigation("RequestType");
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RouteStep", b =>
{
b.HasOne("Tnsu.ApprovalRoute.Domain.Entities.Route", "Route")
.WithMany("Steps")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_route_steps_routes_route_id");
b.Navigation("Route");
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RequestType", b =>
{
b.Navigation("Routes");
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.Route", b =>
{
b.Navigation("Steps");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,98 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "request_types",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false),
name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
page_url = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_request_types", x => x.id);
});
migrationBuilder.CreateTable(
name: "routes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false),
route_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
request_type_id = table.Column<Guid>(type: "uuid", nullable: false),
department_id = table.Column<Guid>(type: "uuid", nullable: false),
company_id = table.Column<Guid>(type: "uuid", nullable: false),
by_project = table.Column<bool>(type: "boolean", nullable: false),
requires_control = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_routes", x => x.id);
table.ForeignKey(
name: "fk_routes_request_types_request_type_id",
column: x => x.request_type_id,
principalTable: "request_types",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "route_steps",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false),
order_no = table.Column<int>(type: "integer", nullable: false),
executor_email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
position = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
project_role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
route_id = table.Column<Guid>(type: "uuid", nullable: false),
allow_reassign_to_other_approvers = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_route_steps", x => x.id);
table.ForeignKey(
name: "fk_route_steps_routes_route_id",
column: x => x.route_id,
principalTable: "routes",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_route_steps_route_id_order_no",
table: "route_steps",
columns: new[] { "route_id", "order_no" });
migrationBuilder.CreateIndex(
name: "ix_routes_request_type_id",
table: "routes",
column: "request_type_id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "route_steps");
migrationBuilder.DropTable(
name: "routes");
migrationBuilder.DropTable(
name: "request_types");
}
}
}
@@ -0,0 +1,180 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Tnsu.ApprovalRoute.Infrastructure.Persistence;
#nullable disable
namespace Tnsu.ApprovalRoute.Infrastructure.Persistence.Migrations
{
[DbContext(typeof(ApprovalRouteDbContext))]
partial class ApprovalRouteDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RequestType", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasColumnName("name");
b.Property<string>("PageUrl")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("character varying(512)")
.HasColumnName("page_url");
b.HasKey("Id")
.HasName("pk_request_types");
b.ToTable("request_types", (string)null);
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.Route", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<bool>("ByProject")
.HasColumnType("boolean")
.HasColumnName("by_project");
b.Property<Guid>("CompanyId")
.HasColumnType("uuid")
.HasColumnName("company_id");
b.Property<Guid>("DepartmentId")
Review

Guid?

Guid?
Review

поправил

поправил
.HasColumnType("uuid")
.HasColumnName("department_id");
b.Property<Guid>("RequestTypeId")
.HasColumnType("uuid")
.HasColumnName("request_type_id");
b.Property<bool>("RequiresControl")
.HasColumnType("boolean")
.HasColumnName("requires_control");
b.Property<string>("RouteType")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("route_type");
b.HasKey("Id")
.HasName("pk_routes");
b.HasIndex("RequestTypeId")
.HasDatabaseName("ix_routes_request_type_id");
b.ToTable("routes", (string)null);
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RouteStep", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<bool>("AllowReassignToOtherApprovers")
.HasColumnType("boolean")
.HasColumnName("allow_reassign_to_other_approvers");
b.Property<string>("ExecutorEmail")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasColumnName("executor_email");
b.Property<int>("OrderNo")
.HasColumnType("integer")
.HasColumnName("order_no");
b.Property<string>("Position")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasColumnName("position");
b.Property<string>("ProjectRole")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("project_role");
b.Property<Guid>("RouteId")
.HasColumnType("uuid")
.HasColumnName("route_id");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("status");
b.HasKey("Id")
.HasName("pk_route_steps");
b.HasIndex("RouteId", "OrderNo")
.HasDatabaseName("ix_route_steps_route_id_order_no");
b.ToTable("route_steps", (string)null);
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.Route", b =>
{
b.HasOne("Tnsu.ApprovalRoute.Domain.Entities.RequestType", "RequestType")
.WithMany("Routes")
.HasForeignKey("RequestTypeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_routes_request_types_request_type_id");
b.Navigation("RequestType");
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RouteStep", b =>
{
b.HasOne("Tnsu.ApprovalRoute.Domain.Entities.Route", "Route")
.WithMany("Steps")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_route_steps_routes_route_id");
b.Navigation("Route");
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.RequestType", b =>
{
b.Navigation("Routes");
});
modelBuilder.Entity("Tnsu.ApprovalRoute.Domain.Entities.Route", b =>
{
b.Navigation("Steps");
});
#pragma warning restore 612, 618
}
}
}
@@ -4,8 +4,14 @@
<ProjectReference Include="..\Tnsu.ApprovalRoute.Domain\Tnsu.ApprovalRoute.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="EFCore.NamingConventions" />
<PackageReference Include="Extensions.Hosting.AsyncInitialization" />
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
</ItemGroup>
</Project>