Use Producer/Consumer
type classes without creating Producer classes just to call Consumer classes.
- Create your implementations
- Register them under the same interface
- Inject in AllOf and use that to send a 'Publish' command.
If you like this library, consider buying me a coffee.
Reduce:
public class MyImplementation1 : IMyInterface { ... }
public class MyImplementation2 : IMyInterface { ... }
public class MyImplementation3 : IMyInterface { ... }
public interface IMyPublisher
{
void PublishSomething();
Task PublishSomethingAsync();
}
public class MyPublisher : IMyPublisher
{
private readonly IEnumerable<IMyInterface> _myInterfaces;
public MyPublisher(IEnumerable<IMyInterface> myInterfaces)
{
_myInterfaces = myInterfaces;
}
public void PublishSomething()
{
foreach(var myInterface in _myInterfaces)
{
myInterface.DoSomething();
}
}
public async Task PublishSomethingAsync()
{
var tasks = _myInterfaces.Select(myInterface => myInterface.DoSomethingAsync());
await Task.WhenAll(tasks);
}
}
public class MyWorker
{
private readonly IMyPublisher _myPublisher;
public MyWorker(IMyPublisher _myPublisher)
{
_myPublisher = myPublisher;
}
public async Task DoSomething()
{
...
_myPublisher.PublishSomething();
await _myPublisher.PublishSomethingAsync();
}
}
To
public class MyImplementation1 : IMyInterface { ... }
public class MyImplementation2 : IMyInterface { ... }
public class MyImplementation3 : IMyInterface { ... }
public class MyWorker
{
private readonly AllOf<IMyInterface> _myInterfaces;
public MyWorker(AllOf<IMyInterface> myInterfaces)
{
_myInterfaces = myInterfaces;
}
public async Task DoSomething()
{
...
_myInterfaces.OnEach().DoSomething();
await _myInterfaces.OnEach().DoSomethingAsync();
}
}
It may not seem like much, but it eliminates an entire class. It also means you don't need to handle the looping and Task (if async) management on these methods.
- Register multiple implementations of your interfaces(s) in your ServiceCollection
services.AddSingleton<IMyInterface, MyImplementation1>()
.AddScoped<IMyInterface, MyImplementation2>()
.AddTransient<IMyInterface, MyImplementation3>();
- Call
AddAllOfs()
on your ServiceCollection
services.AddAllOfs()
- Inject
AllOf<T>
into your class
public class MyWorker
{
private readonly AllOf<IMyInterface> _myInterfaces;
public MyWorker(AllOf<IMyInterface> myInterfaces)
{
_myInterfaces = myInterfaces;
}
}
- Call
AllOf.OnEach().SomeMethod()
and it'll call the same method in all of the different implementations. This handles asynchronous Tasks as well as synchronous methods, so no loop or Task handling for you to implement.
_myInterfaces.OnEach().DoSomething();
await _myInterface.OnEach().DoSomethingElseAsync();
The above will essentially do:
MyImplementation1.DoSomething();
MyImplementation2.DoSomething();
MyImplementation3.DoSomething();
await Task.WhenAll(
MyImplementation1.DoSomethingElseAsync(),
MyImplementation2.DoSomethingElseAsync(),
MyImplementation3.DoSomethingElseAsync()
);
AllOf<>
is an interface so can be easily mocked. As well as OnEach()
, it holds an Items
property which is an IEnumerable<T>
if you need to access your enumerable of implementations.
If you want to change the naming, create a wrapper class around it and register it in the DI. E.g.
public interface PublisherOf<out T>
{
T ForEachSubscriber();
}
public class PublisherOfImpl<T> : PublisherOf<T>
{
private readonly AllOf<T> _allOf;
public PublisherOfImpl(AllOf<T> allOf)
{
_allOf = allOf;
}
public T ForEachSubscriber()
{
return _allOf.OnEach();
}
}
in Startup do
services.AddTransient(typeof(PublisherOf<>), typeof(PublisherOfImpl<>))
And then you can inject this type into your classes, if that reads better for your codebase.
public class MyLoginService
{
public MyLoginService(PublisherOf<ICustomerLoggedInEvent> customerLoggedInEventPublisher)
{
_customerLoggedInEventPublisher = customerLoggedInEventPublisher;
}
public void DoStuff()
{
_customerLoggedInEventPublisher.ForEachSubscriber().DoThis();
}
}