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

Add interfaces for the ActorRegistry to allow mocking in tests #42

68 changes: 67 additions & 1 deletion src/Akka.Hosting/ActorRegistry.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Akka.Actor;
Expand All @@ -24,7 +25,7 @@ public override ActorRegistry CreateExtension(ExtendedActorSystem system)
///
/// If you are adding every single actor in your <see cref="ActorSystem"/> to the registry you are definitely using it wrong.
/// </remarks>
public class ActorRegistry : IExtension
public class ActorRegistry : IActorRegistry, IExtension
{
private readonly ConcurrentDictionary<Type, IActorRef> _actorRegistrations =
new ConcurrentDictionary<Type, IActorRef>();
Expand Down Expand Up @@ -105,10 +106,75 @@ public IEnumerator<KeyValuePair<Type, IActorRef>> GetEnumerator()
{
return _actorRegistrations.GetEnumerator();
}
/// <summary>
/// Allows enumerated access to the collection of all registered actors.
/// </summary>
/// <returns></returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public static ActorRegistry For(ActorSystem actorSystem)
{
return actorSystem.WithExtension<ActorRegistry, ActorRegistryExtension>();
}
}

/// <summary>
/// Represents a read-only collection of <see cref="IActorRef"/> instances keyed by the actor name.
/// </summary>
public interface IReadOnlyActorRegistry : IEnumerable<KeyValuePair<Type, IActorRef>>
{
/// <summary>
/// Try to retrieve an <see cref="IActorRef"/> with the given <see cref="TKey"/>.
/// </summary>
/// <param name="actor">The bound <see cref="IActorRef"/>, if any. Is set to <see cref="ActorRefs.Nobody"/> if key is not found.</param>
/// <returns><c>true</c> if an actor with this key exists, <c>false</c> otherwise.</returns>
bool TryGet<TKey>(out IActorRef actor);

/// <summary>
/// Try to retrieve an <see cref="IActorRef"/> with the given <see cref="TKey"/>.
/// </summary>
/// <param name="key">The key for a particular actor.</param>
/// <param name="actor">The bound <see cref="IActorRef"/>, if any. Is set to <see cref="ActorRefs.Nobody"/> if key is not found.</param>
/// <returns><c>true</c> if an actor with this key exists, <c>false</c> otherwise.</returns>
bool TryGet(Type key, out IActorRef actor);

/// <summary>
/// Fetches the <see cref="IActorRef"/> by key.
/// </summary>
/// <typeparam name="TKey">The key type to retrieve this actor.</typeparam>
/// <returns>If found, the underlying <see cref="IActorRef"/>.
/// If not found, returns <see cref="ActorRefs.Nobody"/>.</returns>
IActorRef Get<TKey>();
}

/// <summary>
/// An abstraction to allow <see cref="IActorRef"/> instances to be injected to non-Akka classes (such as controllers and SignalR Hubs).
/// </summary>
/// <remarks>
/// Should only be used for top-level actors that need to be accessed from inside or outside the <see cref="ActorSystem"/>.
///
/// If you are adding every single actor in your <see cref="ActorSystem"/> to the registry you are definitely using it wrong.
/// </remarks>
public interface IActorRegistry: IReadOnlyActorRegistry
{
/// <summary>
/// Attempts to register an actor with the registry.
/// </summary>
/// <param name="actor">The bound <see cref="IActorRef"/>, if any. Is set to <see cref="ActorRefs.Nobody"/> if key is not found.</param>
/// <param name="overwrite">If <c>true</c>, allows overwriting of a previous actor with the same key. Defaults to <c>false</c>.</param>
/// <returns><c>true</c> if the actor was set to this key in the registry, <c>false</c> otherwise.</returns>
bool TryRegister<TKey>(IActorRef actor, bool overwrite = false);

/// <summary>
/// Attempts to register an actor with the registry.
/// </summary>
/// <param name="key">The key for a particular actor.</param>
/// <param name="actor">The bound <see cref="IActorRef"/>, if any. Is set to <see cref="ActorRefs.Nobody"/> if key is not found.</param>
/// <param name="overwrite">If <c>true</c>, allows overwriting of a previous actor with the same key. Defaults to <c>false</c>.</param>
/// <returns><c>true</c> if the actor was set to this key in the registry, <c>false</c> otherwise.</returns>
bool TryRegister(Type key, IActorRef actor, bool overwrite = false);
}
}
18 changes: 14 additions & 4 deletions src/Akka.Hosting/AkkaConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public enum HoconAddMode
/// <summary>
/// Delegate used to instantiate <see cref="IActorRef"/>s once the <see cref="ActorSystem"/> has booted.
/// </summary>
public delegate Task ActorStarter(ActorSystem system, ActorRegistry registry);
public delegate Task ActorStarter(ActorSystem system, IActorRegistry registry);

/// <summary>
/// Used to help populate a <see cref="SerializationSetup"/> upon starting the <see cref="ActorSystem"/>,
Expand Down Expand Up @@ -166,9 +166,9 @@ internal AkkaConfigurationBuilder AddHoconConfiguration(Config newHocon,
}
}

private static ActorStarter ToAsyncStarter(Action<ActorSystem, ActorRegistry> nonAsyncStarter)
private static ActorStarter ToAsyncStarter(Action<ActorSystem, IActorRegistry> nonAsyncStarter)
{
Task Starter(ActorSystem f, ActorRegistry registry)
Task Starter(ActorSystem f, IActorRegistry registry)
{
nonAsyncStarter(f, registry);
return Task.CompletedTask;
Expand All @@ -177,7 +177,7 @@ Task Starter(ActorSystem f, ActorRegistry registry)
return Starter;
}

public AkkaConfigurationBuilder StartActors(Action<ActorSystem, ActorRegistry> starter)
public AkkaConfigurationBuilder StartActors(Action<ActorSystem, IActorRegistry> starter)
{
if (_complete) return this;
_actorStarters.Add(ToAsyncStarter(starter));
Expand Down Expand Up @@ -210,6 +210,16 @@ internal void Bind()
{
return ActorRegistry.For(sp.GetRequiredService<ActorSystem>());
});

ServiceCollection.AddSingleton<IActorRegistry>(sp =>
{
return sp.GetRequiredService<ActorRegistry>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

});

ServiceCollection.AddSingleton<IReadOnlyActorRegistry>(sp =>
{
return sp.GetRequiredService<ActorRegistry>();
});
}

private static Func<IServiceProvider, ActorSystem> ActorSystemFactory()
Expand Down
2 changes: 1 addition & 1 deletion src/Akka.Hosting/AkkaHostingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static AkkaConfigurationBuilder WithActorRefProvider(this AkkaConfigurati
/// <param name="actorStarter">A <see cref="ActorStarter"/> delegate
/// for configuring and starting actors.</param>
/// <returns>The same <see cref="AkkaConfigurationBuilder"/> instance originally passed in.</returns>
public static AkkaConfigurationBuilder WithActors(this AkkaConfigurationBuilder builder, Action<ActorSystem, ActorRegistry> actorStarter)
public static AkkaConfigurationBuilder WithActors(this AkkaConfigurationBuilder builder, Action<ActorSystem, IActorRegistry> actorStarter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

{
return builder.StartActors(actorStarter);
}
Expand Down