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.Framework/Connected.Entities/Caching/EntityCacheClient.cs

128 lines
3.8 KiB

2 years ago
using Connected.Caching;
using Connected.Data;
using Connected.Entities.Concurrency;
using Connected.Entities.Storage;
using Connected.Interop;
using Connected.ServiceModel;
using Connected.ServiceModel.Transactions;
using System.Collections.Immutable;
namespace Connected.Entities.Caching;
public abstract class EntityCacheClient<TEntity, TPrimaryKey> : StatefulCacheClient<TEntity, TPrimaryKey>, IEntityCacheClient<TEntity, TPrimaryKey>
where TEntity : class, IPrimaryKey<TPrimaryKey>, IEntity
where TPrimaryKey : notnull
{
protected EntityCacheClient(IEntityCacheContext context, string key) : base(context.Cache, key)
{
Context = context;
}
private IEntityCacheContext Context { get; }
protected override sealed async Task OnInitializing()
{
using var ctx = Context.ContextProvider.Create();
var transaction = ctx.GetService<ITransactionContext?>();
try
{
if (await OnInitializing(ctx) is ImmutableList<TEntity> ds)
{
foreach (var r in ds)
Set(r.Id, r, TimeSpan.Zero);
}
if (transaction is not null)
await transaction.Commit();
}
catch
{
if (transaction is not null)
await transaction.Rollback();
throw;
}
}
protected virtual async Task<ImmutableList<TEntity>?> OnInitializing(IContext context)
{
if (context.GetService<IStorageProvider>() is not IStorageProvider db)
return default;
return await (from dc in db.Open<TEntity>()
select dc).AsEntities();
}
protected override async Task OnInvalidate(TPrimaryKey id)
{
using var ctx = Context.ContextProvider.Create();
var transaction = ctx.GetService<ITransactionContext>();
try
{
if (OnInvalidating(ctx, id) is TEntity entity && entity is IPrimaryKey<TPrimaryKey> pk)
Set(pk.Id, entity, TimeSpan.Zero);
if (transaction is not null)
await transaction.Commit();
}
catch
{
if (transaction is not null)
await transaction.Rollback();
throw;
}
}
protected virtual async Task<TEntity?> OnInvalidating(IContext context, TPrimaryKey id)
{
if (context.GetService<IStorageProvider>() is not IStorageProvider provider)
return default;
return await (from dc in provider.Open<TEntity>()
where TypeComparer.Compare(dc.Id, id)
select dc).AsEntity();
}
async Task IEntityCacheClient<TEntity, TPrimaryKey>.Refresh(TPrimaryKey id)
{
await Refresh(id);
}
async Task IEntityCacheClient<TEntity, TPrimaryKey>.Remove(TPrimaryKey id)
{
await Remove(id);
}
protected override void Set(TPrimaryKey id, TEntity instance)
{
Set(id, instance, TimeSpan.Zero);
}
protected override void Set(TPrimaryKey id, TEntity instance, TimeSpan duration)
{
if (instance is IConcurrentEntity<TPrimaryKey> concurrent)
{
if (Get(id) is TEntity existing && existing is IConcurrentEntity<TPrimaryKey> existingConcurrent)
{
lock (existingConcurrent)
{
if (existingConcurrent.Sync != concurrent.Sync)
throw new InvalidOperationException(SR.ErrConcurrent);
concurrent.GetType().GetProperty(nameof(IConcurrentEntity<TPrimaryKey>.Sync))?.SetValue(concurrent, concurrent.Sync + 1);
Set(id, instance, duration);
return;
}
}
}
base.Set(id, instance, duration);
}
}