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>();
Related
I'd like to access IWebHostEnvironment.WebRootPath anywhere in the asp.net core mvc application. For instance, some random class deep in the class hierarchy. Is there a static class or some other method to do so?
I am aware that I can inject IWebHostEnvironment or that I can cache the value on the startup of the application. My question is strictly about accessing it without these methods.
I am aware that I can inject IWebHostEnvironment or that I can cache the value on the startup of the application. My question is strictly about accessing it without these methods.
No, you cannot. There's no static built in here with access to this information. You can create your own though.
You can achieve this y doing the following
In your Shared project or common project which is reference by the Web project add the below interface
public interface IApplicationContext
{
public string BaseUrl { get; }
}
Then, in the web project add below code
public sealed class ApplicationContext : IApplicationContext
{
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly IHttpContextAccessor _httpContextAccessor;
public ApplicationContext(IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor)
{
_webHostEnvironment = webHostEnvironment;
_httpContextAccessor = httpContextAccessor;
}
public string BaseUrl
{
get
{
var baseUrl = _webHostEnvironment.IsDevelopment() ? AppConstants.BaseUrl.FELocalHostBaseUrl :
_httpContextAccessor.HttpContext?.Request.BaseUrl();
return baseUrl!;
}
}
}
Then, in you need to configure the dependency injection in your Startup.cs or any where that you configure DI as below
services.AddHttpContextAccessor();
services.AddScoped<IApplicationContext, ApplicationContext>();
Then you can inject the IApplicationContext in any service class constructor and access the baseUrl like below
public sealed class SecurityService
{
private readonly IApplicationContext _applicationContext;
public SecurityService(IApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
public async Task<ResponseResult> SendResetPasswordEmail(ForgotPasswordModel forgotPasswordModel, CancellationToken cancellationToken)
{
var baseUrl = _applicationContext.BaseUrl;
return new ResponseResult();
}
}
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.
I need the ASP.Net Core dependency injection to pass some parameters to the constructor of my GlobalRepository class which implements the ICardPaymentRepository interface.
The parameters are for configuration and come from the config file and the database, and I don't want my class to go and reference the database and config itself.
I think the factory pattern is the best way to do this but I can't figure out the best way to use a factory class which itself has dependencies on config and database.
My startup looks like this currently:
public class Startup
{
public IConfiguration _configuration { get; }
public IHostingEnvironment _environment { get; }
public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
_configuration = configuration;
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IDbRepository, DbRepository>();
var connection = _configuration.GetConnectionString("DbConnection");
services.Configure<ConnectionStrings>(_configuration.GetSection("ConnectionStrings"));
services.AddDbContext<DbContext>(options => options.UseSqlServer(connection));
services.AddScoped<ICardPaymentRepository, GlobalRepository>();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IRFDbRepository rFDbRepository)
{
...
}
}
The GlobalRepository constructor looks like this:
public GlobalRepository(string mode, string apiKey)
{
}
How do I now pass the mode from configuration and the apiKey from the DbRepository into the constructor from Startup?
Use the factory delegate overload when registering the repository
//...
string mode = "get value from config";
services.AddScoped<ICardPaymentRepository, GlobalRepository>(sp => {
IDbRepository repo = sp.GetRequiredService<IDbRepository>();
string apiKey = repo.GetApiKeyMethodHere();
return new GlobalRepository(mode, apiKey);
});
//...
Alternative using ActivatorUtilities.CreateInstance
//...
string mode = "get value from config";
services.AddScoped<ICardPaymentRepository>(sp => {
IDbRepository repo = sp.GetRequiredService<IDbRepository>();
string apiKey = repo.GetApiKeyMethodHere();
return ActivatorUtilities.CreateInstance<GlobalRepository>(sp, mode, apiKey);
});
//...
You might want to also check these links...
https://github.com/Microsoft/AspNetCoreInjection.TypedFactories
https://espressocoder.com/2018/10/08/injecting-a-factory-service-in-asp-net-core/
With regard to the last link the code is basically:
public class Factory<T> : IFactory<T>
{
private readonly Func<T> _initFunc;
public Factory(Func<T> initFunc)
{
_initFunc = initFunc;
}
public T Create()
{
return _initFunc();
}
}
public static class ServiceCollectionExtensions
{
public static void AddFactory<TService, TImplementation>(this IServiceCollection services)
where TService : class
where TImplementation : class, TService
{
services.AddTransient<TService, TImplementation>();
services.AddSingleton<Func<TService>>(x => () => x.GetService<TService>());
services.AddSingleton<IFactory<TService>, Factory<TService>>();
}
}
I think castle windsor's typed factories dispose of all they created when they themselves are disposed (which may not be always the best idea), with these links you would probably have to consider if you are still expecting that behaviour. When I reconsidered why I wanted a factory I ended up just creating a simple factory wrapping new, such as:
public class DefaultFooFactory: IFooFactory{
public IFoo create(){return new DefaultFoo();}
}
I'll show the minimal example for the factory that resolves ITalk implementation by a string key. The solution can be easily extended to a generic factory with any key and entity type.
For the sake of example let's define the interface ITalk and two implementations Cat and Dog:
public interface ITalk
{
string Talk();
}
public class Cat : ITalk
{
public string Talk() => "Meow!";
}
public class Dog : ITalk
{
public string Talk() => "Woof!";
}
Now define the TalkFactoryOptions and TalkFactory:
public class TalkFactoryOptions
{
public IDictionary<string, Type> Types { get; } = new Dictionary<string, Type>();
public void Register<T>(string name) where T : ITalk
{
Types.Add(name, typeof(T));
}
}
public class TalkFactory
{
private readonly IServiceProvider _provider;
private readonly IDictionary<string, Type> _types;
public TalkFactory(IServiceProvider provider, IOptions<TalkFactoryOptions> options)
{
_provider = provider;
_types = options.Value.Types;
}
public ITalk Resolve(string name)
{
if (_types.TryGetValue(name, out var type))
{
return (ITalk)_provider.GetRequiredService(type);
}
throw new ArgumentOutOfRangeException(nameof(name));
}
}
Add extension method for simple implementations registration:
public static class FactoryDiExtensions
{
public static IServiceCollection RegisterTransientSpeaker<TImplementation>(this IServiceCollection services, string name)
where TImplementation : class, ITalk
{
services.TryAddTransient<TalkFactory>();
services.TryAddTransient<TImplementation>();
services.Configure<TalkFactoryOptions>(options => options.Register<TImplementation>(name));
return services;
}
}
And register the Cat and Dog implementations:
services
.RegisterTransientSpeaker<Cat>("cat")
.RegisterTransientSpeaker<Dog>("dog");
Now you can inject the TalkFactory and resolve the implementation by the name:
var speaker = _factory.Resolve("cat");
var speech = speaker.Talk();
The trick here is Configure<TOptions(). This method is additive, which means you can call it multiple times to configure the same instance of TalkFactoryOptions.
As I said this example can be converted into a generic factory and add the ability to register factory delegate instead of a concrete type. But the code will be too long for SO.
I've been running up against the same issue and solved this by registering a set of open generics for IFactory<TService>, IFactory<T, TService>, IFactory<T1, T2, TService> etc. A single call on startup to add this facility then allows any IFactory<...> to be injected / resolved, which will instantiate an instance of TService for a given set of argument types, provided a constuctor exists whose last parameters match the T* types of the factory generic. Source code, NuGet package and explanatory blog article below:
https://github.com/jmg48/useful
https://www.nuget.org/packages/Ariadne.Extensions.ServiceCollection/
https://jon-glass.medium.com/abstract-factory-support-for-microsoft-net-dependency-injection-3c3834894c19
An alternative to the other answers. Follow the options pattern.
First introduce a strong type for your configuration;
public class RespositoryOptions {
public string Mode { get; set; }
public string ApiKey { get; set; }
}
public GlobalRepository(IOptions<RespositoryOptions> options) {
// use options.Value;
}
You could still use a service factory method to unwrap the IOptions<RespositoryOptions> if you prefer. But then you lose the ability to verify that your service dependencies have all been met.
Then you can seed your options from configuration;
public void ConfigureServices(IServiceCollection services) {
...
services.Configure<RespositoryOptions>(_configuration.GetSection(name));
...
}
And write another service to update that options instance from other services, like a database;
public class ConfigureRespositoryOptions : IConfigureOptions<RespositoryOptions> {
private readonly IDbRepository repo;
public ConfigureRespositoryOptions(IDbRepository repo) {
this.repo = repo;
}
public void Configure(RespositoryOptions config) {
string apiKey = repo.GetApiKeyMethodHere();
}
}
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");
}
}
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();