How to inject custom depencency in Autofac factory? - c#

I have often code that requires dynamic initialization after the program has started and looks something like that (WinFoms):
public class MyParentComponent : IMyParentComponent
{
private readonly ILogger<MyParentComponent> _logger;
private readonly IMyChildComponentFactory _factory;
private readonly Dictionary<int, IMyChildComponent> _children;
public MyParentComponent(ILogger<MyParentComponent> logger, IMyChildComponentFactory factory)
{
_logger = logger;
_factory = factory;
_children = new Dictionary<int, IMyChildComponent>();
}
// WinForms, called with parameter from GUI.
public void Initialize(int instanceCnt)
{
for (var id = 0; id < instanceCnt; id++)
{
var child = _factory.CreateComponent(id);
_children.Add(id, child);
}
}
}
public interface IMyParentComponent
{
void Initialize(int instanceCnt);
}
public class MyChildComponent : IMyChildComponent
{
private readonly ILogger<MyChildComponent> _logger;
private int _id;
public MyChildComponent(ILogger<MyChildComponent> logger, int id)
{
_logger = logger;
_id = id;
}
}
public interface IMyChildComponent
{
}
For that, I usually create factory manually and use it to resolve dependency by some kind of id (no pre-defined list).
I use IServiceProvider instead of ILifetimeScope to skip dependency to Autofac and think that components should have no dependency on DI contained and don't want to inject ILifetimeScope in MyParentComponent ctor.
public interface IMyChildComponentFactory
{
IMyChildComponent CreateComponent(int id);
}
/// <summary>
/// Factory to create components.
/// </summary>
public class MyChildComponentFactory : IMyChildComponentFactory
{
private readonly ILogger<MyChildComponentFactory> _logger;
private readonly IServiceProvider _serviceProvider;
public MyChildComponentFactory(ILogger<MyChildComponentFactory> logger, IServiceProvider serviceProvider)
{
_logger = logger;
_serviceProvider = serviceProvider;
}
public IMyChildComponent CreateComponent(int id)
{
var logger = _serviceProvider.GetRequiredService<ILogger<MyChildComponent>>();
var mInstance = new MyChildComponent(logger, id);
return mInstance;
}
}
In Autofac, I can use delegate factories for that, but in some cases I need to resolve other dependencies like ILogger and that makes MyParentComponent dependent on DI container.
Is there any pre-defined pattern for that?

Related

How to pass logger object while creating instance using reflection?

