You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Common/Common/Globalization/LanguageOps.cs

235 lines
6.8 KiB

2 years ago
using System.Collections.Immutable;
using Connected;
using Connected.Entities;
using Connected.Entities.Storage;
using Connected.Globalization.Languages;
using Connected.Notifications.Events;
using Connected.ServiceModel;
using Connected.Services;
namespace Common.Globalization;
/// <summary>
/// Queries all <see cref="ILanguage"/> records except those marked as deleting.
/// </summary>
internal sealed class QueryLanguages : ServiceFunction<IDto, ImmutableList<ILanguage>?>
{
public QueryLanguages(ILanguageCache cache)
{
Cache = cache;
}
private ILanguageCache Cache { get; }
protected override async Task<ImmutableList<ILanguage>?> OnInvoke()
{
/*
* Filter records to return only currently valid record. Those with
* deleting state should never be returned to any client.
*/
return await (from dc in Cache
select dc).AsEntities<ILanguage>();
}
}
/// <summary>
/// Queries the <see cref="ILanguage"/> records for the specified set of ids.
/// </summary>
internal sealed class LookupLanguages : ServiceFunction<PrimaryKeyListArgs<int>, ImmutableList<ILanguage>>
{
public LookupLanguages(ILanguageCache cache)
{
Cache = cache;
}
private ILanguageCache Cache { get; }
protected override async Task<ImmutableList<ILanguage>?> OnInvoke()
{
if (Arguments?.IdList is null)
return default;
return await (from dc in Cache
where Arguments.IdList.Any(f => f == dc.Id)
select dc).AsEntities<ILanguage>();
}
}
/// <summary>
/// Returns first <see cref="ILanguage"/> with matches the provided mapping.
/// </summary>
internal sealed class ResolveLanguage : ServiceFunction<LanguageResolveArgs, ILanguage?>
{
public ResolveLanguage(ILanguageCache cache)
{
Cache = cache;
}
private ILanguageCache Cache { get; }
protected override Task<ILanguage?> OnInvoke()
{
return Task.FromResult<ILanguage?>(Cache.Select(Arguments.Mapping));
}
}
/// <summary>
/// Returns <see cref="ILanguage"/> with the specified id or null
/// if the record for the specified id does not exist.
/// </summary>
internal sealed class SelectLanguage : ServiceFunction<PrimaryKeyArgs<int>, ILanguage?>
{
public SelectLanguage(ILanguageCache cache)
{
Cache = cache;
}
private ILanguageCache Cache { get; }
protected override async Task<ILanguage?> OnInvoke()
{
return await (from dc in Cache
where dc.Id == Arguments.Id
select dc).AsEntity();
}
}
/// <summary>
/// Returns <see cref="ILanguage"/> with the specified name and rate or null if
/// the record with the specified arguments does not exist.
/// </summary>
internal sealed class SelectLanguageByName : ServiceFunction<NameArgs, ILanguage?>
{
public SelectLanguageByName(ILanguageCache cache)
{
Cache = cache;
}
private ILanguageCache Cache { get; }
protected override async Task<ILanguage?> OnInvoke()
{
return await (from dc in Cache
where string.Equals(dc.Name, Arguments.Name, StringComparison.OrdinalIgnoreCase)
select dc).AsEntity();
}
}
/// <summary>
/// Inserts a new <see cref="ILanguage"/> and returns its Id.
/// </summary>
internal sealed class InsertLanguage : ServiceFunction<LanguageInsertArgs, int>
{
public InsertLanguage(ILanguageService languageService, IStorageProvider storage, IEventService events, ILanguageCache cache)
{
LanguageService = languageService;
Storage = storage;
Events = events;
Cache = cache;
}
/// <summary>
/// We need this service to call a distribute event.
/// </summary>
private ILanguageService LanguageService { get; }
private IStorageProvider Storage { get; }
private IEventService Events { get; }
private ILanguageCache Cache { get; }
protected override async Task<int> OnInvoke()
{
/*
* First, create a new entity from the passed arguments and mark its state as new. This will
* signal the DatabaseContext to perform an insert operation when calling the Update.
*/
var entity = Arguments.AsEntity<Language>(State.New);
/*
* Call update on the DatabaseContext. This call will return a new ILanguage of the inserted
* entity.
*/
var result = await Storage.Open<Language>().Update(entity);
/*
* Return a newly inserted id to the caller.
*/
return result.Id;
}
protected override async Task OnCommitted()
{
await Cache.Refresh(Result);
/*
* If ILanguageServer is our implementation (and should be) it's a IServerNotificationsTriggers
* for sure.
*/
await Events.Enqueue(this, LanguageService, ServiceEvents.Inserted, Result);
}
}
internal sealed class DeleteLanguage : ServiceAction<PrimaryKeyArgs<int>>
{
public DeleteLanguage(ILanguageService languageService, IStorageProvider storage, ILanguageCache cache, IEventService events)
{
LanguageService = languageService;
Storage = storage;
Cache = cache;
Events = events;
}
private ILanguageService LanguageService { get; }
private IStorageProvider Storage { get; }
private ILanguageCache Cache { get; }
private IEventService Events { get; }
protected override async Task OnInvoke()
{
var entity = new Language { Id = Arguments.Id, State = State.Deleted };
await Storage.Open<Language>().Update(entity);
}
protected override async Task OnCommitted()
{
await Cache.Remove(Arguments.Id);
await Events.Enqueue(this, LanguageService, ServiceEvents.Deleted, Arguments.Id);
}
}
/// <summary>
/// Updates <see cref="ILanguage"/> entity.
/// </summary>
internal sealed class UpdateLanguage : ServiceAction<LanguageUpdateArgs>
{
public UpdateLanguage(ILanguageService languageService, IStorageProvider storage, ILanguageCache cache, IEventService events)
{
LanguageService = languageService;
Storage = storage;
Cache = cache;
Events = events;
}
private ILanguageService LanguageService { get; }
private IStorageProvider Storage { get; }
private ILanguageCache Cache { get; }
private IEventService Events { get; }
protected override async Task OnInvoke()
{
/*
* Updating Concurrency entity requires a bit more logic. Since Concurrency entity
* guarantees data consistency we must use a retry logic in case of
* Concurrency failure. We'll call Update method with reload lambda function.
*/
await Storage.Open<Language>().Update(await Load(), Arguments, async () =>
{
/*
* Remove entry from the cache to ensure it will be loaded from the database
* next time.
*/
await Cache.Refresh(Arguments.Id);
return await Load();
});
}
private async Task<Language> Load() => await (from dc in Cache where dc.Id == Arguments.Id select dc).AsEntity();
protected override async Task OnCommitted()
{
/*
* Once the update is complete remove the entity from the cache because its concurrency
* state is not valid enymore.
*/
await Cache.Remove(Arguments.Id);
/*
* Now trigger the distributed event notifying the update has completed.
*/
await Events.Enqueue(this, LanguageService, ServiceEvents.Updated, Arguments.Id);
}
}