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.
Related
How should I register GrainService in Orleans 7.0?
I have GrainService:
public interface IAlfaGrainService : IGrainService
{
Task<IReadOnlyList<AlfaData>> TestMethod();
}
[Reentrant]
public class AlfaGrainService : GrainService, IAlfaGrainService
{
readonly IGrainFactory _grainFactory;
private readonly ILogger<AlfaGrainService> logger;
public AlfaGrainService(
IServiceProvider services,
Silo silo,
ILoggerFactory loggerFactory,
IGrainFactory grainFactory,
ILogger<AlfaGrainService> logger)
: base(GrainId.Create(nameof(AlfaGrainService), Guid.Empty.ToString()), silo, loggerFactory)
{
_grainFactory = grainFactory;
this.logger = logger;
}
public async Task<IReadOnlyList<AlfaData>> TestMethod()
{
logger.LogInformation("TestMethod() hit");
// TODO: custom logic here.
var data = new List<AlfaData> {
new AlfaData
{
Id = 1,
Name = "Test 1"
},
new AlfaData
{
Id = 2,
Name = "Test 2"
}
};
return await Task.FromResult(data);
}
}
GrainServiceClient (because I want to call GrainService from Grain):
public interface IAlfaGrainServiceClient : IGrainServiceClient<IAlfaGrainService>, IAlfaGrainService
{
}
public class AlfaGrainServiceClient : GrainServiceClient<IAlfaGrainService>, IAlfaGrainServiceClient
{
public AlfaGrainServiceClient(
IServiceProvider serviceProvider)
: base(serviceProvider)
{ }
public Task<IReadOnlyList<AlfaData>> TestMethod()
{
// Not sure how to get grainService reference:
var grainId = GrainId.Create(nameof(AlfaGrainService), Guid.Empty.ToString());
var service = GetGrainService(grainId);
// -------------------------------------
return service.TestMethod();
}
}
Grain from which I want to call GrainService (by proxy GrainServiceClient):
public interface IAlfaGrain: IGrainWithStringKey
{
Task<IReadOnlyList<AlfaData>> LoadData();
}
public class AlfaGrain: Grain, IAlfaGrain
{
private readonly IAlfaGrainServiceClient alfaGrainServiceClient;
public AlfaGrain(
IAlfaGrainServiceClient alfaGrainServiceClient)
{
this.alfaGrainServiceClient = alfaGrainServiceClient;
}
public async Task<IReadOnlyList<AlfaData>> LoadData()
{
return await alfaGrainServiceClient.TestMethod();
}
}
But If I register GrainService like this:
siloBuilder
.AddGrainService<AlfaGrainService>() // Register grainService like this ??
.ConfigureServices(services =>
{
services.AddSingleton<IAlfaGrainServiceClient, AlfaGrainServiceClient>();
});
I got error during starting app:
A suitable constructor for type 'GrainServiceApp.GrainServices.AlfaGrainService' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
Maybe because the GrainServiceFactory() (Orleans.Hosting.GrainServicesSiloBuilderExtensions) doesn't create instance with all ctor parameters.
Microsoft documentation is only about prev version of Orleans and thus doesn't work in my case.
Does anybody know how register GrainService in Orleans 7.0?
All code is on github
I have already found the solution and it was quite simple.
I needed to inject GrainId into GrainService and pass it into the base class:
public AlfaGrainService(
GrainId grainId,
Silo silo,
IServiceProvider services,
ILoggerFactory loggerFactory,
ILogger<AlfaGrainService> logger)
: base(grainId, silo, loggerFactory)
{
this.logger = logger;
}
Then in GrainServiceClient I got GrainService instance by CurrentGrainReference.GrainId like this:
public Task<IReadOnlyList<AlfaData>> TestMethod()
{
var service = GetGrainService(CurrentGrainReference.GrainId);
return service.TestMethod();
}
I have updated the example on GitHub
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.
I'm going to add in the necessary infrastructure to bind each request to its own nested container, So in this case having a Container Per Request gives us a unique session (Context Per Request), In my code I'm using the new implementation of ObjectFactory:
public static class SmObjectFactory
{
private static readonly Lazy<Container> _containerBuilder =
new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
public static IContainer Container
{
get { return _containerBuilder.Value; }
}
private static Container defaultContainer()
{
return new Container(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
});
}
}
So, for example If I just set a break point in the ApplicationDbContext's constructor, each time an instance is created. For example in my case I have these controllers:
public partial class HomeController : Controller
{
private readonly IUnitOfWork _uow;
public HomeController(IUnitOfWork uow)
{
_uow = uow;
}
public virtual ActionResult Index()
{
return View();
}
}
public class TestController : Controller
{
private readonly IUnitOfWork _uow;
public TestController(IUnitOfWork uow)
{
_uow = uow;
}
public ActionResult GetData()
{
return Content("Data");
}
}
So the view returned by Index action uses this code to pull in content from TestController:
#Html.Action("GetData", "Test")
In that example, several instances are created per request!
So I've changed SmObjectFactory this way:
public class NewObjectFactory
{
public static IContainer Container { get; set; }
static NewObjectFactory()
{
Container = new Container();
Container.Configure(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
}
}
Then in the Global.asax I've added these lines of code for using nested container:
public IContainer Container
{
get
{
return (IContainer)HttpContext.Current.Items["_Container"];
}
set
{
HttpContext.Current.Items["_Container"] = value;
}
}
public void Application_BeginRequest()
{
Container = NewObjectFactory.Container.GetNestedContainer();
}
public void Application_EndRequest()
{
Container.Dispose();
Container = null;
}
And inside Application_Start:
DependencyResolver.SetResolver(
new StructureMapDependencyResolver(() => Container ?? NewObjectFactory.Container));
And inside of the DependencyResolver I've implemented the factory function this way:
public class StructureMapDependencyResolver : IDependencyResolver
{
private readonly Func<IContainer> _factory;
public StructureMapDependencyResolver(Func<IContainer> factory)
{
_factory = factory;
}
public object GetService(Type serviceType)
{
if (serviceType == null)
{
return null;
}
var factory = _factory();
return serviceType.IsAbstract || serviceType.IsInterface
? factory.TryGetInstance(serviceType)
: factory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _factory().GetAllInstances(serviceType).Cast<object>();
}
}
Finaly when I run the application I get this error:
No default Instance is registered and cannot be automatically
determined for type
'Microsoft.Owin.Security.DataProtection.IDataProtectionProvider'
But when I comment this line in the Startup.cs file:
ConfigureAuth(app)
everything works and this time the ApplicationDbContext is created once and then disposed. So that's what I want: Only a single instance of the context is created now and it is correctly disposed of at the end of the web request, It means that the nested container reused the context to satisfy the dependencies of both controllers. :)
Any idea?
We have following implementation of IProduct & ILogger class. Manager classes are being used as communication channel. We have used unity as given below to resolve manager class but it giving following error-
===================unity configuration==========
IUnityContainer container = new UnityContainer();
container.RegisterType<ILogger, FileLogger>("File");
container.RegisterType<ILogger, BDLogger>("DB");
container.RegisterType<IProduct, ProductA>("productA");
container.RegisterType<IProduct, ProductB>("productB");
container.RegisterType<ProducManager>(new InjectionConstructor(container.Resolve<IProduct>("productA")));
container.Resolve<ProducManager>("productA");
========================================================
"Resolution of the dependency failed, type = \"usingDI.IProduct\",
name = \"productA\".\r\nException occurred while: while
resolving.\r\nException is: InvalidOperationException - The current
type, usingDI.ILogger, is an interface and cannot be constructed. Are
you missing a type
mapping?\r\n-----------------------------------------------\r\nAt the
time of the exception, the container was:\r\n\r\n Resolving
usingDI.ProductA,productA (mapped from usingDI.IProduct, productA)\r\n
Resolving parameter \"logger\" of constructor
usingDI.ProductA(usingDI.ILogger logger)\r\n Resolving
usingDI.ILogger,(none)\r\n"}"
Please suggest me best way to resolve dependency for manager classes.
public interface IProduct
{
void dosomething();
}
public class ProducManager
{
IProduct _product;
public ProducManager(IProduct product)
{
_product = product;
}
public void DoSomething()
{
_product.dosomething();
}
}
public class ProductA : IProduct
{
ILogger _logger;
public ProductA(ILogger logger)
{
_logger = logger;
}
public void dosomething()
{
try
{
//code
//
}
catch (Exception ex)
{
_logger.WriteLog(ex.Message);
}
}
}
public class ProductB : IProduct
{
ILogger _logger;
public ProductB(ILogger logger)
{
_logger = logger;
}
public void dosomething()
{
try
{
//code
//
}
catch (Exception ex)
{
_logger.WriteLog(ex.Message);
}
}
}
public class LogManager
{
ILogger _logger;
public LogManager(ILogger logger)
{
_logger = logger;
}
public void WriteLog(string exception)
{
_logger.WriteLog(exception);
}
}
public interface ILogger
{
void WriteLog(string exception);
}
public class FileLogger : ILogger
{
public void WriteLog(string exception)
{
}
}
public class BDLogger : ILogger
{
public void WriteLog(string exception)
{
}
}
You need to register a type mapping for your interface ILogger. Otherwise does Unity not now which implementation of ILogger it should use (see your exception).
HereĀ“s an example registering the interface ILogger with a mapping to FileLogger:
_container.RegisterType<ILogger, FileLogger>();
IProduct product = _container.Resolve<ProductA>(); //ILogger is injected to Product
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;
}