Table of Contents

Class UpdatingReferencedResource

Namespace
Acuit.Pinpoint.ResourceManagement
Assembly
Acuit.Pinpoint.ResourceManagement.Abstractions.dll

A base class for resources referenced by resource providers derived from ResourceProvider that updates the resource value upon "update-needed" signals.

public abstract class UpdatingReferencedResource : ReferencedResource, IDisposable
Inheritance
UpdatingReferencedResource
Implements
Derived
Inherited Members

Examples

This can be used to implement a resource that must be polled periodically like this:

internal class PolledResourceProvider : ResourceProvider // TODO: Typically, also implement IRegisteredResourceProvider
{
    private readonly IErrorHandler _errorHandler;
    private readonly ITimeService _timeService;

    public PolledResourceProvider(IErrorHandler errorHandler, ITimeService timeService)
    {
        _errorHandler = errorHandler;
        _timeService = timeService;
    }

    protected override ReferencedResource CreateReferencedResource(Uri url)
    {
        return new PolledReferencedResource(url, _errorHandler, _timeService);
    }
}

internal class PolledReferencedResource : UpdatingReferencedResource
{
    private static readonly TimeSpan s_pollPeriod = TimeSpan.FromMinutes(15);
    private static readonly TimeSpan s_retryPeriod = TimeSpan.FromSeconds(15);

    private readonly ITimeService _timeService;
    private DelayChangeToken _updateNeededChangeToken;

    public PolledReferencedResource(Uri url, IErrorHandler errorHandler, ITimeService timeService)
        : base(url, errorHandler, timeService)
    {
        _timeService = timeService;
        ResetUpdateNeededChangeToken(TimeSpan.Zero);
        StartUpdating();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            _updateNeededChangeToken.Dispose();
        base.Dispose(disposing);
    }

    private void ResetUpdateNeededChangeToken(TimeSpan delay)
    {
        var oldToken = _updateNeededChangeToken;
        _updateNeededChangeToken = new DelayChangeToken(delay, _timeService);
        oldToken?.Dispose();
    }

    protected override async Task<object> GetUpdatedResourceValueAsync(CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        try
        {
            var value = await PollValueAsync().ConfigureAwait(false); // TODO: poll the value
            ResetUpdateNeededChangeToken(s_pollPeriod);
            return value;
        }
        catch
        {
            ResetUpdateNeededChangeToken(s_retryPeriod); // Upon an error, retry sooner
            throw;
        }
    }

    protected override IChangeToken GetUpdateNeededToken() => _updateNeededChangeToken;
}
///

This can be used to implement a resource that derives its value from another resource like this:

internal class DerivedResourceProvider : ResourceProvider // TODO: Typically, also implement IRegisteredResourceProvider
{
    private readonly IErrorHandler _errorHandler;
    private readonly IResourceProvider _resourceProvider;
    private readonly ITimeService _timeService;

    public DerivedResourceProvider(IErrorHandler errorHandler, IResourceProvider resourceProvider, ITimeService timeService)
    {
        _errorHandler = errorHandler;
        _resourceProvider = resourceProvider;
        _timeService = timeService;
    }

    protected override ReferencedResource CreateReferencedResource(Uri url)
    {
        var sourceResourceUrl = DetermineSourceResourceUrl(url); // TODO: determine source resource URL for this derived resource
        return new DerivedReferencedResource(url, sourceResourceUrl, _errorHandler, _resourceProvider, _timeService);
    }
}

internal class DerivedReferencedResource : UpdatingReferencedResource
{
    private readonly IResourceReference _sourceResourceReference;

    public DerivedReferencedResource(Uri url, Uri sourceResourceUrl, IErrorHandler errorHandler, IResourceProvider resourceProvider, ITimeService timeService)
        : base(url, errorHandler, timeService)
    {
        _sourceResourceReference = resourceProvider.CreateResourceReference(sourceResourceUrl);
        StartUpdating();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            _sourceResourceReference.Dispose();
        base.Dispose(disposing);
    }

