IGet.GetAll 1.0.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package IGet.GetAll --version 1.0.3                
NuGet\Install-Package IGet.GetAll -Version 1.0.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="IGet.GetAll" Version="1.0.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add IGet.GetAll --version 1.0.3                
#r "nuget: IGet.GetAll, 1.0.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install IGet.GetAll as a Cake Addin
#addin nuget:?package=IGet.GetAll&version=1.0.3

// Install IGet.GetAll as a Cake Tool
#tool nuget:?package=IGet.GetAll&version=1.0.3                

IGet

Instantiate classes that are NOT in your service collection via i.Get<MyClass>(). Dependencies from your service collection are automatically injected. Get an IEnumerable of class instances (with their dependencies injected) via i.GetAll<IMyInterface>() or i.GetAll<MyBaseClass>().

Package Latest version i
IGet Nuget i.Get<Class>() or i.Get<IInterface>(reflectedClassType)
IGet.GetAll Nuget i.GetAll<IInterface>() or i.GetAll<BaseClass>()

Table of Contents

Quick setup

  1. Install via Visual Studio's NuGet Package Manager.

  2. Add IGet to your service collection via serviceCollection.AddIGet() - in a .NET Core app, this can be done in Program.cs:

builder.Services.AddIGet();
  1. Now you can use it (for example in a .NET Core web app):
public class IndexModel : PageModel
{
    private readonly IGet i;

    public IndexModel(IGet iget)
    {
        i = iget;
    }
    
    
    public void OnGet()
    {
        var data = i.Get<DataRequestHandler>().Handle();
        ...
    }
...
}
  1. If you've also installed IGet.GetAll, then add the following using statement (or add it as a global using):
using IGetAll;
  1. and add to the service collection:
serviceCollection.AddIGet();
serviceCollection.AddIGetAll(new [] { typeof(Startup).Assembly, ... });

For more examples, see below.

Why IGet?

  • you don't need to implement any interface for your handlers.
  • have compile-time checks that all handlers exist.
  • use editor shortcuts to jump to a handler's method immediately.
  • have a short StackTrace in case of an error.
  • IGet is easy to understand - this might save time and money.
  • IGet is extremely lightweight - less code often means fewer bugs.

Declaring a handler

Example 1

A method signature that fits many contexts is Task<TResult> HandleAsync(TRequest request, CancellationToken cancellationToken):

public class MyHandler
{
    private IConnectionFactory _connectionFactory;

    public MyHandler(IConnectionFactory connectionFactory)
    {
        _connectionFactory = connectionFactory;
    }

    public async Task<MyResult> HandleAsync(MyRequest request, CancellationToken cancellationToken)
    {
        ...
    }
}
Example 2

A method with a value type parameter:

public class MyHandler
{
    private IConnectionFactory _connectionFactory;

    public MyHandler(IConnectionFactory connectionFactory)
    {
        _connectionFactory = connectionFactory;
    }

    public async Task<MyResult> ChooseASignature(int id)
    {
        ...
    }
}
Example 3

Synchronous code:

public class MyHandler
{
    private ILogger<MyHandler> _logger;

    public MyHandler(ILogger<MyHandler> logger)
    {
        _logger = logger;
    }

    public void Handle()
    {
        // do something
    }
}

Using a handler

Example 1
var result = i.Get<MyHandler>().AnyRandomSignature(1);
Example 2
var handler = i.Get<MyHandler>();
handler.Handle();
Example 3
var result = await i.Get<MyHandler>().HandleAsync(new MyRequest
{
    Id = 2
});
Example 4
var result = i.Get<MyHandler>().Handle(request);

Because you get the handler via generics, your code is type-checked by the compiler - therefore you know that each request has a handler immediately. Also, you can place your cursor on the class's method and press F12 to quickly navigate to the method declaration.

More complex scenarios

Example 1

Handlers may get other handlers to do stuff for them.

Declare:

public class SubscribeRequestHandler
{
    private readonly IConnectionFactory _connectionFactory;
    private readonly IGet i;

    public SubscribeRequestHandler(IConnectionFactory connectionFactory, IGet iget)
    {
        _connectionFactory = connectionFactory;
        i = iget;
    }