Abstract Class:
public abstract class Rater
{
public Rater()
{
}
public abstract decimal Rate(Policy policy);
}
Child classes:
public class AutoPolicyRater : Rater
{
public readonly ILogger<AutoPolicyRater> _logger;
public AutoPolicyRater(ILogger<AutoPolicyRater> logger)
{
_logger = logger;
}
public override decimal Rate(Policy policy)
{
_logger.Log("Rating AUTO policy...");
_logger.Log("Validating policy.");
if (String.IsNullOrEmpty(policy.Make))
{
_logger.Log("Auto policy must specify Make");
return 0m;
}
if (policy.Make == "BMW")
{
if (policy.Deductible < 500)
{
return 1000m;
}
return 900m;
}
return 0m;
}
}
public class LandPolicyRater : Rater
{
public readonly ILogger<LandPolicyRater> _logger;
public AutoPolicyRater(ILogger<LandPolicyRater> logger)
{
_logger = logger;
}
public override decimal Rate(Policy policy)
{
_logger.Log("Rating LAND policy...");
_logger.Log("Validating policy.");
if (policy.BondAmount == 0 || policy.Valuation == 0)
{
_logger.Log("Land policy must specify Bond Amount and Valuation.");
return 0m;
}
if (policy.BondAmount < 0.8m * policy.Valuation)
{
_logger.Log("Insufficient bond amount.");
return 0m;
}
return (policy.BondAmount * 0.05m);
}
}
Factory class, where I want to dynamically pass the logger object:
public class RaterFactory
{
private readonly IRatingUpdater _ratingUpdater;
public RaterFactory(ILogger logger)
{
}
public Rater Create(Policy policy)
{
try
{
return (Rater)Activator.CreateInstance(
Type.GetType($"MyCompany.{policy.Type}PolicyRater"),
new object[] { ?? });//here I want to pass logger object
}
catch
{
return new UnknownPolicyRater();
}
}
}
As these classes are not controllers, and I want to create object in my factory method, how can I pass logger object and log information to application insight? I would like to pass generic logger object, however, if there is another approach to achieve, I'm ok.
EDIT:
After #fildor's suggestion, I tried below and it is logging information in Application Insight traces.
public class RaterFactory
{
private readonly ILoggerFactory _loggerFactory;
public RaterFactory(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public Rater Create(Policy policy)
{
try
{
string typeString = $"MyCompany.{policy.Type}PolicyRater";
ILogger modelLogger = _loggerFactory.CreateLogger(typeString);
return (Rater)Activator.CreateInstance(
Type.GetType($"MyCompany.{policy.Type}PolicyRater"),
new object[] { modelLogger });
}
catch
{
return new UnknownPolicyRater();
}
}
}
public class AutoPolicyRater : Rater
{
public readonly ILogger _logger;
public AutoPolicyRater(ILogger logger)
{
_logger = logger;
}
//other code
}
As requested: a possible implementation:
public class RaterFactory
{
private readonly ILoggerFactory _loggerFactory;
public RaterFactory(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
}
public Rater Create(Policy policy)
{
try
{
return (Rater)Activator.CreateInstance(
Type.GetType($"MyCompany.{policy.Type}PolicyRater"),
new object[] { _loggerFactory });
}
catch
{
return new UnknownPolicyRater();
}
}
}
And then ...
public class AutoPolicyRater : Rater
{
private readonly ILogger<AutoPolicyRater> _logger;
public AutoPolicyRater(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AutoPolicyRater>();
}
public override decimal Rate(Policy policy)
{
// ... ommited for brevity
}
}
The RaterFactory class has no need to know in advance all dependencies injected into the instances it creates.
Instead, you can inject IServiceProvider and let ActivatorUtilities resolve the dependencies of the Rater instances that you are creating each time.
This is how it can be done:
public class RaterFactory
{
private readonly IServiceProvider _serviceProvider;
public RaterFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public Rater Create(Policy policy)
{
try
{
// OPTION 1
return (Rater)ActivatorUtilities.CreateInstance(
_serviceProvider,
Type.GetType($"MyCompany.{policy.Type}PolicyRater"));
// OPTION 2
return (Rater)ActivatorUtilities.GetServiceOrCreateInstance(
_serviceProvider,
Type.GetType($"MyCompany.{policy.Type}PolicyRater"));
}
catch
{
return new UnknownPolicyRater();
}
}
}
As shown above, there are two possible options that you should choose according to your needs and constraints.
ActivatorUtilities.CreateInstance: This method creates each time a new instance and does not query the service collection for the target type. This is convenient if you don't know all the possible target types in advance (or you don't want to register them for some reason).
ActivatorUtilities.GetServiceOrCreateInstance: This method looks for the target type into the service collection; if a registration is found, it returns the corresponding instance, otherwise it behaves like ActivatorUtilities.CreateInstance. This means that you can register the target type in the service collection as usual with the most appropriate lifetime (singleton, scoped or transient) for each type. The only downside of this approach is that, if you have some singleton or scoped target types, you have to provide a way to register them in the service collection, which may be tricky in a plugin-like application.
Again, please note that there are no constraints on which dependencies can be injected in the Rater subtypes, because after all the "dirty" work of dependency resolution is done by the ActivatorUtilities class.

Get an instance of a class by its name

