Class ReferencedResource<TKey, TValue>
- Namespace
- Acuit.Pinpoint.ResourceManagement
- Assembly
- Acuit.Pinpoint.ResourceManagement.Abstractions.dll
A base class for implementing IReferencedResource<TKey, TValue> that maintains the list of active references and manages their potentially simultaneous requests for resource values.
public abstract class ReferencedResource<TKey, TValue> : IReferencedResource<TKey, TValue>, IDisposable where TKey : notnull
Type Parameters
TKeyThe type of the key used to identify different resources, which must be a non-nullable type.
TValueThe resource value type.
- Inheritance
-
ReferencedResource<TKey, TValue>
- Implements
-
IReferencedResource<TKey, TValue>
- Derived
-
DerivedReferencedResource<TKey, TValue, TSourceValue>PolledReferencedResource<TKey, TValue>
- Inherited Members
Examples
The following example demonstrates a ReferencedResource<TKey, TValue> implementation that caches values:
public interface IWidgetProvider
{
IResourceReference<Widget> CreateWidgetReference(string widgetGroupName, string widgetItemName);
}
internal sealed record WidgetId(string WidgetGroupName, string WidgetItemName);
internal sealed class WidgetProvider(IMemoryCache memoryCache, IWidgetRetrievalService widgetRetrievalService)
: ResourceProvider<WidgetId, Widget, ReferencedWidget>, IWidgetProvider
{
public IResourceReference<Widget> CreateWidgetReference(string widgetGroupName, string widgetItemName) =>
CreateResourceReference(new WidgetId(widgetGroupName, widgetItemName));
protected override ReferencedWidget CreateReferencedResource(WidgetId key) => new(key, memoryCache, widgetRetrievalService);
}
internal sealed class ReferencedWidget(WidgetId key, IMemoryCache memoryCache, IWidgetRetrievalService widgetRetrievalService)
: ReferencedResource<WidgetId, Widget>(key)
{
private static readonly TimeSpan s_cachePeriod = TimeSpan.FromHours(1);
private readonly ResourceChangeTracker _resourceChangeTracker = new();
protected override void Dispose(bool disposing)
{
_resourceChangeTracker.Dispose();
base.Dispose(disposing);
}
protected override async Task<Widget> GetValueAsync(CancellationToken cancellationToken)
{
var cacheKey = Tuple.Create(GetType(), Key); // ensure key will be unique across app
return await memoryCache.GetOrCreateAsync(cacheKey, async entry =>
{
Widget widget = await widgetRetrievalService.RetrieveWidgetAsync(Key.WidgetGroupName, Key.WidgetItemName,
cancellationToken).ConfigureAwait(false);
_resourceChangeTracker.SignalChange();
return widget;
}, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = s_cachePeriod }).ConfigureAwait(false)
?? throw new UnreachableException();
}
protected override IChangeToken GetChangeToken() => _resourceChangeTracker.GetChangeToken();
}
Remarks
To properly implement a derived class:
- Provide a constructor that calls the base class constructor with the necessary argument.
- Provide an override for GetValueAsync(CancellationToken) that produces the resource value. See the remarks for that method for details.
- Provide an override for GetChangeToken() that produces a change token that signals when the resource value has changed.
Consider deriving from DerivedReferencedResource<TKey, TValue, TSourceValue> or PolledReferencedResource<TKey, TValue>, as they provide additional functionality for common use cases.
Sometimes it is desirable to cache resource values provided by a resource provider to limit the frequency of expensive retrievals for resources that will not necessarily have long-lived resource references. For example, a resource specific to a particular unit might be retrieved upon a unit scan, immediately releasing the resource reference. When the retrieval of this resource is relatively expensive, it is beneficial to cache these values for some period of time. There is no built-in implementation for this because it is easily accomplished within your class derived from ReferencedResource<TKey, TValue> using an IMemoryCache or IDistributedCache, as demonstrated in the example below.
Constructors
ReferencedResource(TKey)
Initializes a new instance of the ReferencedResource<TKey, TValue> class.
protected ReferencedResource(TKey key)
Parameters
keyTKeyThe resource key.
Exceptions
- ArgumentNullException
keyis null.
Properties
Key
Gets the resource key.
public TKey Key { get; }
Property Value
- TKey
Methods
CreateNewReference()
Creates a new reference to this resource.
public IResourceReference<TValue> CreateNewReference()
Returns
- IResourceReference<TValue>
A new IResourceReference<TValue>, which should be disposed when the reference is no longer needed to remove the reference.
Exceptions
- ObjectDisposedException
The object has been disposed.
Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
Dispose(bool)
Closes and releases all resources used by the ReferencedResource<TKey, TValue>.
protected virtual void Dispose(bool disposing)
Parameters
Remarks
Derived classes should override this when they have any resources that should be disposed.
GetChangeToken()
Returns a IChangeToken that can be used to observe when this resource value changes.
protected abstract IChangeToken GetChangeToken()
Returns
GetValueAsync(CancellationToken)
Gets the resource value.
protected abstract Task<TValue> GetValueAsync(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenA cancellation token that can be used to request canceling retrieving the resource.
Returns
- Task<TValue>
The task object representing the asynchronous operation.
Remarks
Once the operation completes, the Result property on the returned task object contains the resource value.
This method will be called on behalf of resource references, ensuring it is never called more than once at a time. The returned value will be cached and returned to all references until the change token (retrieved via GetChangeToken() before calling GetValueAsync(CancellationToken)) signals a change.
If this method throws an exception, it will be propagated to resource references (via their call to GetValueAsync(CancellationToken)), and subsequent calls to GetValueAsync(CancellationToken) by resource references will cause this method to be called again.
Exceptions
- Exception
The resource could not be retrieved. Specific exceptions depend on the implementation.
Events
LastReferenceRemoved
Raised when the last reference to this resource is removed.
public event EventHandler<LastReferenceRemovedEventArgs<TKey>>? LastReferenceRemoved