/// <summary> /// Event Stream returned by SelectObjectContentStream. /// <para></para> /// Events can be retrieved from this stream by either /// <list type="bullet"> /// <item><description>attaching handlers to listen events, and then call StartProcessing <i>or</i></description></item> /// <item><description>enumerating over the events.</description></item> /// </list> /// <para></para> /// These options should be treaded as mutually exclusive. /// </summary> /// <param name="selectObjectStream">The network stream which events will be parsed from.</param> /// <param name="eventStreamDecoder">The decoder responsible for parsing events.</param> public SelectObjectContentEventStream(Stream selectObjectStream, IEventStreamDecoder eventStreamDecoder) : base(selectObjectStream, eventStreamDecoder) { // C# doesn't do event inheritance right. They are effectively treated as new, but not quite. // // In this case, EventStream has an event handler reference, and SelectObjectContentEventStream has an event handler reference (like new). // But it also overrides the accessors, so references will be polymorphically directed (like override). // // So when we attach event handlers, they are attached to the SelectObjectContentEventStream. // But when the EventStream code raises an event, those are raised on the EventStream handler reference. // // As a result, we get no events! // // Here, we are attaching a handler to raise our event when a subclass event/exception is raised. // Currently, all 'events' are raised in the subclass, so this is only needed for exceptions. Stubbed for generator use. base.EventReceived += (sender, args) => EventReceived?.Invoke(this, args); base.ExceptionReceived += (sender, args) => ExceptionReceived?.Invoke(this, args); // Mapping the generic event to more specific events. Decoder.MessageReceived += (sender, args) => { IS3Event ev; try { ev = ConvertMessageToEvent(args.Message); } catch (UnknownEventStreamMessageTypeException) { // Silence to ensure backwards-compatability with future EventStream specification. return; } EventReceived?.Invoke(this, new EventStreamEventReceivedArgs <IS3Event>(ev)); // Call RaiseEvent until it returns true or all calls complete. This way, only a subset of casts is preformed, // and we can avoid a cascade of nested if/else statements. The result is thrown away. var _ = RaiseEvent(RecordsEventReceived, ev) || RaiseEvent(StatsEventReceived, ev) || RaiseEvent(ProgressEventReceived, ev) || RaiseEvent(ContinuationEventReceived, ev) || RaiseEvent(EndEventReceived, ev); }; }
private void ProcessLoop(object state) { var buffer = new byte[BufferSize]; try { while (IsProcessing) { ReadFromStream(buffer); } } // These exceptions are raised on the background thread. They are fired as events for visibility. catch (Exception ex) { IsProcessing = false; // surfaceException means what is surfaced to the user. For example, in S3Select, that would be a S3EventStreamException. var surfaceException = WrapException(ex); // Raise the exception as an event. ExceptionReceived?.Invoke(this, new EventStreamExceptionReceivedArgs <TE>(surfaceException)); } }