Files
AdamuSw/src/Persistence/EntityFramework/PlayerContext.cs
sven-n 67cc57cf31 Merge pull request #840 from nolt/fix-admin-panel-account-search
Search accounts in the database instead of loading all of them

(cherry picked from commit 4e806cbe64f855a765e864c0c2ff45238f487e36)
2026-07-23 11:38:09 +03:00

136 lines
5.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// <copyright file="PlayerContext.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Persistence.EntityFramework;
using System.Threading;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using MUnique.OpenMU.Persistence.EntityFramework.Model;
/// <summary>
/// Persistence context which is used by in-game players.
/// </summary>
internal class PlayerContext : CachingEntityFrameworkContext, IPlayerContext
{
/// <summary>
/// Initializes a new instance of the <see cref="PlayerContext" /> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="repositoryProvider">The repository provider.</param>
/// <param name="logger">The logger.</param>
public PlayerContext(DbContext context, IContextAwareRepositoryProvider repositoryProvider, ILogger<PlayerContext> logger)
: base(context, repositoryProvider, null, logger)
{
}
/// <inheritdoc/>
public async ValueTask<DataModel.Entities.LetterBody?> GetLetterBodyByHeaderIdAsync(Guid headerId, CancellationToken cancellationToken = default)
{
using var context = this.RepositoryProvider.ContextStack.UseContext(this);
if (this.RepositoryProvider.GetRepository<LetterBody, LetterBodyRepository>() is { } repository)
{
return await repository.GetBodyByHeaderIdAsync(headerId, cancellationToken).ConfigureAwait(false);
}
return null;
}
/// <inheritdoc/>
public async ValueTask<bool> CanSaveLetterAsync(Interfaces.LetterHeader letterHeader, CancellationToken cancellationToken = default)
{
if (letterHeader is not Model.LetterHeader persistentHeader)
{
return false;
}
persistentHeader.Receiver = await this.Context.Set<Character>().FirstOrDefaultAsync(c => c.Name == letterHeader.ReceiverName, cancellationToken).ConfigureAwait(false);
return persistentHeader.Receiver != null;
}
/// <inheritdoc />
public async ValueTask<DataModel.Entities.AccountState?> AuthenticateAsync(string loginName, string password, CancellationToken cancellationToken = default)
{
using (this.RepositoryProvider.ContextStack.UseContext(this))
{
if (this.RepositoryProvider.GetRepository<Account, AccountRepository>() is { } accountRepository)
{
return await accountRepository.AuthenticateAsync(loginName, password, cancellationToken).ConfigureAwait(false);
}
}
return null;
}
/// <inheritdoc />
public async ValueTask<DataModel.Entities.Account?> GetAccountByLoginNameAsync(string loginName, string password, CancellationToken cancellationToken = default)
{
using (this.RepositoryProvider.ContextStack.UseContext(this))
{
if (this.RepositoryProvider.GetRepository<Account, AccountRepository>() is { } accountRepository)
{
return await accountRepository.GetAccountByLoginNameAsync(loginName, password, cancellationToken).ConfigureAwait(false);
}
}
return null;
}
/// <inheritdoc />
public async ValueTask<DataModel.Entities.Account?> GetAccountByLoginNameAsync(string loginName, CancellationToken cancellationToken = default)
{
using (this.RepositoryProvider.ContextStack.UseContext(this))
{
if (this.RepositoryProvider.GetRepository<Account, AccountRepository>() is { } accountRepository)
{
return await accountRepository.GetAccountByLoginNameAsync(loginName, cancellationToken).ConfigureAwait(false);
}
}
return null;
}
/// <inheritdoc />
public async ValueTask<IEnumerable<DataModel.Entities.Account>> GetAccountsOrderedByLoginNameAsync(int skip, int count, CancellationToken cancellationToken = default)
{
using (this.RepositoryProvider.ContextStack.UseContext(this))
{
return await this.Context.Set<Account>().AsNoTracking().OrderBy(a => a.LoginName).Skip(skip).Take(count).ToListAsync(cancellationToken).ConfigureAwait(false);
}
}
/// <inheritdoc />
public async ValueTask<IEnumerable<DataModel.Entities.Account>> SearchAccountsAsync(string searchTerm, int skip, int count, CancellationToken cancellationToken = default)
{
using (this.RepositoryProvider.ContextStack.UseContext(this))
{
// Invariant: this one runs in .NET, so it must not depend on the server's locale - in a
// Turkish one, "I".ToLower() is a dotless "ı" and the term would match nothing. The
// ToLower() calls inside the query below are a different matter: they are translated to
// the database's own lower(), which is why they cannot take a culture.
var term = searchTerm.ToLowerInvariant();
return await this.Context.Set<Account>().AsNoTracking()
.Where(a => a.LoginName.ToLower().Contains(term)
|| a.RawCharacters.Any(c => c.Name.ToLower().Contains(term)))
.OrderBy(a => a.LoginName)
.Skip(skip)
.Take(count)
.ToListAsync(cancellationToken).ConfigureAwait(false);
}
}
/// <inheritdoc />
public async ValueTask<DataModel.Entities.Account?> GetAccountByCharacterNameAsync(string characterName, CancellationToken cancellationToken = default)
{
using (this.RepositoryProvider.ContextStack.UseContext(this))
{
if (this.RepositoryProvider.GetRepository<Account, AccountRepository>() is { } accountRepository)
{
return await accountRepository.GetAccountByCharacterNameAsync(characterName, cancellationToken).ConfigureAwait(false);
}
}
return null;
}
}