Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure consumer when adding #164

Merged
merged 3 commits into from
May 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 24 additions & 18 deletions src/Tingle.EventBus/DependencyInjection/EventBusBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,9 @@ public EventBusBuilder UseDefaultSerializer<TEventSerializer>() where TEventSeri
/// Subscribe to events that a consumer can listen to.
/// </summary>
/// <typeparam name="TConsumer">The type of consumer to handle the events.</typeparam>
/// <param name="lifetime">
/// The lifetime to use when resolving and maintaining instances of <typeparamref name="TConsumer"/>.
/// Using <see cref="ServiceLifetime.Transient"/> is best because a clean instance is created each
/// time a message is received and has a high level of isolation hence avoiding leackage of dependencies.
/// However, it can result in high memory usage making <see cref="ServiceLifetime.Scoped"/> the
/// middleground. Scoped instances are resued for each message received in a batch of messages so long
/// as they are processed sequencially.
/// <br />
/// <br />
/// These decisions do not apply in all scenarios and should be reconsidered depending on the
/// design of <typeparamref name="TConsumer"/>.
/// </param>
/// <param name="configure"></param>
/// <returns></returns>
public EventBusBuilder AddConsumer<TConsumer>(ServiceLifetime lifetime = ServiceLifetime.Scoped) where TConsumer : class, IEventConsumer
public EventBusBuilder AddConsumer<TConsumer>(Action<EventRegistration, EventConsumerRegistration> configure) where TConsumer : class, IEventConsumer
{
var consumerType = typeof(TConsumer);
if (consumerType.IsAbstract)
Expand All @@ -149,7 +138,7 @@ public EventBusBuilder AddConsumer<TConsumer>(ServiceLifetime lifetime = Service
}

// register the consumer for resolution
Services.Add(ServiceDescriptor.Describe(consumerType, consumerType, lifetime));
Services.AddScoped(consumerType);

var genericConsumerType = typeof(IEventConsumer<>);
var eventTypes = new List<Type>();
Expand Down Expand Up @@ -181,18 +170,35 @@ public EventBusBuilder AddConsumer<TConsumer>(ServiceLifetime lifetime = Service
{
foreach (var et in eventTypes)
{
// get or create a simple registration
if (!options.Registrations.TryGetValue(et, out var registration))
// get or create a simple EventRegistration
if (!options.Registrations.TryGetValue(et, out var ereg))
{
registration = options.Registrations[et] = new EventRegistration(et);
ereg = options.Registrations[et] = new EventRegistration(et);
}

// create a ConsumerRegistration
var ecr = new EventConsumerRegistration(consumerType: consumerType);

// call the configuration function
configure?.Invoke(ereg, ecr);

// add the consumer to the registration
registration.Consumers.Add(new EventConsumerRegistration(consumerType: consumerType));
ereg.Consumers.Add(ecr);
}
});
}

/// <summary>
/// Subscribe to events that a consumer can listen to.
/// </summary>
/// <typeparam name="TConsumer">The type of consumer to handle the events.</typeparam>
/// <param name="configure"></param>
/// <returns></returns>
public EventBusBuilder AddConsumer<TConsumer>(Action<EventConsumerRegistration> configure = null) where TConsumer : class, IEventConsumer
{
return AddConsumer<TConsumer>((ereg, creg) => configure?.Invoke(creg));
}

/// <summary>
/// Unsubscribe to events that a consumer can listen to.
/// </summary>
Expand Down
54 changes: 45 additions & 9 deletions src/Tingle.EventBus/DependencyInjection/EventBusOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,23 +154,44 @@ public EventRegistration GetOrCreateRegistration<TEvent>()
/// </summary>
/// <typeparam name="TEvent">The event type from wich to retrieve a <see cref="EventConsumerRegistration"/> for.</typeparam>
/// <typeparam name="TConsumer">The consumer to configure.</typeparam>
/// <param name="registration">
/// <param name="ereg">
/// When this method returns, contains the event registration associated with the specified event type,
/// if the event type is found; otherwise, <see langword="null"/> is returned.
/// This parameter is passed uninitialized.
/// </param>
/// <param name="creg">
/// When this method returns, contains the consumer registration associated with the specified event type,
/// if the event type is found; otherwise, <see langword="null"/> is returned.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true" /> if there's a consumer registered for the given event type; otherwise, false.</returns>
public bool TryGetConsumerRegistration<TEvent, TConsumer>(out EventConsumerRegistration registration)
internal bool TryGetConsumerRegistration<TEvent, TConsumer>(out EventRegistration ereg, out EventConsumerRegistration creg)
{
registration = default;
if (Registrations.TryGetValue(typeof(TEvent), out var ereg))
creg = default;
if (Registrations.TryGetValue(typeof(TEvent), out ereg))
{
registration = ereg.Consumers.SingleOrDefault(cr => cr.ConsumerType == typeof(TConsumer));
if (registration != null) return true;
creg = ereg.Consumers.SingleOrDefault(cr => cr.ConsumerType == typeof(TConsumer));
if (creg != null) return true;
}
return false;
}

/// <summary>
/// Get the consumer registration in a given event type.
/// </summary>
/// <typeparam name="TEvent">The event type from wich to retrieve a <see cref="EventConsumerRegistration"/> for.</typeparam>
/// <typeparam name="TConsumer">The consumer to configure.</typeparam>
/// <param name="registration">
/// When this method returns, contains the consumer registration associated with the specified event type,
/// if the event type is found; otherwise, <see langword="null"/> is returned.
/// This parameter is passed uninitialized.
/// </param>
/// <returns><see langword="true" /> if there's a consumer registered for the given event type; otherwise, false.</returns>
public bool TryGetConsumerRegistration<TEvent, TConsumer>(out EventConsumerRegistration registration)
{
return TryGetConsumerRegistration<TEvent, TConsumer>(out _, out registration);
}

/// <summary>
/// Configure the <see cref="EventRegistration"/> for <typeparamref name="TEvent"/>.
/// </summary>
Expand All @@ -194,17 +215,32 @@ public EventBusOptions ConfigureEvent<TEvent>(Action<EventRegistration> configur
/// <typeparam name="TConsumer">The consumer to configure.</typeparam>
/// <param name="configure"></param>
/// <returns></returns>
public EventBusOptions ConfigureConsumer<TEvent, TConsumer>(Action<EventConsumerRegistration> configure)
public EventBusOptions ConfigureConsumer<TEvent, TConsumer>(Action<EventRegistration, EventConsumerRegistration> configure)
where TConsumer : class, IEventConsumer
{
if (configure is null) throw new ArgumentNullException(nameof(configure));

if (TryGetConsumerRegistration<TEvent, TConsumer>(out var registration))
if (TryGetConsumerRegistration<TEvent, TConsumer>(out var ereg, out var creg))
{
configure(registration);
configure(ereg, creg);
}

return this;
}

/// <summary>
/// Configure the <see cref="EventConsumerRegistration"/> for <typeparamref name="TConsumer"/>.
/// </summary>
/// <typeparam name="TEvent">The event in the consumer to configure for.</typeparam>
/// <typeparam name="TConsumer">The consumer to configure.</typeparam>
/// <param name="configure"></param>
/// <returns></returns>
public EventBusOptions ConfigureConsumer<TEvent, TConsumer>(Action<EventConsumerRegistration> configure)
where TConsumer : class, IEventConsumer
{
if (configure is null) throw new ArgumentNullException(nameof(configure));

return ConfigureConsumer<TEvent, TConsumer>((ereg, creg) => configure(creg));
}
}
}