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 : StatefulCacheClient, IEntityCacheClient where TEntity : class, IPrimaryKey, 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(); try { if (await OnInitializing(ctx) is ImmutableList 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?> OnInitializing(IContext context) { if (context.GetService() is not IStorageProvider db) return default; return await (from dc in db.Open() select dc).AsEntities(); } protected override async Task OnInvalidate(TPrimaryKey id) { using var ctx = Context.ContextProvider.Create(); var transaction = ctx.GetService(); try { if (OnInvalidating(ctx, id) is TEntity entity && entity is IPrimaryKey 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 OnInvalidating(IContext context, TPrimaryKey id) { if (context.GetService() is not IStorageProvider provider) return default; return await (from dc in provider.Open() where TypeComparer.Compare(dc.Id, id) select dc).AsEntity(); } async Task IEntityCacheClient.Refresh(TPrimaryKey id) { await Refresh(id); } async Task IEntityCacheClient.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 concurrent) { if (Get(id) is TEntity existing && existing is IConcurrentEntity existingConcurrent) { lock (existingConcurrent) { if (existingConcurrent.Sync != concurrent.Sync) throw new InvalidOperationException(SR.ErrConcurrent); concurrent.GetType().GetProperty(nameof(IConcurrentEntity.Sync))?.SetValue(concurrent, concurrent.Sync + 1); Set(id, instance, duration); return; } } } base.Set(id, instance, duration); } }