Closed pamswam closed 2 years ago
Hi @pamswam,
Ok let's see what options we have:
Option 1: Create and manage logger inside aspect:
This is quite easy provided you use global aspects. Global aspects are singletons by themselves. So everything you create in their constructor are singletons as well.
[Aspect(Scope.Global)]
public class LogAspect : Attribute
{
private readonly TelemetryClient_logger;
public LogAspect(ILogger logger)
{
_logger = new TelemetryClient();
}
...
You can still use Scope.PerInstance
but then make sure you manage your instance TelemetryClient properly (e.g. create one in static constructor of an Aspect)
Option 2: Manage runtime objects with IoC container and inject all you need trough constructor:
You need a static class with a static method:
public static object GetInstance(Type type)
Then tell your aspect to use this class as a factory (use can use Global scope as well):
[Aspect(Scope.PerInstance, Factory = typeof(ApplicationServices))]
public class LogAspect
Then request TelemetryClient from constructor
private readonly TelemetryClient _logger;
public LogAspect(TelemetryClient logger)
{
_logger = logger;
}
Then you can use any IoC container to setup your services, in example below I use Microsoft.Extensions.DependencyInjection. It requires all services to be registered, so I had to register both LogAspect
and TelemetryClient
.AddTransient<LogAspect>()
.AddSingleton<TelemetryClient>()
One thing to pay attention to is since Aspect has constructor injection, you can't have it as Attribute. so you need a separate Attribute like this:
[Injection(typeof(LogAspect))]
public class LogAttribute : Attribute
{
}
Full code below (you can copy/paste to experiment yourself):
public static class ApplicationServices
{
public static readonly ServiceProvider ServiceProvider;
static ApplicationServices()
{
ServiceProvider = new ServiceCollection()
.AddTransient<LogAspect>()
.AddSingleton<TelemetryClient>()
.BuildServiceProvider();
}
public static object GetInstance(Type type) => ServiceProvider.GetRequiredService(type);
}
class Program
{
static void Main(string[] args)
{
new TestClass().Do();
}
}
[Log]
public class TestClass
{
public void Do() { }
}
[Injection(typeof(LogAspect))]
public class LogAttribute : Attribute
{
}
[Aspect(Scope.PerInstance, Factory = typeof(ApplicationServices))]
public class LogAspect
{
private readonly TelemetryClient _logger;
public LogAspect(TelemetryClient logger)
{
_logger = logger;
}
[Advice(Kind.Before)]
public void LogEnter([Argument(Source.Name)] string name)
{
_logger.TrackEvent($"Calling '{name}' method...");
}
}
Awesome! Will give a try! Thank you for the super quick response and thanks a bunch for this awesome library!
Thanks for using it, it encourages me to develop it further. Feel free to share your questions, feedback and feature requests!
I started with UniversalWrapper sample with logger injected as above. But in static method WrapAsync I cannot use not static variable. Sure I am missing something... Are statics in UniversalWrapper mandatory?
@GioviQ , statics aren't mandatory at all, feel free to make them instance methods.
There are a few mandatory things in AspectInjector e.g. 'Around advice should return object', but these limitations are enforced by both compiler and roslyn analyzer. So as a rule - if it compiles successfully - it is valid code for aspect injector :)
Thank you @pamidur it's working now. See https://github.com/enkodellc/blazorboilerplate/blob/development/src/Server/BlazorBoilerplate.Server/Aop/ApiResponseExceptionAspect.cs
Maybe it can be written with less code.
@GioviQ Looks good to me. Currenty I am afraid this is least possible amount of code, although we have plans for generic Around advices for future releases.
Moved here #166 and see also #148 Feel free to reopen if needed
Hi there, Please can you show some light on how to pass runtime objects (or create and manage logger inside the aspect, ideally a singleton)
Writing to Console.Writeline works, however in production, writing to console is not sufficient.
Many thanks!