StructureMap AOP DynamicProxy - c#

I am trying to implement AOP using StructureMap and Castle.Core. I am using the latest versions.
I am able to make it with the default constructor, but what I need to inject the logger into the constructor of the IInterceptor. How can I do this.
public class IwInterceptor : IInterceptor
{
private readonly IwLogger logger;
public IwInterceptor(IwLogger logger)
{
this.logger = logger;
}
public void Intercept(IInvocation invocation)
{
logger.Debug("Entered " + invocation.Method.Name);
invocation.Proceed();
logger.Debug("Left");
}
}

Related

Using Microsoft logging extensions with typed loggers when objects are instantiated in code?

Learning how to work with Microsoft logging extensions, DI and Serilog. For controller classes, it just works, i.e. I added ILogger to the constructor, and save a local reference to the logger, and DI does the rest.
I'm not sure how to implement this for classes that are instantiated by the controller though. For instance:
public class MyController: ControllerBase
{
private readonly ILogger<MyController> _logger;
public MyController(ILogger<MyController> logger) ( _logger = logger; }
public bool DoWork()
{
MyWorker w = new MyWorker(???);
w.DoSomething();
}
}
public class MyWorker
{
private readonly ILogger<MyWorker> _logger;
public MyWorker(ILogger<MyWorker> logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.Debug("DoSomething");
}
}
What is this code supposed to look like when instantiating the MyWorker so it will work with DI?

Serilog in Azure Functions

Each method in Azure Functions can have a Microsoft.Extensions.Logging.ILogger injected into it for logging. Using WebJobsStartup with a startup class you can change the logging to use Serilog using the following syntax:
[assembly: WebJobsStartup(typeof(Startup))]
namespace MyFuncApp {
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.Services.AddLogging(
lb => lb.ClearProviders()
.AddSerilog(
new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(#"C:\Temp\MyFuncApp.log")
.CreateLogger(),
true));
}
}
}
I can also add other objects to the DI and inject them either into the methods or into the constructor for the class containing the methods using i.e. builder.Services.AddSingleton<IMyInterface, MyImplementation>();
However, I would very much like to be able to inject the Microsoft.Extensions.Logging.ILogger in the same way, but if I try to use the ILogger in the constructor I get the following error during method invokation (as that's when the class is created):
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'MyFuncApp.MyFunctions'.
So, is there any way of injecting the ILogger into a class constructor like this?
public class MyFunctions
{
private IMyInterface _myImpl;
private ILogger _log;
public MyFunctions(
IMyInterface myImplememtation, // This works
ILogger log) // This does not
{
_myImpl = myImplementation;
_log = log;
_log.LogInformation("Class constructed");
}
public async Task<IActionResult> Function1([HttpTrigger() ... ) {
_log.LogInformation("Function1 invoked");
}
}
Please try the code below, it works at my side:
[assembly: WebJobsStartup(typeof(Startup))]
namespace MyApp
{
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
//other code
builder.Services.AddLogging();
}
}
public class Functions
{
//other code
private ILogger _log;
public Functions(ILoggerFactory loggerFactory)
{
_log = loggerFactory.CreateLogger<Functions>();
}
[FunctionName("Token")]
public async Task<IActionResult> Function1(
[HttpTrigger()]...)
{
_log.LogInformation("Function1 invoked");
}
}
}
It is possible to further simplify the necessary setup by using the package Anotar.Serilog.Fody (and any other Anotar package for that matter)
You need to set up Serilog all the same in the Startup class.
However, with the Fody package you can completely get rid of the injected logger
using Anotar.Serilog;
public class Functions
{
[FunctionName("Token")]
public async Task<IActionResult> Function1(
[HttpTrigger()]...)
{
// static calls to the LogTo class
// get translated into proper Serilog code during build
LogTo.Information("Function1 invoked");
}
}
With AzureFunctions v3, the pattern you outlined in your question works out-of-the box.

How Interface is resolved in MvvmCross