I have StrategyName set in appsettings.json which represents the name of the strategy class. I need to get an instance of it.
ITradingStrategy _tradingStrategy = StrategyUtils.GetStrategyInstance(logger, _tradeOptions.StrategyName)
which is equal to
ITradingStrategy _tradingStrategy = new RsiStrategy(logger);
Is it possible to be made in a better way? It works but looks ugly. Since we know the strategy name in the beginning (from appsettings.json), there should probably be a way to obtain it in a better ASP.NET Core way. Maybe some cool extension method, I don't know.
appsettings.json
{
"TradeConfiguration": {
"StrategyName": "RsiStrategy",
...
}
}
Code
public class LiveTradeManager : ITradeManager
{
private readonly ILogger _logger;
private readonly IExchangeClient _exchangeClient;
private readonly ITradingStrategy _tradingStrategy;
private readonly ExchangeOptions _exchangeOptions;
private readonly TradeOptions _tradeOptions;
public LiveTradeManager(ILogger logger, IConfiguration configuration, IExchangeClient exchangeClient)
{
_logger = logger;
_exchangeClient = exchangeClient;
_exchangeOptions = configuration.GetSection("ExchangeConfiguration").Get<ExchangeOptions>();
_tradeOptions = configuration.GetSection("TradeConfiguration").Get<TradeOptions>();
_tradingStrategy = StrategyUtils.GetStrategyInstance(logger, _tradeOptions.StrategyName); // This is the questioned line
}
}
public static ITradingStrategy GetStrategyInstance(ILogger logger, string strategyName)
{
var strategyType = Assembly.GetAssembly(typeof(StrategyBase))
.GetTypes().FirstOrDefault(type => type.IsSubclassOf(typeof(StrategyBase)) && type.Name.Equals(strategyName));
if (strategyType == null)
{
throw new ArgumentException($"The strategy \"{strategyName}\" could not be found.", nameof(strategyName));
}
var strategy = Activator.CreateInstance(strategyType, logger) as ITradingStrategy;
return strategy;
}
// Strategies
public interface ITradingStrategy
{
IReadOnlyList<TradeAdvice> Prepare(IReadOnlyList<OHLCV> candles);
}
public abstract class StrategyBase : ITradingStrategy
{
private readonly ILogger _logger;
protected StrategyBase(ILogger logger)
{
_logger = logger;
}
public abstract IReadOnlyList<TradeAdvice> Prepare(IReadOnlyList<OHLCV> candles);
}
public class RsiStrategy : StrategyBase
{
private readonly ILogger _logger;
public RsiStrategy(ILogger logger) : base(logger)
{
_logger = logger;
}
public override IReadOnlyList<TradeAdvice> Prepare(IReadOnlyList<OHLCV> candles)
{
... _logger.Information("Test");
}
}
// Main
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
})
.ConfigureServices((hostingContext, services) =>
{
services.AddSingleton(
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostingContext.Configuration)
.CreateLogger());
services.AddSingleton<ITradeManager, LiveTradeManager>();
services.AddSingleton<IExchangeClient, BinanceSpotClient>();
services.AddHostedService<LifetimeEventsHostedService>();
})
.UseSerilog();
}
Your problem can be solved multiple ways and using reflection would be the last one.
From your problem statement, I figure that you have multiple strategy classed implementing ITradingStrategy interface, and you configuration value from appsettings.json file decides which strategy to use.
One of the approach you can use here is to use factory to initialize appropriate strategy class based on the configuration value.
Following is the factory class and interface which will create Strategy class object based on the strategy name passed to it.
public interface IStrategyFactory
{
ITradingStrategy GetStrategy(string strategyName);
}
public class StrategyFactory : IStrategyFactory
{
private IServiceProvider _serviceProvider;
public StrategyFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ITradingStrategy GetStrategy(string strategyName)
{
switch (strategyName)
{
case "Rsi":
// Resolve RsiStrategy object from the serviceProvider.
return _serviceProvider.GetService<RsiStrategy>();
case "Dmi":
// Resolve DmiStrategy object from the serviceProvider.
return _serviceProvider.GetService<DmiStrategy>();
default:
return null;
}
}
}
This strategy can now be used in controller and call its GetStrategy method by passing the strategy name which in-turn is retrieved from the configuration.
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
// Strategy factory.
private IStrategyFactory _strategyFactory;
// Configuration
private IConfiguration _configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration, IStrategyFactory strategyFactory)
{
_logger = logger;
_strategyFactory = strategyFactory;
_configuration = configuration;
}
public IActionResult Index()
{
// Get Configuration value "StrategyName" from configuration.
// In your case this will be your own custom configuration.
var strategyName = _configuration.GetValue<string>("StrategyName");
// Pass strategyName to GetStrategy Method.
var strategy = _strategyFactory.GetStrategy(strategyName);
// Call Prepare method on the retrieved strategy object.
ViewBag.PreparedList = strategy.Prepare(new List<OHLCV>());
return View();
}
}
For the above code to work you need to register strategy classed in to serviceCollection.
services.AddSingleton<RsiStrategy>();
services.AddSingleton<DmiStrategy>();
And also the StrategyFactory.
services.AddSingleton<IStrategyFactory, StrategyFactory>();
EDIT
Based on your comment below, you need to be able to resolve the strategy types without additional overhead of registering them in DI as when new types are created and also without making changes in the factory.
You need to use reflection for this. Using reflection you can determine the types which you want to register in the DI. As following.
//Get all the types which are inheriting from StrategyBase class from the assembly.
var strategyTypes = Assembly.GetAssembly(typeof(StrategyBase))
?.GetTypes()
.Where(type => type.IsSubclassOf(typeof(StrategyBase)));
if (strategyTypes != null)
{
//Loop thru the types collection and register them in serviceCollection.
foreach (var type in strategyTypes)
{
services.Add(new ServiceDescriptor(typeof(StrategyBase), type, ServiceLifetime.Singleton));
}
}
With the above code, all the types which are inheriting from StrategyBase are registered in serviceCollection. Now using serivceProvider we can get all the registered instances and look for the instance which has correct strategyName.
So the factory's GetStrategy method will look like as following.
public ITradingStrategy GetStrategy(string strategyName)
{
var strategies = _serviceProvider.GetServices<StrategyBase>();
var strategy = strategies.FirstOrDefault(s => s.GetType().Name == strategyName);
if (strategy == null)
{
throw new ArgumentException($"The strategy \"{strategyName}\" could not be found.", nameof(strategyName));
}
return strategy;
}
I hope this will help you resolve your issue.

