|
|
|
|
using System.Collections.Immutable;
|
|
|
|
|
using Connected.Caching;
|
|
|
|
|
using Connected.Entities;
|
|
|
|
|
using Connected.Entities.Storage;
|
|
|
|
|
using Connected.Notifications.Events;
|
|
|
|
|
using Connected.ServiceModel;
|
|
|
|
|
using Connected.Services;
|
|
|
|
|
|
|
|
|
|
namespace Logistics.Types.Serials;
|
|
|
|
|
internal sealed class SerialOps
|
|
|
|
|
{
|
|
|
|
|
/// <inheritdoc cref="ISerialService.Delete(PrimaryKeyArgs{long})"/>
|
|
|
|
|
public sealed class Delete : ServiceAction<PrimaryKeyArgs<long>>
|
|
|
|
|
{
|
|
|
|
|
public Delete(IStorageProvider storage, ISerialService serials, IEventService events, ICachingService cache)
|
|
|
|
|
{
|
|
|
|
|
Storage = storage;
|
|
|
|
|
Serials = serials;
|
|
|
|
|
Events = events;
|
|
|
|
|
Cache = cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
|
private ISerialService Serials { get; }
|
|
|
|
|
private IEventService Events { get; }
|
|
|
|
|
private ICachingService Cache { get; }
|
|
|
|
|
|
|
|
|
|
protected override async Task OnInvoke()
|
|
|
|
|
{
|
|
|
|
|
if (await Serials.Select(Arguments.Id) is not ISerial serial)
|
|
|
|
|
return;
|
|
|
|
|
/*
|
|
|
|
|
* Setting state of the entity enable other middleware to use the entity even after it
|
|
|
|
|
* is deleted. For example, IEventListener will receive the state of this operation.
|
|
|
|
|
*/
|
|
|
|
|
SetState(serial);
|
|
|
|
|
/*
|
|
|
|
|
* Perform delete.
|
|
|
|
|
*/
|
|
|
|
|
await Storage.Open<Serial>().Update(Arguments.AsEntity<Serial>(State.Deleted));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override async Task OnCommitted()
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Remove entity from the cache.
|
|
|
|
|
*/
|
|
|
|
|
await Cache.Remove(Serial.EntityKey, Arguments.Id);
|
|
|
|
|
/*
|
|
|
|
|
* Enqueue event so event listeners can respond to the transaction.
|
|
|
|
|
*/
|
|
|
|
|
await Events.Enqueue(this, Events, nameof(ISerialService.Deleted), Arguments);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc cref=" ISerialService.Insert(InsertSerialArgs)"/>
|
|
|
|
|
public sealed class Insert : ServiceFunction<InsertSerialArgs, long>
|
|
|
|
|
{
|
|
|
|
|
public Insert(IStorageProvider storage, IEventService events)
|
|
|
|
|
{
|
|
|
|
|
Storage = storage;
|
|
|
|
|
Events = events;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
|
private IEventService Events { get; }
|
|
|
|
|
|
|
|
|
|
protected override async Task<long> OnInvoke()
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Perform insert and return the newly inserted id.
|
|
|
|
|
*/
|
|
|
|
|
return (await Storage.Open<Serial>().Update(Arguments.AsEntity<Serial>(State.New))).Id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override async Task OnCommitted()
|
|
|
|
|
{
|
|
|
|
|
await Events.Enqueue(this, Events, nameof(ISerialService.Inserted), Arguments);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc cref="ISerialService.Query(QueryArgs?)"/>
|
|
|
|
|
public sealed class Query : ServiceFunction<QueryArgs, ImmutableList<ISerial>>
|
|
|
|
|
{
|
|
|
|
|
public Query(IStorageProvider storage)
|
|
|
|
|
{
|
|
|
|
|
Storage = storage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
|
|
|
|
|
|
protected override async Task<ImmutableList<ISerial>> OnInvoke()
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* For non cached entities query always hits the storage.
|
|
|
|
|
*/
|
|
|
|
|
return await (from e in Storage.Open<Serial>()
|
|
|
|
|
select e).WithArguments(Arguments).AsEntities<ISerial>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <inheritdoc cref="ISerialService.Query(PrimaryKeyListArgs{long})"/>
|
|
|
|
|
public sealed class Lookup : ServiceFunction<PrimaryKeyListArgs<long>, ImmutableList<ISerial>>
|
|
|
|
|
{
|
|
|
|
|
public Lookup(IStorageProvider storage)
|
|
|
|
|
{
|
|
|
|
|
Storage = storage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
|
|
|
|
|
|
protected override async Task<ImmutableList<ISerial>> OnInvoke()
|
|
|
|
|
{
|
|
|
|
|
return await (from e in Storage.Open<Serial>()
|
|
|
|
|
where Arguments.IdList.Any(f => f == e.Id)
|
|
|
|
|
select e).AsEntities<ISerial>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <inheritdoc cref="ISerialService.Select(PrimaryKeyArgs{long})"/>
|
|
|
|
|
public sealed class Select : NullableServiceFunction<PrimaryKeyArgs<long>, ISerial>
|
|
|
|
|
{
|
|
|
|
|
public Select(IStorageProvider storage, ICachingService cache)
|
|
|
|
|
{
|
|
|
|
|
Storage = storage;
|
|
|
|
|
Cache = cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
|
private ICachingService Cache { get; }
|
|
|
|
|
|
|
|
|
|
protected override async Task<ISerial?> OnInvoke()
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* First, try to receive entity from the cache. If it doesn't exist in the cache
|
|
|
|
|
* load it from storage. If storage returns non null value, store it in the cache
|
|
|
|
|
* for subsequent calls. The entity gets remove either because of inactivity or when
|
|
|
|
|
* updating or deleting it.
|
|
|
|
|
*/
|
|
|
|
|
return await Cache.Get<ISerial>(Serial.EntityKey, Arguments.Id, async (f) =>
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Doesn't exist in the cache. Let's do the storage action.
|
|
|
|
|
*/
|
|
|
|
|
return await (from e in Storage.Open<Serial>()
|
|
|
|
|
where e.Id == Arguments.Id
|
|
|
|
|
select e).AsEntity();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <inheritdoc cref="ISerialService.Select(SelectSerialArgs)"/>
|
|
|
|
|
public sealed class SelectByValue : NullableServiceFunction<SelectSerialArgs, ISerial>
|
|
|
|
|
{
|
|
|
|
|
public SelectByValue(IStorageProvider storage, ICachingService cache)
|
|
|
|
|
{
|
|
|
|
|
Storage = storage;
|
|
|
|
|
Cache = cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
|
private ICachingService Cache { get; }
|
|
|
|
|
|
|
|
|
|
protected override async Task<ISerial?> OnInvoke()
|
|
|
|
|
{
|
|
|
|
|
return await Cache.Get<ISerial>(Serial.EntityKey, f => string.Equals(f.Value, Arguments.Value, StringComparison.OrdinalIgnoreCase), async (f) =>
|
|
|
|
|
{
|
|
|
|
|
return await (from e in Storage.Open<Serial>()
|
|
|
|
|
where string.Equals(e.Value, Arguments.Value, StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
select e).AsEntity();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <inheritdoc cref="ISerialService.Update(UpdateSerialArgs)"/>
|
|
|
|
|
public sealed class Update : ServiceAction<UpdateSerialArgs>
|
|
|
|
|
{
|
|
|
|
|
public Update(IStorageProvider storage, ICachingService cache, ISerialService packingService, IEventService events)
|
|
|
|
|
{
|
|
|
|
|
Storage = storage;
|
|
|
|
|
Cache = cache;
|
|
|
|
|
Serials = packingService;
|
|
|
|
|
Events = events;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IStorageProvider Storage { get; }
|
|
|
|
|
private ICachingService Cache { get; }
|
|
|
|
|
private ISerialService Serials { get; }
|
|
|
|
|
private IEventService Events { get; }
|
|
|
|
|
|
|
|
|
|
protected override async Task OnInvoke()
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Set the state of the unchanged entity. This enable other middleware to
|
|
|
|
|
* calculate quantity delta, for example because they receive the state of
|
|
|
|
|
* this operation. Not all middleware supports this, the primary example of
|
|
|
|
|
* such state client is IEventListener.
|
|
|
|
|
*/
|
|
|
|
|
if (SetState(await Serials.Select(Arguments.Id)) is not Serial entity)
|
|
|
|
|
return;
|
|
|
|
|
/*
|
|
|
|
|
* Sinc ethis is concurrent entity we must perform retry if the concurrency fails.
|
|
|
|
|
*/
|
|
|
|
|
await Storage.Open<Serial>().Update(entity.Merge(Arguments, State.Default), Arguments, async () =>
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The update failed because of concurrency. Remove the entity from the cache to ensure
|
|
|
|
|
* it gets loaded from the storage next time with fresh values and try again.
|
|
|
|
|
*/
|
|
|
|
|
await Cache.Remove(Serial.EntityKey, Arguments.Id);
|
|
|
|
|
/*
|
|
|
|
|
* Since the entity reloaded we must overwrite its state.
|
|
|
|
|
*/
|
|
|
|
|
return SetState(await Serials.Select(Arguments.Id)) as Serial;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override async Task OnCommitted()
|
|
|
|
|
{
|
|
|
|
|
await Cache.Remove(Serial.EntityKey, Arguments.Id);
|
|
|
|
|
await Events.Enqueue(this, Serials, nameof(Serials.Updated), Arguments);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|