/// <summary> /// Subscribes the specified aggregator to a catchup. /// </summary> /// <typeparam name="TProjection">The type of the projection.</typeparam> /// <typeparam name="TData">The type of the stream's data.</typeparam> /// <typeparam name="TCursor">The type of the cursor.</typeparam> /// <param name="catchup">The catchup.</param> /// <param name="aggregator">The aggregator.</param> /// <returns>A disposable that, when disposed, unsubscribes the aggregator.</returns> public static IDisposable Subscribe <TProjection, TData, TCursor>( this IStreamCatchup <TData, TCursor> catchup, IStreamAggregator <TProjection, TData> aggregator, FetchAndSaveProjection <TProjection> manageProjection, HandleAggregatorError <TProjection> onError = null) { return(catchup.SubscribeAggregator(aggregator, manageProjection, onError)); }
/// <summary> /// Wraps a stream aggregator in a decorator function having the same signature. /// </summary> /// <typeparam name="TProjection">The type of the projection.</typeparam> /// <typeparam name="TData">The type of the data.</typeparam> /// <param name="aggregator">The aggregator.</param> /// <param name="initial">The prior state of the projection.</param> /// <returns>A new aggregator that wraps calls to the specified aggregator.</returns> public static IStreamAggregator <TProjection, TData> Pipeline <TProjection, TData>( this IStreamAggregator <TProjection, TData> aggregator, Func <TProjection, IStreamBatch <TData>, AggregateAsync <TProjection, TData>, Task> initial) { return(Create <TProjection, TData>((projection, batch) => initial(projection, batch, aggregator.Aggregate))); }
/// <summary> /// Subscribes the specified aggregator to a catchup. /// </summary> /// <typeparam name="TProjection">The type of the projection.</typeparam> /// <typeparam name="TData">The type of the stream's data.</typeparam> /// <typeparam name="TCursor">The type of the cursor.</typeparam> /// <param name="catchup">The catchup.</param> /// <param name="aggregator">The aggregator.</param> /// <param name="projectionStore">The projection store.</param> /// <returns>A disposable that, when disposed, unsubscribes the aggregator.</returns> public static IDisposable Subscribe <TProjection, TData, TCursor>( this IStreamCatchup <TData, TCursor> catchup, IStreamAggregator <TProjection, TData> aggregator, IProjectionStore <string, TProjection> projectionStore = null) { return(catchup.Subscribe(aggregator, projectionStore.AsHandler())); }
/// <summary> /// Traces calls to the specified aggregator. /// </summary> /// <typeparam name="TProjection">The type of the projection.</typeparam> /// <typeparam name="TData">The type of the data.</typeparam> /// <param name="aggregator">The aggregator.</param> /// <param name="write">A delegate that can be used to specify how the arguments should be traced. If this is not provided, output is sent to <see cref="System.Diagnostics.Trace" />.</param> /// <returns>The original aggregator wrapped in a tracing decorator.</returns> public static IStreamAggregator <TProjection, TData> Trace <TProjection, TData>( this IStreamAggregator <TProjection, TData> aggregator, Action <TProjection, IStreamBatch <TData> > write = null) { write = write ?? TraceDefault; return(aggregator.Pipeline(async(projection, batch, next) => { try { write(projection, batch); return await next(projection, batch); } catch (Exception exception) { System.Diagnostics.Trace.WriteLine("[Aggregate] Exception: " + exception); throw; } })); }
/// <summary> /// Aggregates a single batch of data from a stream using the specified aggregator and projection. /// </summary> /// <typeparam name="TProjection">The type of the projection.</typeparam> /// <typeparam name="TData">The type of the data.</typeparam> /// <typeparam name="TCursor">The type of the cursor.</typeparam> /// <param name="stream">The stream.</param> /// <param name="aggregator">The aggregator.</param> /// <param name="projection">The projection.</param> /// <returns>The updated state of the projection.</returns> /// <remarks>This method can be used to create on-demand projections. It does not do any projection persistence.</remarks> public static async Task <TProjection> Aggregate <TProjection, TData, TCursor>( this IStream <TData, TCursor> stream, IStreamAggregator <TProjection, TData> aggregator, TProjection projection = null) where TProjection : class { var cursor = (projection as ICursor <TCursor>) ?? stream.NewCursor(); var query = stream.CreateQuery(cursor); var data = await query.NextBatch(); if (data.Any()) { projection = await aggregator.Aggregate(projection, data); } return(projection); }
public IDisposable SubscribeAggregator <TProjection>( IStreamAggregator <TProjection, TData> aggregator, FetchAndSaveProjection <TProjection> fetchAndSaveProjection, HandleAggregatorError <TProjection> onError) { var added = aggregatorSubscriptions.TryAdd(typeof(TProjection), new AggregatorSubscription <TProjection, TData>(aggregator, fetchAndSaveProjection, onError)); if (!added) { throw new InvalidOperationException(string.Format("Aggregator for projection of type {0} is already subscribed.", typeof(TProjection))); } return(Disposable.Create(() => { IAggregatorSubscription _; aggregatorSubscriptions.TryRemove(typeof(TProjection), out _); })); }
public AggregatorSubscription( IStreamAggregator <TProjection, TData> aggregator, FetchAndSaveProjection <TProjection> fetchAndSaveProjection = null, HandleAggregatorError <TProjection> onError = null) { if (aggregator == null) { throw new ArgumentNullException("aggregator"); } OnError = onError ?? (error => { }); if (onError != null) { fetchAndSaveProjection = fetchAndSaveProjection.Catch(onError); } FetchAndSaveProjection = fetchAndSaveProjection ?? (async(streamId, aggregate) => { await aggregate(Activator.CreateInstance <TProjection>()); }); Aggregator = aggregator; }