Search accounts in the database instead of loading all of them (cherry picked from commit 4e806cbe64f855a765e864c0c2ff45238f487e36)
136 lines
5.8 KiB
C#
136 lines
5.8 KiB
C#
// <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;
|
||
}
|
||
} |