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();
}
}
Related
I am using Simple Injector to register a concrete type in the container in a .NET Core console app (in Program.cs), but Simple Injector throws an exception on start up:
The constructor of type Application contains the parameter with name 'configUpdater' and type ConfigUpdater, but ConfigUpdater is not registered. For ConfigUpdater to be resolved, it must be registered in the container. An implicit registration could not be made because Container.Options.ResolveUnregisteredConcreteTypes is set to 'false', which is now the default setting in v5. This disallows the container to construct this unregistered concrete type. For more information on why resolving unregistered concrete types is now disallowed by default, and what possible fixes you can apply, see https://simpleinjector.org/ructd
EDIT:
Adding a MRE example which throws the exception:
using System.Threading.Tasks;
using NLog;
using SimpleInjector;
namespace MRE
{
public static class Program
{
private static Container container;
static Program()
{
container = new Container();
container.Register<IApplication, Application>(Lifestyle.Singleton);
var appSettings = new AppSettings();
container.Register(
typeof(AppSettings),
() => appSettings,
Lifestyle.Singleton
);
container.RegisterConditional(
typeof(ILog),
typeCtx => typeof(NLogProxy<>).MakeGenericType(typeCtx.Consumer.ImplementationType),
Lifestyle.Singleton,
predCtx => true
);
container.Register<IConfigUpdater, ConfigUpdater>(Lifestyle.Scoped);
}
public static void Main(string[] args)
{
var application = container.GetInstance<IApplication>();
application.RunAsync();
}
}
public class AppSettings
{
public string ConnectionString { get; set; } = "DataSource=data.db";
}
public interface ILog
{
void Info(string message);
}
public class NLogProxy<T> : ILog
{
private static readonly NLog.ILogger Logger = LogManager.GetLogger(typeof(T).FullName);
public void Info(string message) => Logger.Log(LogLevel.Info, message);
}
public interface IApplication
{
Task RunAsync();
}
public class Application : IApplication
{
private readonly ILog logger;
private readonly IConfigUpdater configUpdater;
public Application(
ILog logger,
IConfigUpdater configUpdater
)
{
this.logger = logger;
this.configUpdater = configUpdater;
}
public Task RunAsync()
{
logger.Info("Running");
configUpdater.DoTask();
return Task.CompletedTask;
}
}
public interface IConfigUpdater
{
Task DoTask();
}
public class ConfigUpdater : IConfigUpdater
{
private readonly AppSettings appSettings;
private readonly ILog logger;
public ConfigUpdater(
AppSettings appSettings,
ILog logger
)
{
this.appSettings = appSettings;
this.logger = logger;
}
public Task DoTask()
{
var connectionString = appSettings.ConnectionString;
logger.Info(connectionString);
return Task.CompletedTask;
}
}
}
EDIT #2:
With the help of the MRE, I discovered my issue was actually hiding behind the scenes. It was a issue with using Lifestyle.Scoped which for some reason was not the first exception thrown. Setting the default lifestyle to AsyncScopedLifestyle fixes it.
With the help of the MRE, I found that the actual error was to do with the default Lifestyle SimpleInjector was using. Adding the line:
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
fixes the issue of this question.
As to why the Lifestyle exception wasn't thrown first, I don't know.
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>();
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?
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.
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;
}