Migrate to EF.Core

This commit is contained in:
Laura Hausmann 2023-02-13 18:17:38 +01:00
parent d819bee048
commit 5f75f1ec71
Signed by: zotan
GPG key ID: D044E84C5BE01605
25 changed files with 405 additions and 243 deletions

2
.gitignore vendored
View file

@ -187,3 +187,5 @@ Temporary Items
.vscode/* .vscode/*
database.db database.db
database.db-shm
database.db-wal

View file

@ -9,12 +9,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="linq2db" Version="4.4.1" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.2" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.6" /> <PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.6" />
<PackageReference Include="System.Data.SQLite" Version="1.0.115" />
<PackageReference Include="System.Data.SQLite.Core.osx.arm64" Version="1.0.117" />
</ItemGroup> </ItemGroup>
<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation"> <Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">

View file

@ -1,34 +0,0 @@
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using LinqToDB.Configuration;
using LinqToDB.Data;
namespace AfRApay.Web.Backend;
public class Database {
private class ConnectionStringSettings : IConnectionStringSettings {
public string ConnectionString { get; set; } = null!;
public string Name { get; set; } = null!;
public string ProviderName { get; set; } = null!;
public bool IsGlobal => false;
}
public class Settings : ILinqToDBSettings {
public IEnumerable<IDataProviderSettings> DataProviders => Enumerable.Empty<IDataProviderSettings>();
public string DefaultConfiguration => "SQLite";
public string DefaultDataProvider => "SQLite";
public IEnumerable<IConnectionStringSettings> ConnectionStrings {
get { yield return new ConnectionStringSettings { Name = "db", ProviderName = "SQLite", ConnectionString = @"Data Source=database.db;" }; }
}
}
public class DbConn : DataConnection {
public DbConn() : base("db") { }
public ITable<DbInfo> DbInfo => this.GetTable<DbInfo>();
public ITable<User> Users => this.GetTable<User>();
public ITable<Card> Cards => this.GetTable<Card>();
public ITable<Config> Config => this.GetTable<Config>();
}
}

View file

@ -0,0 +1,21 @@
using AfRApay.Web.Backend.Database.Tables;
using Microsoft.EntityFrameworkCore;
namespace AfRApay.Web.Backend.Database;
public partial class DatabaseContext : DbContext {
public DatabaseContext() { }
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
public virtual DbSet<Card> Cards { get; set; } = null!;
public virtual DbSet<User> Users { get; set; } = null!;
public virtual DbSet<Config> Config { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlite("Data Source=database.db");
protected override void OnModelCreating(ModelBuilder modelBuilder) {
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

View file

@ -0,0 +1,95 @@
// <auto-generated />
using AfRApay.Web.Backend.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace AfRApay.Web.Backend.Database.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20230213181437_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b =>
{
b.Property<string>("Id")
.HasColumnType("NVarChar(255)")
.HasColumnName("CardId");
b.Property<long>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Cards");
});
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Config", b =>
{
b.Property<string>("Name")
.HasColumnType("NVarChar(255)")
.HasColumnName("Name");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("NVarChar(255)")
.HasColumnName("Value");
b.HasKey("Name");
b.ToTable("Config");
});
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("ID");
b.Property<long>("Balance")
.HasColumnType("INTEGER")
.HasColumnName("Balance");
b.Property<string>("LastIdempotencyKey")
.HasColumnType("NVarChar(255)")
.HasColumnName("LastIdempotencyKey");
b.Property<string>("Nickname")
.IsRequired()
.HasColumnType("NVarChar(255)")
.HasColumnName("Nickname");
b.HasKey("Id");
b.HasIndex("Nickname")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b =>
{
b.HasOne("AfRApay.Web.Backend.Database.Tables.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,83 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AfRApay.Web.Backend.Database.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Config",
columns: table => new
{
Name = table.Column<string>(type: "NVarChar(255)", nullable: false),
Value = table.Column<string>(type: "NVarChar(255)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Config", x => x.Name);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
ID = table.Column<long>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Balance = table.Column<long>(type: "INTEGER", nullable: false),
Nickname = table.Column<string>(type: "NVarChar(255)", nullable: false),
LastIdempotencyKey = table.Column<string>(type: "NVarChar(255)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.ID);
});
migrationBuilder.CreateTable(
name: "Cards",
columns: table => new
{
CardId = table.Column<string>(type: "NVarChar(255)", nullable: false),
UserId = table.Column<long>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Cards", x => x.CardId);
table.ForeignKey(
name: "FK_Cards_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Cards_UserId",
table: "Cards",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_Users_Nickname",
table: "Users",
column: "Nickname",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Cards");
migrationBuilder.DropTable(
name: "Config");
migrationBuilder.DropTable(
name: "Users");
}
}
}

View file

@ -0,0 +1,92 @@
// <auto-generated />
using AfRApay.Web.Backend.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace AfRApay.Web.Backend.Database.Migrations
{
[DbContext(typeof(DatabaseContext))]
partial class DatabaseContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b =>
{
b.Property<string>("Id")
.HasColumnType("NVarChar(255)")
.HasColumnName("CardId");
b.Property<long>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Cards");
});
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Config", b =>
{
b.Property<string>("Name")
.HasColumnType("NVarChar(255)")
.HasColumnName("Name");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("NVarChar(255)")
.HasColumnName("Value");
b.HasKey("Name");
b.ToTable("Config");
});
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("ID");
b.Property<long>("Balance")
.HasColumnType("INTEGER")
.HasColumnName("Balance");
b.Property<string>("LastIdempotencyKey")
.HasColumnType("NVarChar(255)")
.HasColumnName("LastIdempotencyKey");
b.Property<string>("Nickname")
.IsRequired()
.HasColumnType("NVarChar(255)")
.HasColumnName("Nickname");
b.HasKey("Id");
b.HasIndex("Nickname")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b =>
{
b.HasOne("AfRApay.Web.Backend.Database.Tables.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace AfRApay.Web.Backend.Database.Tables;
[Table("Cards")]
public class Card {
[Column("CardId", TypeName = "NVarChar(255)")] [Key] public string Id { get; set; } = null!;
[Column("UserId")] [Required] public User User { get; set; } = null!;
}

View file

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace AfRApay.Web.Backend.Database.Tables;
[Table("Config")]
public class Config {
[Column("Name", TypeName = "NVarChar(255)")] [Key] public string Name { get; set; } = null!;
[Column("Value", TypeName = "NVarChar(255)")] [Required] public string Value { get; set; } = null!;
}

View file

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace AfRApay.Web.Backend.Database.Tables;
[Table("Users")]
[Index(nameof(Nickname), IsUnique = true)]
public class User {
[Column("ID")] [Key] public long Id { get; set; }
[Column("Balance")] [Required] public long Balance { get; set; }
[Column("Nickname", TypeName = "NVarChar(255)")] [Required] public string Nickname { get; set; } = null!;
[Column("LastIdempotencyKey", TypeName = "NVarChar(255)")] public string? LastIdempotencyKey { get; set; }
}

View file

@ -1,91 +0,0 @@
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using LinqToDB.Data;
namespace AfRApay.Web.Backend;
public static class Migrations {
private const int DbVer = 1;
// ReSharper disable once CollectionNeverUpdated.Local
// ReSharper disable once InconsistentNaming
private static readonly List<Migration> _migrations = new();
public static void RunMigrations() {
using var db = new Database.DbConn();
var ccolor = Console.ForegroundColor;
if (!db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.Any()) {
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write("Running migration: ");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Initialize Database");
db.CreateTable<DbInfo>();
db.CreateTable<User>();
db.CreateTable<Card>();
db.CreateTable<Config>();
db.InsertWithIdentity(new DbInfo { DbVer = DbVer });
}
else if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) {
db.CreateTable<DbInfo>();
db.InsertWithIdentity(new DbInfo { DbVer = 0 });
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Database version: {db.DbInfo.ToList().First().DbVer}");
var migrationsToRun = _migrations.FindAll(p => p.IntroducedWithDbVer > db.DbInfo.First().DbVer);
if (migrationsToRun.Count == 0) {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("No migrations to run.");
}
else {
new Migration(0, "BEGIN TRANSACTION").Run(db);
try {
migrationsToRun.ForEach(p => p.Run(db));
}
catch {
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine($"Migrating to database version {DbVer} failed.");
new Migration(0, "ROLLBACK TRANSACTION").Run(db);
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Rolled back migrations.");
Environment.Exit(1);
}
new Migration(0, "COMMIT TRANSACTION").Run(db);
var newdb = new Database.DbConn();
var dbinfo = newdb.DbInfo.First();
dbinfo.DbVer = DbVer;
newdb.Update(dbinfo);
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine($"Database version is now: {DbVer}");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Finished running migrations.");
}
Console.ForegroundColor = ccolor;
}
private class Migration {
private readonly string _sql;
public readonly int IntroducedWithDbVer;
public Migration(int introducedWithDbVer, string sql) {
IntroducedWithDbVer = introducedWithDbVer;
_sql = sql;
}
public void Run(DataConnection db) {
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write("Running migration: ");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(_sql);
db.Execute(_sql);
}
}
}

View file

@ -1,11 +1,8 @@
using System.Reflection; using System.Reflection;
using AfRApay.Web.Backend; using AfRApay.Web.Backend.Database;
using LinqToDB.Data; using Microsoft.EntityFrameworkCore;
using Swashbuckle.AspNetCore.Filters; using Swashbuckle.AspNetCore.Filters;
DataConnection.DefaultSettings = new Database.Settings();
Migrations.RunMigrations();
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
@ -17,6 +14,7 @@ builder.Services.AddSwaggerGen(options => {
builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly()); builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly());
#if (DEBUG) #if (DEBUG)
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllers().AddRazorRuntimeCompilation(); builder.Services.AddControllers().AddRazorRuntimeCompilation();
builder.Services.AddControllers(); builder.Services.AddControllers();
#else #else
@ -40,4 +38,6 @@ app.UseAuthorization();
app.MapRazorPages(); app.MapRazorPages();
app.MapControllers(); app.MapControllers();
new DatabaseContext().Database.Migrate();
app.Run(); app.Run();

View file

@ -1,11 +0,0 @@
using LinqToDB.Mapping;
#pragma warning disable CS8618
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "Cards")]
public class Card {
[Column(Name = "CardId"), PrimaryKey, NotNull] public string CardId { get; set; }
[Column(Name = "UserId"), NotNull] public int UserId { get; set; }
}

View file

@ -1,10 +0,0 @@
using LinqToDB.Mapping;
#pragma warning disable CS8618
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "Config")]
public class Config {
[Column(Name = "Name"), PrimaryKey, NotNull] public string Name { get; set; }
[Column(Name = "Value"), NotNull] public string Value { get; set; }
}

View file

@ -1,9 +0,0 @@
using LinqToDB.Mapping;
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "DbInfo")]
public class DbInfo {
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
[Column(Name = "DbVer"), NotNull] public int DbVer { get; set; }
}

View file

@ -1,13 +0,0 @@
using LinqToDB.Mapping;
#pragma warning disable CS8618
namespace AfRApay.Web.Backend.Tables;
[Table(Name = "Users")]
public class User {
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
[Column(Name = "Nickname"), NotNull] public string Nickname { get; set; }
[Column(Name = "Balance")] public int Balance { get; set; }
[Column(Name = "LastIdempotencyKey")] public string? LastIdempotencyKey { get; set; }
}

View file

@ -1,8 +1,8 @@
using AfRApay.Web.Backend; using AfRApay.Web.Backend.Database;
using AfRApay.Web.Backend.Tables; using AfRApay.Web.Backend.Database.Tables;
using AfRApay.Web.Controllers.Schema; using AfRApay.Web.Controllers.Schema;
using LinqToDB;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Swashbuckle.AspNetCore.Filters; using Swashbuckle.AspNetCore.Filters;
namespace AfRApay.Web.Controllers; namespace AfRApay.Web.Controllers;
@ -25,8 +25,8 @@ public class CardController : Controller {
[SwaggerResponseExample(StatusCodes.Status404NotFound, typeof(ErrorNoActiveLinkProcessExample))] [SwaggerResponseExample(StatusCodes.Status404NotFound, typeof(ErrorNoActiveLinkProcessExample))]
[Route("/api/card/{card}/link")] [Route("/api/card/{card}/link")]
public async Task<IActionResult> Link(string card) { public async Task<IActionResult> Link(string card) {
var db = new Database.DbConn(); var db = new DatabaseContext();
if (db.Cards.Any(p => p.CardId == card)) { if (db.Cards.Any(p => p.Id == card)) {
return StatusCode(StatusCodes.Status304NotModified); return StatusCode(StatusCodes.Status304NotModified);
} }
@ -48,8 +48,8 @@ public class CardController : Controller {
var user = db.Users.First(p => p.Id == int.Parse(linkFlag.Value)); var user = db.Users.First(p => p.Id == int.Parse(linkFlag.Value));
linkFlag.Value = ""; linkFlag.Value = "";
await db.InsertAsync(new Card { CardId = card, UserId = user.Id }); db.Add(new Card { Id = card, User = user });
await db.UpdateAsync(linkFlag); await db.SaveChangesAsync();
return Ok(new UserResponse(user)); return Ok(new UserResponse(user));
} }
@ -72,10 +72,9 @@ public class CardController : Controller {
[SwaggerResponseExample(412, typeof(ErrorBalanceOutOfRangeExample))] [SwaggerResponseExample(412, typeof(ErrorBalanceOutOfRangeExample))]
[Route("/api/card/{card}/transaction/{ik}")] [Route("/api/card/{card}/transaction/{ik}")]
public async Task<IActionResult> Transaction(string card, string ik, [FromQuery] int amount) { public async Task<IActionResult> Transaction(string card, string ik, [FromQuery] int amount) {
var db = new Database.DbConn(); var db = new DatabaseContext();
if (db.Cards.Any(p => p.CardId == card)) { if (db.Cards.Any(p => p.Id == card)) {
var userId = db.Cards.First(p => p.CardId == card).UserId; var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User;
var user = db.Users.First(p => p.Id == userId);
if (ik == "" || ik != user.LastIdempotencyKey) { if (ik == "" || ik != user.LastIdempotencyKey) {
user.LastIdempotencyKey = ik; user.LastIdempotencyKey = ik;
@ -87,7 +86,7 @@ public class CardController : Controller {
user.Balance += amount; user.Balance += amount;
} }
await db.UpdateAsync(user); await db.SaveChangesAsync();
return Ok(new UserResponse(user)); return Ok(new UserResponse(user));
} }
@ -108,10 +107,9 @@ public class CardController : Controller {
[SwaggerResponseExample(404, typeof(ErrorUnknownCardExample))] [SwaggerResponseExample(404, typeof(ErrorUnknownCardExample))]
[Route("/api/card/{card}/balance")] [Route("/api/card/{card}/balance")]
public IActionResult Balance(string card) { public IActionResult Balance(string card) {
var db = new Database.DbConn(); var db = new DatabaseContext();
if (db.Cards.Any(p => p.CardId == card)) { if (db.Cards.Any(p => p.Id == card)) {
var userId = db.Cards.First(p => p.CardId == card).UserId; var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User;
var user = db.Users.First(p => p.Id == userId);
return Ok(new UserResponse(user)); return Ok(new UserResponse(user));
} }
@ -120,7 +118,7 @@ public class CardController : Controller {
} }
private class UserUpdatedExample : IExamplesProvider<UserResponse> { private class UserUpdatedExample : IExamplesProvider<UserResponse> {
public UserResponse GetExamples() => new(new User { Id = 123, Nickname = "testman", Balance = 2550, LastIdempotencyKey = "5a6c94aa"}); public UserResponse GetExamples() => new(new User { Id = 123, Nickname = "testman", Balance = 2550, LastIdempotencyKey = "5a6c94aa" });
} }
private class ErrorUnknownCardExample : IExamplesProvider<ErrorResponse> { private class ErrorUnknownCardExample : IExamplesProvider<ErrorResponse> {

View file

@ -1,5 +1,4 @@
using System.ComponentModel; using System.ComponentModel;
using AfRApay.Web.Backend.Tables;
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
namespace AfRApay.Web.Controllers.Schema; namespace AfRApay.Web.Controllers.Schema;

View file

@ -1,5 +1,5 @@
using System.ComponentModel; using System.ComponentModel;
using AfRApay.Web.Backend.Tables; using AfRApay.Web.Backend.Database.Tables;
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
namespace AfRApay.Web.Controllers.Schema; namespace AfRApay.Web.Controllers.Schema;

View file

@ -1,8 +1,6 @@
using System.Data; using System.Net;
using System.Net; using AfRApay.Web.Backend.Database;
using AfRApay.Web.Backend; using AfRApay.Web.Backend.Database.Tables;
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages; namespace AfRApay.Web.Pages;
@ -10,17 +8,18 @@ namespace AfRApay.Web.Pages;
public class AddUserModel : PageModel { public class AddUserModel : PageModel {
public void OnGet() { } public void OnGet() { }
public void OnPost() { public async void OnPost() {
using var db = new Database.DbConn(); await using var db = new DatabaseContext();
if (Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) { if (Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) {
var nick = Request.Form["nickname"]; var nick = Request.Form["nickname"];
if (db.Users.Any(p => p.Nickname == nick)) { if (db.Users.Any(p => p.Nickname == nick.ToString())) {
Response.Redirect("/ErrorRedirect?redir=/AddUser&message=" + WebUtility.UrlEncode("User with nick already exists.")); Response.Redirect("/ErrorRedirect?redir=/AddUser&message=" + WebUtility.UrlEncode("User with nick already exists."));
return; return;
} }
var user = new User { Nickname = nick!, Balance = 0 }; var user = new User { Nickname = nick.ToString(), Balance = 0 };
db.InsertWithIdentity(user); db.Add(user);
await db.SaveChangesAsync();
Response.Redirect($"/#{user.Nickname}"); Response.Redirect($"/#{user.Nickname}");
return; return;
} }

View file

@ -1,15 +1,16 @@
@page "{id:int}" @page "{id:int}"
@using AfRApay.Web.Backend @using AfRApay.Web.Backend.Database
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@using Microsoft.EntityFrameworkCore
@model EditUserModel @model EditUserModel
@{ @{
ViewData["Title"] = "Edit User"; ViewData["Title"] = "Edit User";
if (Request.Method == "POST" && Request.Form["action"] == "delete") { if (Request.Method == "POST" && Request.Form["action"] == "delete") {
return; return;
} }
var db = new Database.DbConn(); var db = new DatabaseContext();
var user = db.Users.First(p => p.Id == int.Parse(RouteData.Values["id"]!.ToString()!)); var user = db.Users.First(p => p.Id == int.Parse(RouteData.Values["id"]!.ToString()!));
var cards = db.Cards.Where(p => p.UserId == user.Id); var cards = db.Cards.Include(p => p.User).Where(p => p.User == user);
var linkFlag = db.Config.FirstOrDefault(p => p.Name == "link"); var linkFlag = db.Config.FirstOrDefault(p => p.Name == "link");
var lTimeFlag = db.Config.FirstOrDefault(p => p.Name == "lTime"); var lTimeFlag = db.Config.FirstOrDefault(p => p.Name == "lTime");
@ -142,8 +143,8 @@
<div class="row justify-content-center"> <div class="row justify-content-center">
@{ @{
foreach (var card in cards) { foreach (var card in cards) {
await RenderCard(user.Nickname, card.CardId); await RenderCard(user.Nickname, card.Id);
await RenderCardModal(user.Nickname, card.CardId); await RenderCardModal(user.Nickname, card.Id);
} }
await RenderCard(user.Nickname, type: EditUserModel.CardType.LinkPlaceholder); await RenderCard(user.Nickname, type: EditUserModel.CardType.LinkPlaceholder);
} }

View file

@ -1,8 +1,6 @@
using System.Data; using System.Net;
using System.Net; using AfRApay.Web.Backend.Database;
using AfRApay.Web.Backend; using AfRApay.Web.Backend.Database.Tables;
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages; namespace AfRApay.Web.Pages;
@ -10,21 +8,22 @@ namespace AfRApay.Web.Pages;
public class EditUserModel : PageModel { public class EditUserModel : PageModel {
public void OnGet() { } public void OnGet() { }
public void OnPost() { public async void OnPost() {
using var db = new Database.DbConn(); await using var db = new DatabaseContext();
var userId = int.Parse(RouteData.Values["id"]!.ToString()!); var userId = long.Parse(RouteData.Values["id"]!.ToString()!);
var user = db.Users.First(p => p.Id == userId); var user = db.Users.First(p => p.Id == userId);
if (Request.Form["action"] == "delete") { if (Request.Form["action"] == "delete") {
db.Cards.Delete(p => p.UserId == userId); db.Remove(user);
db.Delete(user); await db.SaveChangesAsync();
Response.Redirect("/"); Response.Redirect("/");
return; return;
} }
if (Request.Form["action"] == "deleteCard" && Request.Form.ContainsKey("cardId")) { if (Request.Form["action"] == "deleteCard" && Request.Form.ContainsKey("cardId")) {
var card = db.Cards.First(p => p.CardId == Request.Form["cardId"]); var card = db.Cards.First(p => p.Id == Request.Form["cardId"].ToString());
db.Delete(card); db.Remove(card);
await db.SaveChangesAsync();
Response.Redirect($"/EditUser/{userId}"); Response.Redirect($"/EditUser/{userId}");
return; return;
} }
@ -37,16 +36,18 @@ public class EditUserModel : PageModel {
if (lTimeFlag == null) { if (lTimeFlag == null) {
lTimeFlag = new Config { Name = "lTime", Value = DateTime.UtcNow.ToString("s") }; lTimeFlag = new Config { Name = "lTime", Value = DateTime.UtcNow.ToString("s") };
db.Insert(lTimeFlag); db.Add(lTimeFlag);
await db.SaveChangesAsync();
} }
else { else {
lTimeFlag.Value = DateTime.UtcNow.ToString("s"); lTimeFlag.Value = DateTime.UtcNow.ToString("s");
db.Update(lTimeFlag); await db.SaveChangesAsync();
} }
if (linkFlag == null) { if (linkFlag == null) {
linkFlag = new Config { Name = "link", Value = user.Id.ToString() }; linkFlag = new Config { Name = "link", Value = user.Id.ToString() };
db.Insert(linkFlag); db.Add(linkFlag);
await db.SaveChangesAsync();
return; return;
} }
@ -55,27 +56,27 @@ public class EditUserModel : PageModel {
} }
linkFlag.Value = user.Id.ToString(); linkFlag.Value = user.Id.ToString();
db.Update(linkFlag); await db.SaveChangesAsync();
return; return;
} }
if (Request.Form["action"] == "save" && Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) { if (Request.Form["action"] == "save" && Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) {
var nick = Request.Form["nickname"]; var nick = Request.Form["nickname"].ToString();
if (db.Users.Any(p => p.Nickname == nick && p.Id != userId)) { if (db.Users.Any(p => p.Nickname == nick && p.Id != userId)) {
Response.Redirect($"/ErrorRedirect?redir=/EditUser/{userId}&message=" + WebUtility.UrlEncode("User with nick already exists.")); Response.Redirect($"/ErrorRedirect?redir=/EditUser/{userId}&message=" + WebUtility.UrlEncode("User with nick already exists."));
return; return;
} }
user.Nickname = nick!; user.Nickname = nick;
db.Update(user); await db.SaveChangesAsync();
} }
Response.Redirect($"/#{user.Nickname}"); Response.Redirect($"/#{user.Nickname}");
} }
public enum CardType { public enum CardType {
Normal = 1, Normal = 1,
LinkPlaceholder = 2, LinkPlaceholder = 2,
DeletionConfirmation = 3 DeletionConfirmation = 3
} }
} }

View file

@ -1,9 +1,9 @@
@page @page
@using AfRApay.Web.Backend @using AfRApay.Web.Backend.Database
@model IndexModel @model IndexModel
@{ @{
ViewData["Title"] = "Index"; ViewData["Title"] = "Index";
var db = new Database.DbConn(); var db = new DatabaseContext();
} }
@section Header { @section Header {
@ -62,7 +62,7 @@
@($"{user.Balance / 100M:C}") @($"{user.Balance / 100M:C}")
</td> </td>
<td> <td>
<b>@db.Cards.Count(p => p.UserId == user.Id)</b> cards linked. <b>@db.Cards.Count(p => p.User.Id == user.Id)</b> cards linked.
</td> </td>
<td> <td>
<!-- Displayed when in big layout --> <!-- Displayed when in big layout -->

View file

@ -1,7 +1,5 @@
using System.Data; using System.Net;
using System.Net; using AfRApay.Web.Backend.Database;
using AfRApay.Web.Backend;
using LinqToDB;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages; namespace AfRApay.Web.Pages;
@ -9,8 +7,8 @@ namespace AfRApay.Web.Pages;
public class IndexModel : PageModel { public class IndexModel : PageModel {
public void OnGet() { } public void OnGet() { }
public void OnPost() { public async void OnPost() {
using var db = new Database.DbConn(); await using var db = new DatabaseContext();
if (Request.Form["action"] == "transaction" && Request.Form.ContainsKey("userId") && Request.Form.ContainsKey("amount")) { if (Request.Form["action"] == "transaction" && Request.Form.ContainsKey("userId") && Request.Form.ContainsKey("amount")) {
var userId = int.Parse(Request.Form["userId"].ToString()); var userId = int.Parse(Request.Form["userId"].ToString());
var amount = int.Parse(Request.Form["amount"].ToString()); var amount = int.Parse(Request.Form["amount"].ToString());
@ -25,7 +23,7 @@ public class IndexModel : PageModel {
} }
user.Balance += amount; user.Balance += amount;
db.Update(user); await db.SaveChangesAsync();
Response.Redirect($"/#{user.Nickname}"); Response.Redirect($"/#{user.Nickname}");
} }

View file

@ -4,6 +4,10 @@ This is the software that runs on the MateCard terminal, powered by an ESP32.
## Setting up dev environment ## Setting up dev environment
This project uses PlatformIO. Quick setup guide: This project uses PlatformIO. Quick setup guide:
- Install dotnet-sdk (macOS: `brew install dotnet-sdk` / Arch Linux: `pacman -S dotnet-host dotnet-runtime dotnet-sdk dotnet-targeting-pack aspnet-runtime aspnet-targeting-pack`) - Install dotnet-sdk (macOS: `brew install dotnet-sdk` / Arch Linux: `pacman -S dotnet-host dotnet-runtime dotnet-sdk dotnet-targeting-pack aspnet-runtime aspnet-targeting-pack`)
- To start the webserver, execute `dotnet run` in this directory. - Install the EF.Core tool for database management: `dotnet tool install --global dotnet-ef`
- To start the webserver, execute `dotnet run` in this directory. If the database does not exist, it will be created. Migrations are applied automatically.
- Swagger / OpenAPI docs will automatically generate at `/swagger`. - Swagger / OpenAPI docs will automatically generate at `/swagger`.
- To test with the MateCard ESP32 code, get your computer's IP and configure the ESP to use `http://<yourip>:5296` as the API base url. - To test with the MateCard ESP32 code, get your computer's IP and configure the ESP to use `http://<yourip>:5296` as the API base url.
## Creating migrations
- To create a migration, change the respective code in `Backend/Database/Tables/TableName.cs` and then run `dotnet ef migrations add DescriptiveNameOfChanges`. The migration will be generated automatically.