    protected override async Task<object> GetUpdatedResourceValueAsync(CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        var sourceValue = await _sourceResourceReference.GetValueAsync(cancellationToken).ConfigureAwait(false);
        return CalculateDerivedValue(sourceValue); // TODO: create derived value from source value
    }

    protected override IChangeToken GetUpdateNeededToken() => _sourceResourceReference.GetChangeToken();
}

Remarks

This provides common logic for scenarios such as:

  • Resources that must be periodically polled for new values. (DelayChangeToken can be useful for implementing GetUpdateNeededToken().)
  • Resources that monitor some external source for changes.

Implementations must do the following:

  1. Implement GetUpdatedResourceValueAsync(CancellationToken).
  2. Implement GetUpdateNeededToken().
  3. Call StartUpdating(), usually in the constructor.

Note that the token returned from GetChangeToken() will only be signaled upon the completion of the first update attempt, whether it succeeds or fails; and then subsequently upon each successful update when the value changes. If a subsequent update attempt fails, the change token will not be signaled (since the value did not change).

Constructors

UpdatingReferencedResource(Uri, IErrorHandler, ITimeService)

Initializes a new instance of the UpdatingReferencedResource class.

protected UpdatingReferencedResource(Uri url, IErrorHandler errorHandler, ITimeService timeService)

Parameters

url Uri

The resource URL.

errorHandler IErrorHandler

The error handler service.

timeService ITimeService

The time service.

Exceptions

ArgumentNullException

url is null.

ArgumentNullException

errorHandler is null.

ArgumentNullException

timeService is null.

Methods

Dispose(bool)

Closes and releases all resources used by the ReferencedResource.

protected override void Dispose(bool disposing)

Parameters

disposing bool

true when this is in response to a call to Dispose().

Remarks

Derived classes should override this when they have any resources that should be disposed.

GetLastKnownValueAsync(CancellationToken)

Gets the last known value for this resource.

protected override Task<object> GetLastKnownValueAsync(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

A cancellation token that can be used to request canceling waiting for the value.

Returns

Task<object>

A task that represents the asynchronous operation. The value of its Result property contains the last known resource value.

Remarks

In general, this should immediately return the last known resource value, unless the initial asynchronous resource retrieval is still in progress, in which case it should return a task that will complete when that attempt completes. Note that cancellationToken will cancel waiting on that attempt to complete, not cancel that attempt itself.

This might be called from multiple threads and before other asynchronous calls are complete.

Exceptions

ObjectDisposedException

This object has been disposed.

Exception

The resource could not be retrieved.

GetResourceValue(CancellationToken)

Gets the resource value. Derived classes can override the default behavior, which simply calls GetUpdatedResourceValueAsync(CancellationToken).

protected virtual Task<object> GetResourceValue(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

A cancellation token that can be used to request canceling getting the updated resource value.

Returns

Task<object>

A task that represents the asynchronous operation. The value of its Result property contains the resource value.

Exceptions

Exception

The updated resource value could not be obtained. The implementation should throw a specific exception appropriate for the error. This can be thrown synchronously, or it can occur asynchronously, wrapped in an AggregateException as the faulted task's Exception.

GetUpdateNeededToken()

Gets a token that will signal a change when the resource value should be updated again.

protected abstract IChangeToken GetUpdateNeededToken()

Returns

IChangeToken

The change token.

GetUpdatedResourceValueAsync(CancellationToken)

Gets the updated resource value. This should actually retrieve the resource value.

protected abstract Task<object> GetUpdatedResourceValueAsync(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

A cancellation token that can be used to request canceling getting the updated resource value.

Returns

Task<object>

A task that represents the asynchronous operation. The value of its Result property contains the resource value.

Exceptions

Exception

The updated resource value could not be obtained. The implementation should throw a specific exception appropriate for the error. This can be thrown synchronously, or it can occur asynchronously, wrapped in an AggregateException as the faulted task's Exception.

StartUpdating()

Starts the automatic updater.

protected void StartUpdating()

Exceptions

ObjectDisposedException

This object has been disposed.