In .Core project I have an interface for logging:
public interface ILogger
{
void Trace(string format, params object[] args);
.........
void Fatal(string format, params object[] args);
}
Which is used in a log service interface:
public interface ILogService
{
ILogger GetLogger(string name);
ILogger GetLogger(Type typeName);
ILogger GetLogger<T>();
}
In .Droid project that interface is implemented:
public class AndroidLogService : ILogService
{
public ILogger GetLogger(string name)
{
return new AndroidLogger(name);
}
public ILogger GetLogger(Type typeName)
{
return GetLogger(typeName.Name);
}
public ILogger GetLogger<T>()
{
return GetLogger(typeof(T));
}
}
In .Droid Setup.cs file AndroidLogService is registered:
Mvx.LazyConstructAndRegisterSingleton<ILogService, AndroidLogService>();
Mvx.LazyConstructAndRegisterSingleton<IFileService, AndroidFileService>();
and finally used in some file in .Droid project:
private readonly ILogger _logger;
public AndroidFileService(IContextService contextService, ILogService logService, IEncryptionService encryptionService)
{
_logger = logService.GetLogger<AndroidFileService>();
.......
}
Finally logs work like this:
_logger.Warn("Failed to retrieve logo. Error: {0}", ex);
My doubts and questions:
AndroidFileService is never called with params that described above but from MvvmCross docs I've read that it's called Construction Injection
Ok, I understand this part but one thing is dark for me:
Where ILogger implementation exists?
I didn't find in the whole solution any part with something like
Mvx.RegisterType<ILogger , SomeLogger>();
How it can be? What a mechanism is used to register ILogger?
I found the answer.
I went inside AndroidLogService in Setup.cs:
public class AndroidLogService : ILogService
{
public ILogger GetLogger(string name)
{
return new AndroidLogger(name);
}
public ILogger GetLogger(Type typeName)
{
return GetLogger(typeName.Name);
}
public ILogger GetLogger<T>()
{
return GetLogger(typeof(T));
}
}
Then I went inside AndroidLogger:
using NLog;
using ILogger = ....Services.Support.ILogger;//from .Core project
namespace ....Android.Services.Support//from .Droid project
{
public class AndroidLogger : ILogger
{
private readonly NLog.ILogger _logger;
public AndroidLogger(string name)
{
_logger = LogManager.GetLogger(name);
}
.................
where I see that is used NLog which is built-in implementation of ILogger.
So, when the build was created for android I inserted this snippet in 2 files: one from .Core project and another from .Droid
_logger = logService.GetLogger<SomeViewModel>();
String name = logService.GetType().Name;
_logger.Debug("LogService name = {0} ", name);
which resulted for both cases as LogService name = AndroidLogService.
Before this check I thought that as .Core project doesn't have any reference to .Droid project, so, there are different implementations for them, but I was wrong:
Interface implemented in .Droid/.iOs project also works for .Core project.
Try this, if ILogger has an implementation in MvvmCross you would get the object populated.
var logger = Mvx.Resolve<ILogger>();

Dependency Injection into Entity Class

Using Asp.Net Core we can make use of Dependency Injection in controllers/repositories.
However, I wish do do some logging in my Entity Class.
class Person
{
private ILogger<Person> _logger;
private List<Pets> pets;
public Person(ILogger<Person> logger)
{
_logger = logger;
}
public bool HasCat()
{
_logger.LogTrace("Checking to see if person has a cat.");
// logic to determine cat ownership
hasCat = true;
return hasCat;
}
}
When the Person class is instantiated by EntityFramework it does not attempt to inject any dependencies.
Can I force this? Am i going about it in completely the wrong way?
Ultimatley I just want to be able to use logging consistently throughout the application.
Thanks,
It is possible but I don't recommend it because I agree with commenters that logging belongs in your services and controllers.
EF Core 2.1 allows injecting the DbContext into a private constructor that EF will invoke. See the official docs.
First you need to expose a LoggerFactory property in your DbContext class.
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options, ILoggerFactory loggerFactory = null)
{
LoggerFactory = loggerFactory;
}
public ILoggerFactory LoggerFactory { get; }
}
Then you can inject the DbContext into a private constructor in your entity class.
public class Person
{
private readonly ILogger _logger;
public Person() { } // normal public constructor
private Person(MyDbContext db) // private constructor that EF will invoke
{
_logger = db.LoggerFactory?.CreateLogger<Person>();
}
public bool HasCat()
{
_logger?.LogTrace("Check has cat");
return true;
}
}

Serilog's ILogger injected using Log.ForContext<T>, where T is the consumer

Serilog allows creating a context-aware logger:
Log.ForContext<T>()
I would like to register Serilog with SimpleInjector in such a way that T is the type of the consumer, i.e. it is which class it is injected in to.
e.g.
public class Car
{
public Car(ILogger logger) <= would be injected using Log.ForContext<Car>()
{
}
}
I can see this has been done with AutoFac.
And looking through the SimpleInjector documentation, there is a very promising overload of RegisterConditional() (with the Func<TypeFactoryContext, Type> parameter).
c.RegisterConditional(typeof (ILogger),
x => Log.ForContext(x.Consumer.ImplementationType), <= won't compile as expecting a Type
Lifestyle.Scoped,
x => true);
however, I don't want to tell SimpleInjector which Type to build, but how to build one.
I have integrated Serilog with Simple Injector with the following code based on #Steven genius answer on StackOverflow: logger wrapper best practice
public interface ILogger
{
void Log(LogEntry entry);
}
public class SerilogLogger<T> : ILogger
{
private readonly Serilog.ILogger _logger;
public SerilogLogger()
{
_logger = new LoggerConfiguration()
.WriteTo
.Trace(LogEventLevel.Information)
.CreateLogger()
.ForContext(typeof (T));
}
public void Log(LogEntry entry)
{
/* Logging abstraction handling */
}
}
public static class ContainerExtensions {
public static void RegisterLogging(this Container container)
{
container.RegisterConditional(
typeof(ILogger),
c => typeof(SerilogLogger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
}
}
In your Composition Root:
var container = new Container();
container.RegisterLogging();

Categories