IGet 1.1.5
See the version list below for details.
dotnet add package IGet --version 1.1.5
NuGet\Install-Package IGet -Version 1.1.5
<PackageReference Include="IGet" Version="1.1.5" />
paket add IGet --version 1.1.5
#r "nuget: IGet, 1.1.5"
// Install IGet as a Cake Addin #addin nuget:?package=IGet&version=1.1.5 // Install IGet as a Cake Tool #tool nuget:?package=IGet&version=1.1.5
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 | i.Get<Class>() or i.Get<IInterface>(reflectedClassType) |
|
IGet.GetAll | i.GetAll<IInterface>() or i.GetAll<BaseClass>() |
Table of Contents
Quick setup
Install via Visual Studio's NuGet Package Manager.
Add
IGet
to your service collection viaserviceCollection.AddIGet()
- in a .NET Core app, this can be done in Program.cs:
builder.Services.AddIGet();
- 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();
...
}
...
}
- If you've also installed IGet.GetAll, then add the following using statement (or add it as a global using):
using IGetAll;
- 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 | Versions 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. |
-
.NETStandard 2.0
NuGet packages (1)
Showing the top 1 NuGet packages that depend on IGet:
Package | Downloads |
---|---|
IGet.GetAll
Extends IGet. Get an IEnumerable of class instances (with their dependencies injected) via i.GetAll<IMyInterface>() or i.GetAll<MyBaseClass>(). Additional setup: serviceCollection.AddIGetAll(new [] { typeof(Startup).Assembly, ... }). See the readme for many examples. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.1.8 | 17,042 | 12/13/2023 |
1.1.7 | 132 | 12/13/2023 |
1.1.6 | 5,160 | 4/12/2023 |
1.1.5 | 969 | 3/29/2023 |
1.1.4 | 296 | 3/21/2023 |
1.1.3 | 296 | 3/20/2023 |
1.1.2 | 189 | 3/19/2023 |
1.1.1 | 325 | 3/16/2023 |
1.1.0 | 199 | 3/15/2023 |
1.0.14 | 197 | 3/15/2023 |
1.0.13 | 208 | 3/14/2023 |
1.0.12 | 206 | 3/14/2023 |
1.0.11 | 209 | 3/14/2023 |
1.0.10 | 207 | 3/14/2023 |
1.0.9 | 211 | 3/13/2023 |
1.0.8 | 214 | 3/13/2023 |
1.0.7 | 204 | 3/13/2023 |
1.0.6 | 193 | 3/13/2023 |
1.0.5 | 204 | 3/12/2023 |
1.0.4 | 221 | 3/12/2023 |
1.0.3 | 194 | 3/11/2023 |
1.0.2 | 210 | 3/10/2023 |
1.0.1 | 226 | 3/10/2023 |
1.0.0 | 226 | 3/10/2023 |
1.0.0-alpha | 146 | 3/10/2023 |
v1.0.*: updates of the readme.
v1.1.0: add T Get<T>(Type type).
v1.1.*: updates of the readme.