Implementing unit of work in Entity Framework Core

I have implemented unit of work in the next way in Entity Framework Core.
Context:
public class DaleContext : DbContext, IDaleContext
{
private readonly IConnectionStringProvider _connectionStringProvider;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionStringProvider.ConnectionString);
base.OnConfiguring(optionsBuilder);
}
public DaleContext(IConnectionStringProvider connectionStringProvider)
{
_connectionStringProvider = connectionStringProvider;
}
public DbSet<ProductProducts { get; set; }
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
var modifiedEntries = ChangeTracker.Entries()
.Where(x =x.State == EntityState.Added || x.State == EntityState.Modified).ToList();
return base.SaveChanges();
}
}
Unit of work:
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork(DbContext dbContext)
{
DbContext = dbContext;
}
public DbContext DbContext { get; set; }
public int Commit()
{
return DbContext.SaveChanges();
}
public async Task<intCommitAsync()
{
return await DbContext.SaveChangesAsync();
}
}
Repository:
public class Repository<TEntity: IDisposable, IRepository<TEntity>
where TEntity : class
{
private readonly UnitOfWork _unitOfWork;
public Repository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork as UnitOfWork;
}
public void Dispose()
{
_unitOfWork.DbContext.Dispose();
}
public void Create(TEntity entity)
{
_unitOfWork.DbContext.Entry(entity).State = EntityState.Added;
}
}
I have injected all with autofac:
public static class Container
{
public static ContainerBuilder RegisterInfraestructure(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
return containerBuilder;
}
}
public static class Container
{
public static ContainerBuilder RegisterDataResources(this ContainerBuilder containerBuilder)
{
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
var configuration = configurationBuilder.Build();
containerBuilder.Register(x =new ConnectionStringProvider(configuration.GetConnectionString("Fgcm.Dale"))
).As<IConnectionStringProvider>();
containerBuilder.RegisterType<DaleContext>().As<DbContext>().As<IDaleContext>();
return containerBuilder;
}
}
public static class Container
{
public static ContainerBuilder RegisterRepository(this ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<CustomerRepository>().As<ICustomerRepository>();
containerBuilder.RegisterType<ProductRepository>().As<IProductRepository>();
containerBuilder.RegisterType<SaleDetailRepository>().As<ISaleDetailRepository>();
containerBuilder.RegisterType<SaleRepository>().As<ISaleRepository>();
containerBuilder.RegisterDataResources();
containerBuilder.RegisterInfraestructure();
return containerBuilder;
}
}
public static class Container
{
public static ContainerBuilder RegisterApplicationServiceResources(this ContainerBuilder
containerBuilder)
{
containerBuilder.RegisterRepository();
containerBuilder.RegisterType<DaleApplicationService>().As<IDaleApplicationService>();
return containerBuilder;
}
}
When I try to save data it doesn't works (doesn't insert data) ... I would like to know why ? Here are when I try to save:
public Product Create(Product product)
{
try
{
_productRepository.Create(product);
_unitOfWork.Commit();
return product;
}
catch (Exception e)
{
Debug.WriteLine(e);
return null;
}
}
And of course all are injected:
private readonly ICustomerRepository _customerRepository;
private readonly IProductRepository _productRepository;
private readonly ISaleDetailRepository _saleDetailRepository;
private readonly ISaleRepository _saleRepository;
private readonly IUnitOfWork _unitOfWork;
public DaleApplicationService(IProductRepository productRepository, ICustomerRepository customerRepository,
ISaleRepository saleRepository, ISaleDetailRepository saleDetailRepository, IUnitOfWork unitOfWork)
{
_productRepository = productRepository;
_customerRepository = customerRepository;
_saleRepository = saleRepository;
_saleDetailRepository = saleDetailRepository;
_unitOfWork = unitOfWork;
}
What am I missing?
PS: all this works with .NET Core Web Api.
As #zolty13 sort of hinted the Instance scope of your DbContext (DaleContext) is probably incorrect. By default Autofac sets the instance scope to Instance per dependency (also known as a "transient" lifetime) which means a new instance of DaleContext is created for every class that depend on it. So your UnitOfWork receives a different instance of DaleContext than IProductRepository. So changes in IProductRepository are not reflected in UnitOfWork.
One way to solve this is to avoid this convoluted wrapping of your DbContext like #Igor suggest. Do you really need this UnitOfWork? Instead, use a repository class that has one instance of DaleContext and make all the DB changes in there and save them.
Alternatively (if you really think you need a UnitOfWork) you can register your DaleContext with an instance per request scope. Do note: Entity Framework's DbContext is not thread safe, so if you need to do concurrent work, this is not a safe approach.
Otherwise, read up on Instance scope.

