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/*
database.db
database.db-shm
database.db-wal

View file

@ -9,12 +9,15 @@
</PropertyGroup>
<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.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.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>
<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 AfRApay.Web.Backend;
using LinqToDB.Data;
using AfRApay.Web.Backend.Database;
using Microsoft.EntityFrameworkCore;
using Swashbuckle.AspNetCore.Filters;
DataConnection.DefaultSettings = new Database.Settings();
Migrations.RunMigrations();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
@ -17,6 +14,7 @@ builder.Services.AddSwaggerGen(options => {
builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly());
#if (DEBUG)
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllers().AddRazorRuntimeCompilation();
builder.Services.AddControllers();
#else
@ -40,4 +38,6 @@ app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
new DatabaseContext().Database.Migrate();
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.Tables;
using AfRApay.Web.Backend.Database;
using AfRApay.Web.Backend.Database.Tables;
using AfRApay.Web.Controllers.Schema;
using LinqToDB;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Swashbuckle.AspNetCore.Filters;
namespace AfRApay.Web.Controllers;
@ -25,8 +25,8 @@ public class CardController : Controller {
[SwaggerResponseExample(StatusCodes.Status404NotFound, typeof(ErrorNoActiveLinkProcessExample))]
[Route("/api/card/{card}/link")]
public async Task<IActionResult> Link(string card) {
var db = new Database.DbConn();
if (db.Cards.Any(p => p.CardId == card)) {
var db = new DatabaseContext();
if (db.Cards.Any(p => p.Id == card)) {
return StatusCode(StatusCodes.Status304NotModified);
}
@ -48,8 +48,8 @@ public class CardController : Controller {
var user = db.Users.First(p => p.Id == int.Parse(linkFlag.Value));
linkFlag.Value = "";
await db.InsertAsync(new Card { CardId = card, UserId = user.Id });
await db.UpdateAsync(linkFlag);
db.Add(new Card { Id = card, User = user });
await db.SaveChangesAsync();
return Ok(new UserResponse(user));
}
@ -72,10 +72,9 @@ public class CardController : Controller {
[SwaggerResponseExample(412, typeof(ErrorBalanceOutOfRangeExample))]
[Route("/api/card/{card}/transaction/{ik}")]
public async Task<IActionResult> Transaction(string card, string ik, [FromQuery] int amount) {
var db = new Database.DbConn();
if (db.Cards.Any(p => p.CardId == card)) {
var userId = db.Cards.First(p => p.CardId == card).UserId;
var user = db.Users.First(p => p.Id == userId);
var db = new DatabaseContext();
if (db.Cards.Any(p => p.Id == card)) {
var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User;
if (ik == "" || ik != user.LastIdempotencyKey) {
user.LastIdempotencyKey = ik;
@ -87,7 +86,7 @@ public class CardController : Controller {
user.Balance += amount;
}
await db.UpdateAsync(user);
await db.SaveChangesAsync();
return Ok(new UserResponse(user));
}
@ -108,10 +107,9 @@ public class CardController : Controller {
[SwaggerResponseExample(404, typeof(ErrorUnknownCardExample))]
[Route("/api/card/{card}/balance")]
public IActionResult Balance(string card) {
var db = new Database.DbConn();
if (db.Cards.Any(p => p.CardId == card)) {
var userId = db.Cards.First(p => p.CardId == card).UserId;
var user = db.Users.First(p => p.Id == userId);
var db = new DatabaseContext();
if (db.Cards.Any(p => p.Id == card)) {
var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User;
return Ok(new UserResponse(user));
}
@ -120,7 +118,7 @@ public class CardController : Controller {
}
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> {

View file

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

View file

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

View file

@ -1,8 +1,6 @@
using System.Data;
using System.Net;
using AfRApay.Web.Backend;
using AfRApay.Web.Backend.Tables;
using LinqToDB;
using System.Net;
using AfRApay.Web.Backend.Database;
using AfRApay.Web.Backend.Database.Tables;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AfRApay.Web.Pages;
@ -10,17 +8,18 @@ namespace AfRApay.Web.Pages;
public class AddUserModel : PageModel {
public void OnGet() { }
public void OnPost() {
using var db = new Database.DbConn();
public async void OnPost() {
await using var db = new DatabaseContext();
if (Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(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."));
return;
}
var user = new User { Nickname = nick!, Balance = 0 };
db.InsertWithIdentity(user);
var user = new User { Nickname = nick.ToString(), Balance = 0 };
db.Add(user);
await db.SaveChangesAsync();
Response.Redirect($"/#{user.Nickname}");
return;
}

View file

@ -1,15 +1,16 @@
@page "{id:int}"
@using AfRApay.Web.Backend
@using AfRApay.Web.Backend.Database
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Microsoft.EntityFrameworkCore
@model EditUserModel
@{
ViewData["Title"] = "Edit User";
if (Request.Method == "POST" && Request.Form["action"] == "delete") {
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 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 lTimeFlag = db.Config.FirstOrDefault(p => p.Name == "lTime");
@ -142,8 +143,8 @@
<div class="row justify-content-center">
@{
foreach (var card in cards) {
await RenderCard(user.Nickname, card.CardId);
await RenderCardModal(user.Nickname, card.CardId);
await RenderCard(user.Nickname, card.Id);
await RenderCardModal(user.Nickname, card.Id);
}
await RenderCard(user.Nickname, type: EditUserModel.CardType.LinkPlaceholder);
}

View file

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

View file

@ -1,9 +1,9 @@
@page
@using AfRApay.Web.Backend
@using AfRApay.Web.Backend.Database
@model IndexModel
@{
ViewData["Title"] = "Index";
var db = new Database.DbConn();
var db = new DatabaseContext();
}
@section Header {
@ -62,7 +62,7 @@
@($"{user.Balance / 100M:C}")
</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>
<!-- Displayed when in big layout -->

View file

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