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; /// /// Queries all records except those marked as deleting. /// internal sealed class QueryLanguages : ServiceFunction?> { public QueryLanguages(ILanguageCache cache) { Cache = cache; } private ILanguageCache Cache { get; } protected override async Task?> 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(); } } /// /// Queries the records for the specified set of ids. /// internal sealed class LookupLanguages : ServiceFunction, ImmutableList> { public LookupLanguages(ILanguageCache cache) { Cache = cache; } private ILanguageCache Cache { get; } protected override async Task?> 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(); } } /// /// Returns first with matches the provided mapping. /// internal sealed class ResolveLanguage : ServiceFunction { public ResolveLanguage(ILanguageCache cache) { Cache = cache; } private ILanguageCache Cache { get; } protected override Task OnInvoke() { return Task.FromResult(Cache.Select(Arguments.Mapping)); } } /// /// Returns with the specified id or null /// if the record for the specified id does not exist. /// internal sealed class SelectLanguage : ServiceFunction, ILanguage?> { public SelectLanguage(ILanguageCache cache) { Cache = cache; } private ILanguageCache Cache { get; } protected override async Task OnInvoke() { return await (from dc in Cache where dc.Id == Arguments.Id select dc).AsEntity(); } } /// /// Returns with the specified name and rate or null if /// the record with the specified arguments does not exist. /// internal sealed class SelectLanguageByName : ServiceFunction { public SelectLanguageByName(ILanguageCache cache) { Cache = cache; } private ILanguageCache Cache { get; } protected override async Task OnInvoke() { return await (from dc in Cache where string.Equals(dc.Name, Arguments.Name, StringComparison.OrdinalIgnoreCase) select dc).AsEntity(); } } /// /// Inserts a new and returns its Id. /// internal sealed class InsertLanguage : ServiceFunction { public InsertLanguage(ILanguageService languageService, IStorageProvider storage, IEventService events, ILanguageCache cache) { LanguageService = languageService; Storage = storage; Events = events; Cache = cache; } /// /// We need this service to call a distribute event. /// private ILanguageService LanguageService { get; } private IStorageProvider Storage { get; } private IEventService Events { get; } private ILanguageCache Cache { get; } protected override async Task 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(State.New); /* * Call update on the DatabaseContext. This call will return a new ILanguage of the inserted * entity. */ var result = await Storage.Open().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> { 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().Update(entity); } protected override async Task OnCommitted() { await Cache.Remove(Arguments.Id); await Events.Enqueue(this, LanguageService, ServiceEvents.Deleted, Arguments.Id); } } /// /// Updates entity. /// internal sealed class UpdateLanguage : ServiceAction { 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().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 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); } }