.NET Core IServiceCollection Replace not refreshing

I have an Address object that depends on an IState object inject in the constructor:
public class AddressService : BaseService, IAddressService
{
private readonly IStateRepository _stateRepository;
private readonly ICacheClass _cache;
private readonly ILogger<AddressService> _logger;
public const string StatesCacheKey = "StateListManagedByStateService";
public AddressService(IStateRepository stateRepository,
ICacheClass cache,
ILogger<AddressService> logger,
IUserContext userContext) : base(userContext)
{
_stateRepository = stateRepository;
_cache = cache;
_logger = logger;
}
public async Task<IReadOnlyList<T>> GetAllStatesAsync<T>() where T : IState
{
var list = await _cache.GetAsync<IReadOnlyList<T>>(StatesCacheKey);
if (list != null) return list;
var repoList = _stateRepository.GetAll().Cast<T>().ToList();
_logger.LogInformation("GetAllStates retrieved from repository, not in cache.");
await _cache.SetAsync(StatesCacheKey, repoList);
return repoList;
}
}
IStateRepository is injected by the ASP.NET Core web project with the folling lines in Startup:
services.AddTransient<IStateRepository, Local.StateRepository>();
services.AddTransient<IAddressService, AddressService>();
In my Controller I have two action methods. Depending on the one called, I want to change the IStateRepository object associated with DI:
private readonly IAddressService _addressService;
public HomeController(IConfiguration configuration, ILogger<HomeController> logger, IAddressService addressService) : base(configuration,logger)
{
_addressService = addressService;
}
public async Task<IActionResult> DistributedCacheAsync()
{
var services = new ServiceCollection();
services.Replace(ServiceDescriptor.Transient<IStateRepository, Repositories.Local.StateRepository>());
ViewBag.StateList = await _addressService.GetAllStatesAsync<State>();
return View();
}
public async Task<IActionResult> DapperAsync()
{
var services = new ServiceCollection();
services.Replace(ServiceDescriptor.Transient<IStateRepository, Repositories.Dapper.StateRepository>());
ViewBag.StateList = await _addressService.GetAllStatesAsync<State>();
return View();
}
The problem is that _addressService already has the Local.StateRepository associated from Startup, and updating it in DapperAsync using Replace doesn't have an effect on it.
How would be able to change IStateRepository in DI during runtime in the example above?
Changing services at runtime is not supported by Microsoft.Extensions.DependencyInjection.
I recommend creating a wrapper service that at runtime can choose between different implementations based on a condition.