    public async Task<Result> HandleAsync(SubscribeRequest request)
    {
        var validationResult = await i.Get<SubscribeRequestValidator>().ValidateAsync(request);
        if (validationResult.IsFail)
        {
            return validationResult;
        }
        using var connection = await _connectionFactory.GetConnectionAsync();
        await connection.InsertAsync(new WorkshopParticipant
        {
            Name = request.Name.Trim(),
            WorkshopId = request.WorkshopId,
        });

        return Result.Success();
    }
}

Use:

    public async Task<IActionResult> OnPost(SubscribeRequest request)
    {
        var result = await i.Get<SubscribeRequestHandler>().HandleAsync(request);
        ...
Example 2

You may want multiple handlers to have certian behaviour, for example logging their execution time. You could create a base class for (a subset of) your handlers:

public abstract class BaseHandler<THandler,TRequest, TResponse>
    where THandler : notnull
    where TRequest : notnull
{
    protected readonly ILogger<THandler> _logger;
    protected readonly IDbConnectionFactory _connectionFactory;
    protected readonly IHostEnvironment _hostEnvironment;

    public BaseHandler(IBaseHandlerServices baseHandlerServices)
    {
        _logger = baseHandlerServices.LoggerFactory.CreateLogger<THandler>();
        _connectionFactory = baseHandlerServices.ConnectionFactory;
        _hostEnvironment = baseHandlerServices.HostEnvironment;
    }
    public async Task<TResponse> HandleAsync(TRequest request, CancellationToken cancellationToken = default)
    {
        HandleBefore(request);
        var result = await HandleCoreAsync(request, cancellationToken);
        HandleAfter();
        return result;
    }

    private void HandleBefore(TRequest request)
    {
        if (!_hostEnvironment.IsProduction())
        {
            _logger.LogInformation("Start handling request {RequestMembers}.", request.ToKeyValuePairsString());
        }
        StartTime = DateTime.UtcNow;
    }
    DateTime StartTime;
    protected abstract Task<TResponse> HandleCoreAsync(
        TRequest request, 
        CancellationToken cancellationToken);
    private void HandleAfter()
    {
        var totalMilliseconds = (DateTime.UtcNow - StartTime).TotalMilliseconds;
        if (!_hostEnvironment.IsProduction() || totalMilliseconds > 500)
        {
            _logger.LogInformation("Finished in {TotalMilliseconds}ms.", totalMilliseconds);
        }
    }
}

Inherit:

public class ProductOverviewQueryHandler 
    : BaseHandler<ProductOverviewQueryHandler, Query, Result>
{
    public ProductOverviewQueryHandler(IBaseHandlerServices baseHandlerServices) 
        : base(baseHandlerServices)
    { }

    protected override async Task<Result> HandleCoreAsync(
        Query query,
        CancellationToken cancellationToken)
    {
        await using var connection = await _connectionFactory.GetOpenConnectionAsync(cancellationToken);

        ...

        return new Result
        {
            // set properties
        };
    }
}

Use:

var result = await i.Get<ProductOverviewQueryHandler>().HandleAsync(query);
Example 3

Use a try-catch structure for multiple noninterdependent handlers of the same event:

await i.Get<MyEventPublisher>().PublishAsync(myEvent);
public class MyEventPublisher
{
    private IGet i;

    public MyEventPublisher(IGet iget)
    {
        i = iget;
    }

    public async Task PublishAsync(MyEvent myEvent)
    {
        try
        {
            await i.Get<FirstHandler>().HandleAsync(myEvent);
        }
        catch { }
        try
        {
            await i.Get<SecondHandler>().HandleAsync(myEvent);
        }
        catch { }
        try
        {
            i.Get<ThirdHandler>().Handle(myEvent);
        }
        catch { }
    }
}

Notes:

  • Exceptions should be logged in the catch blocks.
  • If you find the example above too risky - because you might forget to register a newly created handler in the event publisher, then have a look at the IGet.GetAll examples below.

Why IGet.GetAll?

With i.GetAll<T>() you can get multiple handlers that implement the same interface or base class. No matter how complicated your interfaces or generic base classes are - think about IMyInterface<SomeClass, NestedBaseClass<AnotherClass, AndMore>> - no additional configuration is needed.

About IGet.GetAll's performance

Each time you use i.GetAll<T>() for a new type T, the collected Type[] is stored in a ConcurrentDictionary. The next time you call i.GetAll<T>() for the same type T, no assembly scanning is done.

i.GetAll<T>() examples

Example 1

This example shows how you can create a generic event publisher that collects the handlers for you.

Declare an interface you like:

public interface IEventHandler<TEvent>
{
    Task HandleAsync(TEvent e, CancellationToken cancellationToken);
}

Implement the interface:

public class EventA { }

public class HandlerA1 : IEventHandler<EventA>
{
    private readonly ILogger<HandlerA1> _logger;
    public HandlerA1(ILogger<HandlerA1> logger)
    {
        _logger = logger;
    }
    public async Task HandleAsync(EventA e, CancellationToken cancellationToken)
    {
        ...
    }
}

public class HandlerA2 : IEventHandler<EventA>
{
    private readonly IConnectionFactory _connectionFactory;
    public HandlerA2(IConnectionFactory connectionFactory)
    {
        _connectionFactory = connectionFactory;
    }
    public async Task HandleAsync(EventA e, CancellationToken cancellationToken)
    {
        ...
    }
}

public class EventB { }

public class HandlerB1 : IEventHandler<EventB>
{
    private readonly ILogger<Handler1> _logger;
    public HandlerB1(ILogger<Handler1> logger)
    {
        _logger = logger;
    }
    public async Task HandleAsync(EventB e, CancellationToken cancellationToken)
    {
        ...
    }
}

Create a generic event publisher for all your event types:

public class EventPublisher<TEvent> where TEvent : notnull
{
    private readonly ILogger _logger;
    private readonly IGet i;

    public EventPublisher(IGet iget, ILogger logger)
    {
        _logger = logger;
        i = iget;
    }

    public async Task Publish(TEvent e, CancellationToken cancellationToken = default)
    {
        foreach (var handler in i.GetAll<IEventHandler<TEvent>>())
        {
            try
            {
                await handler.HandleAsync(e, cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in {handlerType} for {eventKeyValuePairs}.", handler.GetType().FullName, e.ToKeyValuePairsString());
            }
        }
    }
}

Publish events:

// invokes HandlerA1 and HandlerA2:
await i.Get<EventPublisher<EventA>>().Publish(eventA);
// invokes HandlerB1:
await i.Get<EventPublisher<EventB>>().Publish(eventB);
Example 2

Note that because the EventPublisher<TEvent> of the previous example is in your own repository, you can easily tweak it. Do you want some handlers to have priority? Add a second interface IPrio to some handlers and execute those first. Do you want to fire them all first and then call Task.WhenAll? You are in control - without reading any docs:

public async Task Publish(TEvent e, CancellationToken cancellationToken = default)
{
    var handlers = i.GetAll<IEventHandler<TEvent>>();
    var prioTasks = handlers.Where(handler => handler is IPrio).Select(handler => GetSafeTask(handler));
    await Task.WhenAll(prioTasks);
    foreach (var handler in handlers.Where(handler => handler is not IPrio))
    {
        await GetSafeTask(handler);
    }

    async Task GetSafeTask(IEventHandler<TEvent> handler)
    {
        try
        {
            await handler.HandleAsync(e, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error in {handlerType} for {eventKeyValuePairs}.", handler.GetType().FullName, e.ToKeyValuePairsString());
        }
    }
}
Example 3

Just to make sure it's clear: i.GetAll<T>() can be used for any type of interface or base class. Do you need to get a set of validator classes for a certain request? Get them:

i.GetAll<AbstractValidator<UpdateUserCommand>>()

Try it out

The examples above give an idea of how you can be creative with IGet. Share your own examples online to spread the word about IGet.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.5 1,503 12/13/2023
1.0.4 5,060 4/12/2023
1.0.3 870 3/29/2023
1.0.2 213 3/21/2023
1.0.1 204 3/20/2023
1.0.0 196 3/19/2023
1.0.0-alpha 128 3/19/2023

v1.0.*: updates of the readme.