This commit is contained in:
2024-03-08 07:51:57 +01:00
commit e84a2f4f8d
191 changed files with 43915 additions and 0 deletions
@@ -0,0 +1,153 @@
using System.ComponentModel.DataAnnotations;
using Kemkas.Web.Db.Enums;
using Kemkas.Web.Db.Models;
using Kemkas.Web.ViewModels;
namespace Kemkas.Web.Services.FirstEdition.Character;
public interface ICharacter1EDtoToDbModelService
{
public V1Karakter Convert(Character1eDto dto);
public void Update(V1Karakter original, Character1eDto dto);
}
public class Character1EDtoToDbModelService : ICharacter1EDtoToDbModelService
{
public V1Karakter Convert(Character1eDto dto)
{
var karakter = new V1Karakter
{
Nev = dto.Name,
Nem = dto.Nem,
Kor = dto.Kor,
Jellem = JellemExtensions.Convert(dto.Jellem),
Isten = dto.Isten,
Faj = FajExtensions.Convert1E(dto.Faj),
Osztaly = OsztalyExtensions.Convert1E(dto.Osztaly),
Ero = dto.Tulajdonsagok.Ero,
Ugyesseg = dto.Tulajdonsagok.Ugy,
Egeszseg = dto.Tulajdonsagok.Egs,
Intelligencia = dto.Tulajdonsagok.Int,
Bolcsesseg = dto.Tulajdonsagok.Bol,
Karizma = dto.Tulajdonsagok.Kar,
Szint = dto.Szint,
Pajzs = dto.Felszereles.PajzsId,
Pancel = dto.Felszereles.PancelId
};
karakter.KarakterKepzettsegek = ConvertKepzettsegek(dto, karakter);
karakter.Felszereles = dto.Felszereles.FegyverIds.Select(x => new V1Felszereles
{
Karakter = karakter,
IsFegyver = true,
Name = x,
}).ToList();
karakter.Szintlepesek = ConvertSzintLepes(dto, karakter);
return karakter;
}
public void Update(V1Karakter original, Character1eDto dto)
{
original.Nev = dto.Name;
original.Nem = dto.Nem;
original.Kor = dto.Kor;
original.Jellem = JellemExtensions.Convert(dto.Jellem);
original.Isten = dto.Isten;
original.Faj = FajExtensions.Convert1E(dto.Faj);
original.Osztaly = OsztalyExtensions.Convert1E(dto.Osztaly);
original.Ero = dto.Tulajdonsagok.Ero;
original.Ugyesseg = dto.Tulajdonsagok.Ugy;
original.Egeszseg = dto.Tulajdonsagok.Egs;
original.Intelligencia = dto.Tulajdonsagok.Int;
original.Bolcsesseg = dto.Tulajdonsagok.Bol;
original.Karizma = dto.Tulajdonsagok.Kar;
original.Szint = dto.Szint;
original.Pajzs = dto.Felszereles.PajzsId;
original.Pancel = dto.Felszereles.PancelId;
original.KarakterKepzettsegek = ConvertKepzettsegek(dto, original);
original.Felszereles = dto.Felszereles.FegyverIds.Select(x => new V1Felszereles
{
Karakter = original,
IsFegyver = true,
Name = x,
}).ToList();
original.Szintlepesek = ConvertSzintLepes(dto, original);
}
// TODO: this is unnecessary duplication of business logic!!
private static List<V1Szintlepes> ConvertSzintLepes(Character1eDto dto, V1Karakter karakter)
{
if (dto.HpRolls.Count < dto.Szint - 2)
{
throw new ValidationException("not enough HP rolls for a level " + dto.Szint + " character!");
}
var expectedNumberOfFighterSpec = (int)Math.Ceiling(dto.Szint / 2.0);
if (dto.HarcosSpecializaciok.Count > 0 && dto.HarcosSpecializaciok.Count < expectedNumberOfFighterSpec)
{
throw new ValidationException("not enough weapon specialization for a level " + dto.Szint + " fighter!");
}
if (dto.KalozKritikus.Count > 0 && dto.KalozKritikus.Count < dto.Szint/3)
{
throw new ValidationException("not enough crit specialization for a level " + dto.Szint + " pirate!");
}
var level1 = new V1Szintlepes()
{
Karakter = karakter,
KarakterSzint = 1,
// for multiclass characters
// Osztaly = karakter.Osztaly,
// OsztalySzint = 1,
HpRoll = karakter.Osztaly switch // TODO: duplication of logic!!
{
Osztaly1E.Barbar => 12,
Osztaly1E.Amazon or Osztaly1E.Harcos or Osztaly1E.Kaloz or Osztaly1E.Ijasz => 10,
Osztaly1E.Pap => 8,
Osztaly1E.Tolvaj => 6,
Osztaly1E.Varazslo or Osztaly1E.Illuzionista => 4,
_ => throw new ArgumentOutOfRangeException(nameof(karakter.Osztaly), karakter.Osztaly,
"invalid osztaly!")
},
FegyverSpecializacio = karakter.Osztaly == Osztaly1E.Harcos ? dto.HarcosSpecializaciok[0] : null,
};
return Enumerable.Range(2, count: dto.Szint - 1).Select(szint =>
{
var harcosFegyverSpec = dto.HarcosSpecializaciok.Count > 0 && szint % 2 == 1
? dto.HarcosSpecializaciok[szint / 2 ]
: null;
var kalozFegyverKrit = dto.KalozKritikus.Count > 0 && szint % 3 == 0 ? dto.KalozKritikus[szint / 3 - 1] : null;
return new V1Szintlepes
{
Karakter = karakter,
KarakterSzint = (byte)szint,
HpRoll = dto.HpRolls[szint - 2],
FegyverSpecializacio = harcosFegyverSpec ?? kalozFegyverKrit,
TulajdonsagNoveles = szint % 4 == 0 ? TulajdonsagExtensions.Convert(dto.TulajdonsagNovelesek[szint / 4 - 1]) : null,
};
}).Prepend(level1).ToList();
}
private static List<V1KarakterKepzettseg> ConvertKepzettsegek(Character1eDto dto, V1Karakter karakter)
{
var kepzettsegek = dto.Kepzettsegek.Select(x => new V1KarakterKepzettseg
{
Karakter = karakter,
IsTolvajKepzettseg = false,
Kepzettseg = KepzettsegExtensions.Convert1E(x),
}).ToList();
if (dto.Tolvajkepzettsegek != null)
{
kepzettsegek.AddRange(dto.Tolvajkepzettsegek.Select(x => new V1KarakterKepzettseg
{
Karakter = karakter,
IsTolvajKepzettseg = true,
Kepzettseg = KepzettsegExtensions.Convert1E(x),
}));
}
return kepzettsegek;
}
}
@@ -0,0 +1,50 @@
using Kemkas.Web.Db.Enums;
using Kemkas.Web.Db.Models;
using Kemkas.Web.ViewModels;
namespace Kemkas.Web.Services.FirstEdition.Character;
public interface ICharacterDbModelToDto1EService
{
public Character1eDto Convert(V1Karakter entity);
}
public class CharacterDbModelToDto1EService : ICharacterDbModelToDto1EService
{
public Character1eDto Convert(V1Karakter entity)
{
return new Character1eDto
{
Name = entity.Nev,
Nem = entity.Nem,
Kor = entity.Kor,
Jellem = entity.Jellem.Convert(),
Isten = entity.Isten,
Faj = entity.Faj.Convert(),
Osztaly = entity.Osztaly.Convert(),
Tulajdonsagok = new KarakterTulajdonsagokDto
{
Ero = entity.Ero,
Ugy = entity.Ugyesseg,
Egs = entity.Egeszseg,
Int = entity.Intelligencia,
Bol = entity.Bolcsesseg,
Kar = entity.Karizma,
},
Kepzettsegek = entity.KarakterKepzettsegek.Where(x => !x.IsTolvajKepzettseg).Select(x => x.Kepzettseg.Convert()).ToList(),
Tolvajkepzettsegek = entity.KarakterKepzettsegek.Where(x => x.IsTolvajKepzettseg).Select(x => x.Kepzettseg.Convert()).ToList(),
Szint = entity.Szint,
HpRolls = entity.Szintlepesek.OrderBy(x => x.KarakterSzint).Skip(1).Select(x => x.HpRoll).ToList(),
TulajdonsagNovelesek = entity.Szintlepesek.OrderBy(x => x.KarakterSzint).Select(x => x.TulajdonsagNoveles).Where(x => x is not null).Cast<Tulajdonsag>().Select(x => x.Convert()).ToList(),
HarcosSpecializaciok = entity.Osztaly == Osztaly1E.Harcos ? entity.Szintlepesek.OrderBy(x => x.KarakterSzint).Select(x => x.FegyverSpecializacio).Where(x => x != null).Cast<string>().ToList() : [],
KalozKritikus = entity.Osztaly == Osztaly1E.Kaloz ? entity.Szintlepesek.OrderBy(x => x.KarakterSzint).Select(x => x.FegyverSpecializacio).Where(x => x != null).Cast<string>().ToList() : [],
Felszereles = new KarakterFelszerelesDto
{
PajzsId = entity.Pajzs,
PancelId = entity.Pancel,
FegyverIds = entity.Felszereles.Where(x => x.IsFegyver).Select(x => x.Name).ToList(),
},
IsPublic = entity.IsPublic,
};
}
}
@@ -0,0 +1,18 @@
using Kemkas.Web.Services.Identity;
using Kemkas.Web.Services.Shared;
namespace Kemkas.Web.Services.FirstEdition.Character;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCharacter1EServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddScoped<ICurrentUserService, CurrentUserService>();
serviceCollection.AddTransient<ICharacterValidationService, CharacterValidationService>();
serviceCollection.AddTransient<ICharacter1EDtoToDbModelService, Character1EDtoToDbModelService>();
serviceCollection.AddTransient<ICharacterDbModelToDto1EService, CharacterDbModelToDto1EService>();
serviceCollection.AddScoped<ICharacterPersistenceService, CharacterPersistenceService>();
return serviceCollection;
}
}
@@ -0,0 +1,17 @@
using Kemkas.Web.ViewModels;
namespace Kemkas.Web.Services.FirstEdition.Character;
public interface ICharacterValidationService
{
public string? Validate(Character1eDto dto);
}
public class CharacterValidationService : ICharacterValidationService
{
public string? Validate(Character1eDto dto)
{
// TODO: add validation!!
return null;
}
}
@@ -0,0 +1,41 @@
using Kemkas.Web.Db.Models;
using Microsoft.AspNetCore.Identity;
namespace Kemkas.Web.Services.Identity;
public interface ICurrentUserService
{
public Task<ApplicationUser?> GetCurrentUser();
public Task<ApplicationUser> GetCurrentUserOrThrow();
}
public class CurrentUserService(
IHttpContextAccessor httpContextAccessor,
UserManager<ApplicationUser> userManager
) : ICurrentUserService
{
private ApplicationUser? _user;
public async Task<ApplicationUser?> GetCurrentUser()
{
if (_user is not null)
{
return _user;
}
var userName = httpContextAccessor.HttpContext?.User.Identity?.Name;
if (userName is null)
{
return null;
}
_user = await userManager.FindByNameAsync(userName);
return _user;
}
public async Task<ApplicationUser> GetCurrentUserOrThrow()
{
return await GetCurrentUser() ?? throw new InvalidOperationException("no current user!");
}
}
@@ -0,0 +1,29 @@
using System.Net.Http.Headers;
using Kemkas.Web.Config;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
namespace Kemkas.Web.Services.Identity;
public class MailgunEmailSender(IOptions<MailgunOptions> sendGridOptions, HttpClient httpClient) : IEmailSender
{
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{
var domain = sendGridOptions.Value.DomainName;
var uri = new Uri($"https://api.eu.mailgun.net/v3/{domain}/messages");
var request = new HttpRequestMessage(HttpMethod.Post, uri);
var authHeaderValue = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($"api:{sendGridOptions.Value.ApiKey}"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
request.Content = new FormUrlEncodedContent(
[
new KeyValuePair<string,string>("from", $"no-reply@{domain}"),
new KeyValuePair<string,string>("to", email),
new KeyValuePair<string,string>("subject", subject),
new KeyValuePair<string,string>("html", htmlMessage),
]);
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
}
}
@@ -0,0 +1,113 @@
using Kemkas.Web.Db.Enums;
using Kemkas.Web.Db.Models;
using Kemkas.Web.ViewModels;
namespace Kemkas.Web.Services.SecondEdition.Character;
public interface ICharacter2EDtoToDbModelService
{
public V2Karakter Convert(Character2eDto dto);
public void Update(V2Karakter original, Character2eDto dto);
}
public class Character2EDtoToDbModelService : ICharacter2EDtoToDbModelService
{
public V2Karakter Convert(Character2eDto dto)
{
var karakter = new V2Karakter
{
Nev = dto.Nev,
Nem = dto.Nem,
Kor = dto.Kor,
Isten = dto.Isten,
Jellem = JellemExtensions.Convert(dto.Jellem),
Faj = FajExtensions.Convert2E(dto.Faj),
Ero = dto.Tulajdonsagok.Ero,
Ugyesseg = dto.Tulajdonsagok.Ugy,
Egeszseg = dto.Tulajdonsagok.Egs,
Intelligencia = dto.Tulajdonsagok.Int,
Bolcsesseg = dto.Tulajdonsagok.Bol,
Karizma = dto.Tulajdonsagok.Kar,
Szint = dto.Szint,
Pajzs = dto.Felszereles.PancelId,
Pancel = dto.Felszereles.PancelId
};
karakter.KarakterKepzettsegek = ConvertKarakterKepzettsegek(dto, karakter);
karakter.Felszereles = dto.Felszereles.FegyverIds.Select(x => new V2Felszereles
{
Karakter = karakter,
IsFegyver = true,
TargyId = x,
}).ToList();
karakter.Szintlepesek = dto.Szintlepesek.Select((x, i) => new V2Szintlepes
{
Karakter = karakter,
Osztaly = OsztalyExtensions.Convert2E(x.Osztaly),
KarakterSzint = (byte)(i + 1),
HpRoll = x.HProll,
FegyverSpecializacio = x.HarcosFegyver ?? x.KalozKritikus,
TulajdonsagNoveles = x.TulajdonsagNoveles == null ? null : TulajdonsagExtensions.Convert(x.TulajdonsagNoveles),
TolvajExtraKepzettseg = x.TolvajExtraKepzettseg == null ? null : KepzettsegExtensions.Convert2E(x.TolvajExtraKepzettseg),
}).ToList();
return karakter;
}
public void Update(V2Karakter original, Character2eDto dto)
{
original.Nev = dto.Nev;
original.Nem = dto.Nem;
original.Kor = dto.Kor;
original.Isten = dto.Isten;
original.Jellem = JellemExtensions.Convert(dto.Jellem);
original.Faj = FajExtensions.Convert2E(dto.Faj);
original.Ero = dto.Tulajdonsagok.Ero;
original.Ugyesseg = dto.Tulajdonsagok.Ugy;
original.Egeszseg = dto.Tulajdonsagok.Egs;
original.Intelligencia = dto.Tulajdonsagok.Int;
original.Bolcsesseg = dto.Tulajdonsagok.Bol;
original.Karizma = dto.Tulajdonsagok.Kar;
original.Szint = dto.Szint;
original.Pajzs = dto.Felszereles.PajzsId;
original.Pancel = dto.Felszereles.PancelId;
original.KarakterKepzettsegek = ConvertKarakterKepzettsegek(dto, original);
original.Felszereles = dto.Felszereles.FegyverIds.Select(x => new V2Felszereles
{
Karakter = original,
IsFegyver = true,
TargyId = x,
}).ToList();
original.Szintlepesek = dto.Szintlepesek.Select((x, i) => new V2Szintlepes
{
Karakter = original,
Osztaly = OsztalyExtensions.Convert2E(x.Osztaly),
KarakterSzint = (byte)(i + 1),
HpRoll = x.HProll,
FegyverSpecializacio = x.HarcosFegyver ?? x.KalozKritikus,
TulajdonsagNoveles = x.TulajdonsagNoveles == null ? null : TulajdonsagExtensions.Convert(x.TulajdonsagNoveles),
TolvajExtraKepzettseg = x.TolvajExtraKepzettseg == null ? null : KepzettsegExtensions.Convert2E(x.TolvajExtraKepzettseg),
}).ToList();
}
private static List<V2KarakterKepzettseg> ConvertKarakterKepzettsegek(Character2eDto dto, V2Karakter karakter)
{
var kepzettsegek = dto.Kepzettsegek.Select(x => new V2KarakterKepzettseg
{
Karakter = karakter,
IsTolvajKepzettseg = false,
Kepzettseg = KepzettsegExtensions.Convert2E(x),
}).ToList();
if (dto.Tolvajkepzettsegek != null)
{
kepzettsegek.AddRange(dto.Tolvajkepzettsegek.Select(x => new V2KarakterKepzettseg
{
Karakter = karakter,
IsTolvajKepzettseg = true,
Kepzettseg = KepzettsegExtensions.Convert2E(x),
}));
}
return kepzettsegek;
}
}
@@ -0,0 +1,54 @@
using Kemkas.Web.Db.Enums;
using Kemkas.Web.Db.Models;
using Kemkas.Web.ViewModels;
namespace Kemkas.Web.Services.SecondEdition.Character;
public interface ICharacterDbModelToDto2EService
{
public Character2eDto Convert(V2Karakter entity);
}
public class CharacterDbModelToDto2EService : ICharacterDbModelToDto2EService
{
public Character2eDto Convert(V2Karakter entity)
{
return new Character2eDto
{
Nev = entity.Nev,
Nem = entity.Nem,
Kor = entity.Kor,
Jellem = entity.Jellem.Convert(),
Isten = entity.Isten,
Faj = entity.Faj.Convert(),
Tulajdonsagok = new KarakterTulajdonsagokDto
{
Ero = entity.Ero,
Ugy = entity.Ugyesseg,
Egs = entity.Egeszseg,
Int = entity.Intelligencia,
Bol = entity.Bolcsesseg,
Kar = entity.Karizma,
},
Kepzettsegek = entity.KarakterKepzettsegek.Where(x => !x.IsTolvajKepzettseg).Select(x => x.Kepzettseg.Convert()).ToList(),
Tolvajkepzettsegek = entity.KarakterKepzettsegek.Where(x => x.IsTolvajKepzettseg).Select(x => x.Kepzettseg.Convert()).ToList(),
Szint = entity.Szint,
Felszereles = new KarakterFelszerelesDto
{
PajzsId = entity.Pajzs,
PancelId = entity.Pancel,
FegyverIds = entity.Felszereles.Where(x => x.IsFegyver).Select(x => x.TargyId).ToList(),
},
IsPublic = entity.IsPublic,
Szintlepesek = entity.Szintlepesek.OrderBy(x => x.KarakterSzint).Select(sz => new Szintlepes
{
Osztaly = sz.Osztaly.Convert(),
KalozKritikus = sz.Osztaly == Osztaly2E.Tengeresz ? sz.FegyverSpecializacio : null,
HarcosFegyver = sz.Osztaly == Osztaly2E.Harcos ? sz.FegyverSpecializacio : null,
TulajdonsagNoveles = sz.TulajdonsagNoveles?.Convert(),
HProll = sz.HpRoll,
TolvajExtraKepzettseg = sz.TolvajExtraKepzettseg?.Convert()
}).ToList()
};
}
}
@@ -0,0 +1,11 @@
namespace Kemkas.Web.Services.SecondEdition.Character;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCharacter2EServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<ICharacter2EDtoToDbModelService, Character2EDtoToDbModelService>();
serviceCollection.AddTransient<ICharacterDbModelToDto2EService, CharacterDbModelToDto2EService>();
return serviceCollection;
}
}
@@ -0,0 +1,147 @@
using Kemkas.Web.Db;
using Kemkas.Web.Db.Enums;
using Kemkas.Web.Db.Models;
using Kemkas.Web.Services.Identity;
using Kemkas.Web.ViewModels;
using Microsoft.EntityFrameworkCore;
namespace Kemkas.Web.Services.Shared;
public interface ICharacterPersistenceService
{
public Task<Guid> StoreNewCharacter1E(V1Karakter karakter, bool isPublic = false);
public Task<Guid> StoreNewCharacter2E(V2Karakter karakter, bool isPublic = false);
public Task<V1Karakter?> GetCharacter1EById(Guid id, bool tracking = false);
public Task<V2Karakter?> GetCharacter2EById(Guid id, bool tracking = false);
public Task<List<CharacterListItemDto>> GetAllCharactersOfUser();
public Task UpdateCharacter1E(V1Karakter karakter, bool isPublic = false);
public Task UpdateCharacter2E(V2Karakter karakter, bool isPublic = false);
}
public class CharacterPersistenceService(
ApplicationDbContext dbContext,
ICurrentUserService currentUserService
) : ICharacterPersistenceService
{
public async Task<Guid> StoreNewCharacter1E(V1Karakter karakter, bool isPublic = false)
{
var currentUser = await currentUserService.GetCurrentUser();
karakter.OwnerUserId = currentUser?.Id;
karakter.Id = Guid.Empty;
karakter.IsPublic = isPublic;
dbContext.Karakterek.Add(karakter);
await dbContext.SaveChangesAsync();
return karakter.Id;
}
public async Task<Guid> StoreNewCharacter2E(V2Karakter karakter, bool isPublic = false)
{
var currentUser = await currentUserService.GetCurrentUser();
karakter.OwnerUserId = currentUser?.Id;
karakter.Id = Guid.Empty;
karakter.IsPublic = isPublic;
dbContext.Karakterek2E.Add(karakter);
await dbContext.SaveChangesAsync();
return karakter.Id;
}
public async Task<V1Karakter?> GetCharacter1EById(Guid id, bool tracking = false)
{
IQueryable<V1Karakter> queryable = dbContext.Karakterek.Include(x => x.KarakterKepzettsegek)
.Include(x => x.Szintlepesek)
.Include(x => x.Felszereles);
queryable = tracking ? queryable.AsTracking() : queryable.AsNoTracking();
var entity = await queryable
.FirstOrDefaultAsync(x => x.Id == id);
if (entity?.OwnerUserId is null || entity.IsPublic)
{
return entity;
}
var currentUser = await currentUserService.GetCurrentUser();
return entity.OwnerUserId == currentUser?.Id ? entity : null;
}
public async Task<V2Karakter?> GetCharacter2EById(Guid id, bool tracking = false)
{
IQueryable<V2Karakter> queryable = dbContext.Karakterek2E.Include(x => x.KarakterKepzettsegek)
.Include(x => x.Szintlepesek)
.Include(x => x.Felszereles);
queryable = tracking ? queryable.AsTracking() : queryable.AsNoTracking();
var entity = await queryable
.FirstOrDefaultAsync(x => x.Id == id);
if (entity?.OwnerUserId is null || entity.IsPublic)
{
return entity;
}
var currentUser = await currentUserService.GetCurrentUser();
return entity.OwnerUserId == currentUser?.Id ? entity : null;
}
public async Task<List<CharacterListItemDto>> GetAllCharactersOfUser()
{
var currentUser = await currentUserService.GetCurrentUser();
if (currentUser is null)
{
return [];
}
var characters = await dbContext.Karakterek
.Where(k => k.OwnerUserId == currentUser.Id)
.Select(k => new CharacterListItemDto
{
Id = k.Id,
Name = k.Nev,
Szint = k.Szint,
Faj = k.Faj.Convert(),
Osztaly = k.Osztaly.Convert(),
Edition = "1e"
})
.ToListAsync();
var characters2E = await dbContext.Karakterek2E.Include(k => k.Szintlepesek)
.Where(k => k.OwnerUserId == currentUser.Id)
.Select(k => new CharacterListItemDto
{
Id = k.Id,
Name = k.Nev,
Szint = k.Szint,
Faj = k.Faj.Convert(),
Osztaly = k.Szintlepesek.OrderBy(x => x.KarakterSzint).Select(x => x.Osztaly).First().Convert(),
Edition = "2e"
})
.ToListAsync();
characters.AddRange(characters2E);
return characters;
}
public async Task UpdateCharacter1E(V1Karakter karakter, bool isPublic = false)
{
karakter.IsPublic = isPublic;
dbContext.Karakterek.Attach(karakter);
await dbContext.SaveChangesAsync();
}
public async Task UpdateCharacter2E(V2Karakter karakter, bool isPublic = false)
{
karakter.IsPublic = isPublic;
dbContext.Karakterek2E.Attach(karakter);
await dbContext.SaveChangesAsync();
}
}