Castle Windsor won't inject Logger in a property!

I try to inject log4net in a ILogger property of my service class but the property is always NULL!
I've seen this topic but it doesn't help me!
How can I get Castle Windsor to automatically inject a property?
this is Program.cs
CastleContainer.Instance
.Install(
new RepositoriesInstaller(),
new PersistenceInstaller(),
new LoggerInstaller(),
new FormInstaller(),
new ServiceInstaller()
);
FrmStart form1 = CastleContainer.Resolve<FrmStart>(new {Id="666" });
I use log4net.config external file and this is my installer:
public class LoggerInstaller : IWindsorInstaller
{
#region IWindsorInstaller Members
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility("logging", new LoggingFacility(LoggerImplementation.Log4net, "log4net.config"));
}
#endregion
}
This is the class contains the property I want Windsor to inject:
public partial class FrmStart : Form
{
private EventService EventService;
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public FrmStart(EventService eventService, string Id)
: this()
{
Logger.Debug("xxx");
this.EventService = eventService;
this.id = Id;
}
Note that "eventService" and "Id" in the constructor are correctly injected!
If I try to inject the Logger in the constructor it works and I've the Logger object:
{log4net.Repository.Hierarchy.DefaultLoggerFactory+LoggerImpl}! :-(
I've tried to create a public property for EventService and Windsor can inject it properly! So I think the problem is related only to the ILogger interface.
I prepared a simple full-code example here:
using Castle.Core.Logging;
using Castle.Facilities.Logging;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace IocTest
{
public class LoggerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility("logger", new LoggingFacility(LoggerImplementation.Log4net, "log4net.config"));
}
}
public class LogicInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(AllTypes.FromThisAssembly()
.Pick()
.If(t => t.Name.StartsWith("Logic"))
.Configure((c => c.LifeStyle.Transient)));
}
}
class Program
{
static void Main(string[] args)
{
IWindsorContainer container = new WindsorContainer();
container.Install(
new LoggerInstaller(),
new LogicInstaller()
);
LogicClass1 logic1 = container.Resolve<LogicClass1>();
LogicClass2 logic2 = container.Resolve<LogicClass2>();
}
}
public class LogicClass1
{
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public LogicClass1()
{
logger.Debug("Here logger is NullLogger!");
}
}
public class LogicClass2
{
public LogicClass2(ILogger logger)
{
logger.Debug("Here logger is properly injected!");
}
}
}
What's wrong?
A problem is where you are checking it:
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public LogicClass1()
{
logger.Debug("Here logger is NullLogger!");
}
The property injection will not happen until after the constructor is run, so checking the property value in the constructor will never show the value you are expecting
I was having the same problem. It was always null.
I managed to solve the problem by injecting the logger in the constructor this way:
public ILogger logger;
public MyController(ILogger logger)
{
this.logger = logger;
logger.Info("Something");
}
You could also initialize your Logger by using:
public ILogger Logger { get; set; }
public MyController()
{
Logger = NullLogger.Instance;
}

Categories