I'm developing a framework where I've put lots of logging throughout. I used Castle Windsor's ILogger through this property pattern:
namespace Framework
{
public class SomeClass
{
private ILogger _logger = NullLogger.Instance;
public ILogger Logger
{
get { return _logger; }
set { _logger = value; }
}
public void DoSomething()
{
Logger.Info("Doing something.");
}
//...
}
}
I also provide an installer from within the framework:
namespace MyFramework
{
public class LoggerInstaller : IWindsorInstaller
{
private readonly string _configPath;
public LoggerInstaller(string configPath)
{
_configPath = configPath;
}
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility("logging", new LoggingFacility(LoggerImplementation.Log4net, _configPath));
//I've also tried this one:
//container.AddFacility<LoggingFacility>(f => f.LogUsing(LoggerImplementation.Log4net).WithConfig(_configPath));
}
}
}
This project is then referenced from other projects. For example, in the test project, I'll construct a test by first installing the logger. I do this with an abstract class that all of my long running tests extend:
namespace Framework.Test
{
public abstract class Log4NetLoggedTest
{
private const string ConfigFilePath = "log4net.config";
protected ILogger Logger { get; set; }
protected IWindsorContainer Container { get; set; }
protected Log4NetLoggedTest()
{
Container = new WindsorContainer();
Container.Install(new LoggerInstaller(ConfigFilePath));
Logger = Container.Resolve<ILogger>();
}
~Log4NetLoggedTest()
{
Container.Dispose();
}
}
}
So that my test looks like this:
namespace Framework.Test
{
[TestFixture]
public class MyLongRunningTest : Log4NetLoggedTest
{
[Test]
[Category("LongRunning")]
public void ModelConvergesForS50()
{
Logger.Info("Starting test...");
var obj = new SomeClass();
obj.DoSomething();
// ...
}
}
}
The test's ILogger Logger gets resolved and set properly, so in this example I get the "Starting test..." but not the "Doing something." The SomeClass's ILogger stays as a NullLogger.
Please help!
You are instantiating SomeObj with 'new' rather than going through the container. If you don't go through the container, it can't inject the dependency
I may be saying something stupid, but, shouldnt be something like:
namespace Framework.Test
{
[TestFixture]
public class MyLongRunningTest : Log4NetLoggedTest
{
[Test]
[Category("LongRunning")]
public void ModelConvergesForS50()
{
Logger.Info("Starting test...");
var obj = new SomeClass();
obj.Logger = Logger;
obj.DoSomething();
// ...
}
}
}
I couldn't see you applying that instance of the logger that you use inside the class anywhere.
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'm facing a problem with Autofac registrations. In short, if I register the models BEFORE my configuration, when I load the configuration, it works smoothly but, if I register the models AFTER I register the configuration, the configuration models are loaded with their default types (default(T)). Below is the code to reproduce the problem:
using System;
using System.IO;
using Autofac;
using Microsoft.Extensions.Configuration;
namespace AutofacConfigurationTest.CrossCutting
{
public class ModuleModel : Module
{
protected override void Load(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<Cache.Configuration>()
.As<Cache.IConfiguration>();
containerBuilder.RegisterType<Repository.Configuration>()
.As<Repository.IConfiguration>();
}
}
public class ModuleConfiguration : Module
{
protected override void Load(ContainerBuilder containerBuilder)
{
var configurationRoot = new Configuration.Container().ConfigurationRoot;
containerBuilder.RegisterInstance(configurationRoot).As<IConfigurationRoot>();
containerBuilder
.RegisterInstance(configurationRoot.GetSection(Cache.Configuration.Name)
.Get<Cache.Configuration>()).As<Cache.IConfiguration>();
containerBuilder
.RegisterInstance(configurationRoot.GetSection(Repository.Configuration.Name)
.Get<Repository.Configuration>()).As<Repository.IConfiguration>();
}
}
public class Container
{
public IContainer Kernel { get; }
public Container()
{
var containerBuilder = new ContainerBuilder();
// uncomment the line below to make it work //
containerBuilder.RegisterModule(new ModuleModel()); // if we register the models here, before the configuration, the configuration works properly //
containerBuilder.RegisterModule(new ModuleConfiguration());
// comment the line below to make it work //
containerBuilder.RegisterModule(new ModuleModel()); // if we register the models here, after the configuration, the configuration cannot load the data //
Kernel = containerBuilder.Build();
}
}
}
namespace AutofacConfigurationTest.Configuration
{
public class Container
{
private const string ConfigurationFile = "AppSettings.json";
public Container()
{
ConfigurationRoot = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(ConfigurationFile).Build();
}
public IConfigurationRoot ConfigurationRoot { get; }
}
}
namespace AutofacConfigurationTest.Cache
{
public enum Engine
{
None,
Default
}
public interface IConfiguration
{
Engine Engine { get; set; }
int Duration { get; set; }
string ConnectionString { get; set; }
}
public class Configuration : IConfiguration
{
public const string Name = "Cache";
public Engine Engine { get; set; }
public int Duration { get; set; }
public string ConnectionString { get; set; }
}
}
namespace AutofacConfigurationTest.Repository
{
public enum Engine
{
None,
LiteDb
}
public interface IConfiguration
{
Engine Engine { get; set; }
string ConnectionString { get; set; }
}
public class Configuration : IConfiguration
{
public const string Name = "Repository";
public Engine Engine { get; set; }
public string ConnectionString { get; set; }
}
}
namespace AutofacConfigurationTest
{
internal class Program
{
private static IContainer _container;
private static void RegisterServices() => _container = new CrossCutting.Container().Kernel;
private static void DisposeServices()
{
if (_container != null &&
_container is IDisposable disposable)
disposable.Dispose();
}
private static void Main(string[] args)
{
try
{
RegisterServices();
// the following objects will be have a default(T) instance
// if the in the Autofac modules the Model is registered AFTER the Configuration
var cacheConfiguration = _container.Resolve<Cache.IConfiguration>();
var repositoryConfiguration = _container.Resolve<Repository.IConfiguration>();
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
DisposeServices();
}
}
}
}
I have a second question for you: I use the interfaces to force the contract in my models. In short, these interfaces are NEVER injected anywhere, it's just to make easier the maintenance for me. Should I remove the DI/IoC for these models or is there any reason to keep the models registration in the container?
The observed difference in behavior that now depends on the sequence of module registration, is due to the fact that both modules are registering services keyed to Cache.IConfiguration and Repository.IConfiguration. Therefore, the last module to be registered will "win".
When ModuleModel is registered last it will override any previous registration of the two configuration interfaces, and resolution will yield instances of Cache.Configuration and Repository.Configuration.
If ModuleConfiguration is registered last, resolution will yield instances provided by the configurationRoot object.
Inferring your intent from the two modules, since ModuleConfiguration registrations are actually trying to resolve Cache.Configuration and Repository.Configuration, ModuleModel must register these types keyed to those types instead of keyed to the interfaces. You do that using .AsSelf() instead of .As<some interface>(). Read more about AsSelf here.
Consider Apache Ignite.NET cluster that provides service grid.
There is a simple service, that will run on any node:
public class ClientConnectionService : IClientConnectionService, IService
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
[InstanceResource] private IIgnite Ignite { get; set; }
public void Listen(string hostname, int port, uint username, string password,
ClientConnectionListenerOptions options = ClientConnectionListenerOptions.All)
{
Logger.Debug("Listen");
}
public void Init(IServiceContext context)
{
Logger.Debug("Initialized");
}
public void Execute(IServiceContext context)
{
Logger.Debug("Executed");
}
public void Cancel(IServiceContext context)
{
Logger.Debug("Canceled");
}
}
The application is using Castle Windsor as inversion of control container.
I would like to inject custom dependencies, that won't be serialized and transferred over the wire.
Is there any way to achieve it?
N.B. In Java version, there is #SpringResourceannotation that will basically do what I want, but the question is about .NET, that provides just [InstanceResource] attribute.
This is what I have ended up with:
In shared project where all the interfaces and contracts are described I've introduced IContainer
public interface IContainer
{
T Resolve<T>();
}
In project that is responsible for Apache Ignite.NET integration I've implemented simple Apache Ignite.NET plugin
public class DependencyInjectionPlugin
{
public IContainer Container { get; set; }
public T Resolve<T>()
{
return Container.Resolve<T>();
}
}
[PluginProviderType(typeof(DependencyInjectionPluginProvider))]
public class DependencyInjectionPluginConfiguration : IPluginConfiguration
{
public void WriteBinary(IBinaryRawWriter writer)
{
// No-op
}
public int? PluginConfigurationClosureFactoryId { get; } = null; // No Java part
}
public class DependencyInjectionPluginProvider : IPluginProvider<DependencyInjectionPluginConfiguration>
{
public string Name { get; } = "DependencyInjection";
public string Copyright { get; } = "MIT";
protected DependencyInjectionPlugin DependencyInjectionPlugin { get; set; }
public T GetPlugin<T>() where T : class
{
return DependencyInjectionPlugin as T;
}
public void Start(IPluginContext<DependencyInjectionPluginConfiguration> context)
{
DependencyInjectionPlugin = new DependencyInjectionPlugin();
}
public void Stop(bool cancel)
{
}
public void OnIgniteStart()
{
}
public void OnIgniteStop(bool cancel)
{
}
}
In main project, that is responsible for wiring up all components, I've implemented IContainer, defined previously, and registered it in Castle Windsor:
public class DependencyInjectionContainer : IContainer
{
protected IKernel Kernel { get; set; }
public DependencyInjectionContainer(IKernel kernel)
{
Kernel = kernel;
}
public T Resolve<T>()
{
return Kernel.Resolve<T>();
}
}
public class DependencyInjectionInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<IContainer>()
.ImplementedBy<DependencyInjectionContainer>()
);
}
}
In the very same project I've registered Apache Ignite.NET
public class IgniteInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<IIgnite>()
.UsingFactoryMethod(() => Ignition.Start(new IgniteConfiguration
{
PluginConfigurations = new[] {new DependencyInjectionPluginConfiguration()}
}))
);
}
}
Finally, in application's main method:
// Build Windsor container
using (var container = new WindsorContainer())
{
// Install DI abstraction layer
container.Install(new DependencyInjectionInstaller());
// Install cluster abstraction layer
container.Install(new IgniteInstaller());
// Attach DI container to cluster plugin
container
.Resolve<IIgnite>()
.GetPlugin<DependencyInjectionPlugin>("DependencyInjection")
.Container = container.Resolve<IContainer>();
// Wait
Done.Wait();
}
That's it. From now on, I am able to access IContainer implementation in Apache Ignite.NET distributed service like this:
var plugin = Ignite.GetPlugin<DependencyInjectionPlugin>("DependencyInjection");
var whatever = plugin.Resolve<IWhatever>();
The ServiceStack AppHost provides a Funq.Container with which to register types that can be injected into Services as they are constructed. Can this container be used to register an ILog factory that returns an ILog appropriate for the type in which it will reside?
Put another way, given the following AppHost:
public class AppHost : AppHostBase
{
public AppHost() : base("Example Web Services", Assembly.GetExecutingAssembly())
{
}
public override void Configure(Funq.Container container)
{
var baseLogFactory = new ServiceStack.Logging.NLogger.NLogFactory();
LogManager.LogFactory = new ServiceStack.Logging.Elmah.ElmahLogFactory(baseLogFactory);
// Would prefer to register a Func<Type, ILog> one time here
}
}
And a Service:
public class FooService : IService<FooRequest>
{
static ILog Log { get { return LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); } }
// Would prefer:
// public ILog { get; set; }
public object Execute(FooRequest request)
{
Log.Info("Received request: " + request.Dump());
return new FooResponse();
}
}
Is there anything I can add to AppHost.Configure to avoid the static ILog boilerplate in all of my Services (and instead just use a plain old ILog property)?
Put a third way, most succinctly, Can I use Funq.Container for ILog injection instead of LogManager?
container.Register<ILog>(
ctx => LogManager.LogFactory.GetLogger(typeof(IService))
);
Now your service could look like this:
public class FooService : IService<FooRequest>
{
public ILog { get; set; }
public object Execute(FooRequest request)
{
Log.Info("Received request: " + request.Dump());
return new FooResponse();
}
}
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;
}