/// <inheritdoc/> public void ReadAndPublishStreamValue(IStreamReader streamReader, DateTime dateTime) { foreach (var epsilonTimeInterval in this.publishers.Keys) { // Get the index of the data, given the cursor time, and the epsilon interval. int index = IndexHelper.GetIndexForTime(dateTime, epsilonTimeInterval, this.index?.Count ?? 0, (idx) => this.index[idx].OriginatingTime); if (index >= 0) { // Get the index entry var indexedStreamReaderThunk = this.index[index]; // Read the data (this allocates) var data = indexedStreamReaderThunk.Read(streamReader); // Publish the value foreach (var publisher in this.publishers[epsilonTimeInterval]) { publisher.PublishValue(true, data, indexedStreamReaderThunk.OriginatingTime, indexedStreamReaderThunk.CreationTime); } // Deallocate to control the lifetime of the read object this.Deallocator?.Invoke(data); } else { // Cache miss, attempt to seek directly. The cache miss could happen because the // the stream index data structure is being populated, or because the epsilon interval // is set such that no data point is found. In either case, a direct read is // attempted. streamReader.Seek(dateTime + epsilonTimeInterval, true); streamReader.OpenStream( this.StreamName, (data, envelope) => { foreach (var publisher in this.publishers[epsilonTimeInterval]) { publisher.PublishValue(true, data, envelope.OriginatingTime, envelope.CreationTime); } }, this.Allocator, this.Deallocator); // This will force the read of the next message. If a message within the seek // constraints is found, the delegate passed to OpenStream above is executed // to capture the data. If no message exists in that specified seek interval // the delegate does not execute and found below becomes false. var found = streamReader.MoveNext(out var envelope); // If no message was found if (!found) { // Then signal the data providers that to message is available foreach (var publisher in this.publishers[epsilonTimeInterval]) { publisher.PublishValue(false, default, default, default);
/// <inheritdoc/> public override void OpenStream(IStreamReader streamReader) { if (streamReader == null) { throw new ArgumentNullException(nameof(streamReader)); } if (this.StreamAdapter == null) { streamReader.OpenStream(this.StreamName, this.OnReceiveData, this.Allocator, this.Deallocator, this.OnReadError); } else { dynamic dynStreamAdapter = this.StreamAdapter; dynamic dynAdaptedReceiver = dynStreamAdapter.AdaptReceiver(new Action <T, Envelope>(this.OnReceiveData)); dynamic dynReadError = new Action <SerializationException>(this.OnReadError); streamReader.OpenStream(this.StreamName, dynAdaptedReceiver, dynStreamAdapter.SourceAllocator, dynStreamAdapter.SourceDeallocator, dynReadError); } }
/// <inheritdoc /> public void OpenStream(IStreamReader streamReader, bool readIndicesOnly) { if (streamReader == null) { throw new ArgumentNullException(nameof(streamReader)); } if (readIndicesOnly) { if (this.StreamAdapter == null) { streamReader.OpenStreamIndex <T>(this.StreamName, this.OnReceiveIndex); } else { var genericOpenStreamIndex = typeof(IStreamReader) .GetMethod("OpenStreamIndex", new Type[] { typeof(string), typeof(Action <Func <IStreamReader, T>, Envelope>) }) .MakeGenericMethod(this.StreamAdapter.SourceType); var receiver = new Action <Func <IStreamReader, T>, Envelope>(this.OnReceiveIndex); genericOpenStreamIndex.Invoke(streamReader, new object[] { this.StreamName, receiver }); } } else { if (this.StreamAdapter == null) { streamReader.OpenStream <T>(this.StreamName, this.OnReceiveData, this.Allocator, this.OnReadError); } else { dynamic dynStreamAdapter = this.StreamAdapter; dynamic dynAdaptedReceiver = dynStreamAdapter.AdaptReceiver(new Action <T, Envelope>(this.OnReceiveData)); dynamic dynReadError = new Action <SerializationException>(this.OnReadError); streamReader.OpenStream(this.StreamName, dynAdaptedReceiver, dynStreamAdapter.Allocator, dynReadError); } } }
/// <summary> /// Reads instant data from the stream at the given cursor time and pushes it to all registered adapting data providers. /// </summary> /// <param name="streamReader">The stream reader that will read the data.</param> /// <param name="cursorTime">The cursor time at which to read the data.</param> /// <param name="streamCache">The stream reader's cache.</param> public void ReadInstantData(IStreamReader streamReader, DateTime cursorTime, ObservableKeyedCache <DateTime, StreamCacheEntry> streamCache) { // Get the index of the data, given the cursor time int index = IndexHelper.GetIndexForTime(cursorTime, streamCache?.Count ?? 0, (idx) => streamCache[idx].OriginatingTime, this.CursorEpsilon); T data = default; StreamCacheEntry cacheEntry = default; if (index >= 0) { // Get the index entry cacheEntry = streamCache[index]; // Read the data data = cacheEntry.Read <T>(streamReader); } else { // cache miss, attempt to seek directly while the cache is presumably being populated streamReader.Seek(cursorTime + this.CursorEpsilon, true); streamReader.OpenStream <T>(this.streamName, (m, e) => { cacheEntry = new StreamCacheEntry(null, e.CreationTime, e.OriginatingTime); data = m; }); streamReader.MoveNext(out var envelope); } // Notify each adapting data provider of the new data foreach (IAdaptingInstantDataProvider <T> adaptingInstantDataProvider in this.dataProviders.ToList()) { adaptingInstantDataProvider.PushData(data, cacheEntry); } // Release the reference to the local copy of the data if it's shared if (this.isSharedType && data != null) { (data as IDisposable).Dispose(); } }