Dependency Injection of Logger in custom service - dependency-injection

I can't understand how the Dependency Injection works for custom services in SF 4.3 and PHP 7.2
In a controller, this simple code dumps an object Logger correctly initialized :
use Psr\Log\LoggerInterface;
/**
* #Route("/mytest", name="default_mytest")
*/
public function MyTestLoggerAction(LoggerInterface $logger) {
dump($logger);
return $this->render('default/index.html.twig');
}
But in a custom service called Guards in Guards.php, $logger is a null value :
namespace App\Workflow\CompanyDeploying\Transitions\Guards;
use Psr\Log\LoggerInterface;
class Guards {
private $logger;
public function setLogger(LoggerInterface $logger) {
$this->logger = $logger;
}
public function isValid() {
dump($this->logger);
}
}
i tried with :
using LoggerAwareTrait but nothing more happen, $logger always null.
adding #required on getLogger() and setting public to true in services.yml, $logger always null.
using public function isValid(LoggerInterface $logger) but all code who is asking for this isValid method returns "Too few arguments to function isValid()"
using a __contruct(LoggerInterface $logger) but anywhere i need this class, the code returns "Too few arguments to function __construct()"
First Edit
my services.yaml
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
public: false
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
App\EventListener\CompanyIndexer:
tags:
- { name: doctrine.event_listener, event: prePersist }
I tried to force autowire, autoconfig and visibility in services.yaml
App\Workflow\CompanyDeploying\Transitions\Guards\Guards:
autowire: true
autoconfigure: true
public: true
and add in my Guards.php
private $logger;
/**
* #required
*/
public function setLogger(LoggerInterface $logger) {
$this->logger = $logger;
}
public function isValid() {
dump($this->logger);
}
But no success. I always dump a null value.
2nd Edit
I call the Guard service from an EventSubscriberInterface who are listening to Wokflow event :
public static function getSubscribedEvents()
{
return [
'workflow.company_deploying.enter.mystate' => 'onEnter',
'workflow.company_deploying.leave.mystate' => 'onLeave',
'workflow.company_deploying.guard.mystate' => 'guardMyTransition',
];
}
public function guardMyTransition(Event $event) {
$this->event = $event;
if (! $this->guardFactory(__FUNCTION__)->isValid()) {
$event->setBlocked(true);
}
}
protected function guardFactory($guardName) {
$guard = GuardsFactory::create($guardName);
$guard->setCompany($this->event->getSubject());
if (isset($this->entityManager)) $guard->setEntityManager($this->entityManager);
if (isset($this->previousState)) $guard->setPreviousState($this->previousState);
return $guard;
}
My GuardFactory initialize a sub-class of Guards.
In the var/cache, i have a getGuardService.php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the public 'App\Workflow\CompanyDeploying\Transitions\Guards\Guards' shared autowired service.
include_once $this->targetDirs[3].'/src/Workflow/CompanyDeploying/Transitions/Guards/Guards.php';
$this->services['App\\Workflow\\CompanyDeploying\\Transitions\\Guards\\Guards'] = $instance = new \App\Workflow\CompanyDeploying\Transitions\Guards\Guards();
$instance->setLogger(($this->privates['monolog.logger'] ?? $this->getMonolog_LoggerService()));
return $instance;
I just need to easily use $logger (or any other service) in every class i need without writing ton of codes with a lot of setters.
Thanks for you help.
Solution
Dependency Injection doesn't work with this kind of factory call.

Related

How can I set up Lazy Loading with ZF3 (no ServiceLocator pattern from anywhere)

I am writing a new ZF2 app. I have noticed that ServiceLocator usage pattern of calling services "from anywhere" has been deprecated from ZF3. I want to write code in mind for ZF3.
I was able to set up my Controller to call all dependencies at constructor time. But that means loading i.e. Doctrine object upfront before I need it.
Question
How do I set it up so that it is only loaded when I need it immediately? (lazy-loaded). I understand that ZF3 moves loading to Controller construction, which makes it not apparent as to how to load something Just-In-Time.
Old Code
class CommissionRepository
{
protected $em;
function getRepository()
{
//Initialize Doctrine ONLY when getRepository is called
//it is not always called, and Doctrine is not always set up
if (! $this->em)
$this->em = $this->serviceLocator->get('doctrine');
return $this->em;
}
}
Current Code after Refactor of ServiceLocator pattern
class CommissionRepository
{
protected $em;
function getRepository()
{
return $this->em;
}
function setRepository($em)
{
$this->em = $em;
}
function useRepository($id)
{
return $this->em->find($id);
}
}
class CommissionControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $controllerManager->getServiceLocator();
// set up repository
$repository = new CommissionRepository();
$repository->setRepository($parentLocator->get('doctrine'));
// set up controller
$controller = new CommissionController($repository);
$controller->setRepository();
return $controller;
}
}
class CommissionController extends AbstractActionController
{
protected $repository;
public function setRepository(CommissionRepository $repository)
{
$this->repository = $repository;
}
public function indexAction()
{
//$this->repository already contains Doctrine but it should not
//I want it to be initialized upon use. How?
//Recall that it has been set up during Repository construction time
//and I cannot call it from "anywhere" any more in ZF3
//is there a lazy loading solution to this?
$this->repository->useRepository();
}
If you don't have any valid/strong reason to instantiate a custom entity repository, you should prefer extending of Doctrine\ORM\EntityRepository in your repositories like CommissionRepository. For example;
use Doctrine\ORM\EntityRepository;
class CommissionRepository extends EntityRepository
{
// No need to think about $em here. It will be automatically
// injected by doctrine when you call getRepository().
//
function fetchCommissionById($id)
{
// You can easily get the object manager directly (_em) or
// using getEntityManager() accessor method in a repository
return $this->_em->find($id);
}
}
By this way, entity manager will be automatically injected to the repository on construction when you call the $em->getRepository('App\Entity\Commission') method.
I assume that you already have a Commission entity in your app's Entity namespace:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repo\CommissionRepository")
* #ORM\Table
*/
class Commission
{
}
Then you can simplify the injecting process of the repository in your factory something like:
// ZF2 Way
class CommissionControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $services)
{
$em = $services->getServiceLocator()->get('doctrine');
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
}
UPDATE - With the release of Service Manager V3, FactoryInterface has been moved to Zend\ServiceManager\Factory namespace (1), factories are literally invokables (2) and works with any container-interop compatible DIC (3) Updated factory would be like below:
// ZF3 Way
use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
use Doctrine\ORM\EntityManager;
class CommissionControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $dic, $name, array $options = null) {
$em = $dic->get(EntityManager::class);
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
}
For the question; as of marcosh's said, Lazy Services are way to go to create services when need it immediately. ZF3 will use the zend-servicemanager 3.0 component when released. (Currently zend-expressive uses it) As of servicemanager v3 you can create some proxied services by defining lazy_services and delegators in your service configuration:
'factories' => [],
'invokables' => [],
'delegators' => [
FooService::class => [
FooServiceDelegatorFactory::class,
],
],
'lazy_services' => [
// map of service names and their relative class names - this
// is required since the service manager cannot know the
// class name of defined services up front
'class_map' => [
// 'foo' => 'MyApplication\Foo',
],
// directory where proxy classes will be written - default to system_get_tmp_dir()
'proxies_target_dir' => null,
// namespace of the generated proxies, default to "ProxyManagerGeneratedProxy"
'proxies_namespace' => null,
// whether the generated proxy classes should be written to disk or generated on-the-fly
'write_proxy_files' => false,
];
Also, starting with service manager v3 factories are compatible with the ContainerInterface. For the forward-compatibility, you may want to keep both __invoke() and createService() methods in your factories for a smooth migration.
In the end, your ZF3 compatible factory may look like:
class CommissionControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $name, array $options = null)
{
$em = $container->get('doctrine');
$repository = $em->getRepository('App\Entity\Commission');
return new CommissionController($repository);
}
public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
{
return $this($container, $requestedName, []);
}
}
Hope it helps.

Cannot access domain property from Java class (in src folder)

I cannot access exploits property in domain class - Scenario , from my java class - MatchScenario , located in Grails src folder.
Already tried :
Explicit methods :
I have tried explicitly creating the get;set; but I get stackOverflow error since the setExploits() is called infinitely by itself for some reason.
Service to return the exploit field,
Though the service was created, it's never called on my fork-debug integration testing, so tests hangs with no exception
compilation error ->
Error:(59, 44) java: cannot find symbol
symbol: variable exploits
location: variable scenario of type core.Scenario
Java class, error on the inner loop ->
public class MatchScenario implements Callable{
private static final Logger LOG = Logger.getLogger(MatchScenario.class.getCanonicalName());
private List<Scenario> scenarioList
#Override
public List<Scenario> call() throws Exception {
LOG.debug( "*********************** schedule matcher called *****************************" );
if (scenarioList==null) {
LOG.debug("scenarioList not initialized ");
return null;
}
List<Scenario> scenarioAvailable = new ArrayList<Scenario>();
for (Scenario scenario : scenarioList){
for (Exploit exploit : scenario.exploits){
//println 'exploit -> '+exploit
if (!match( exploit.getExpression() ) ){
break;
}
}
//happens only when all scenario are available ( no break issued )
scenarioAvailable.add(scenario);
}
return scenarioAvailable;
}
private boolean match(String expression) {
return true;
}
}
Scenario domain object ->
package core
class Scenario {
String name
static belongsTo = [ Exploit ]
static hasMany = [ exploits : Exploit ]
static constraints = {
name nullable: false , maxSize: 32
}
}
You're confusing fields and properties. When you declare a property in a Groovy class, e.g. String name, the Groovy compiler converts that to a private field and adds a getter and a setter (unless you already defined one or both of them - it won't overwrite), in this case something like
private String name
public void setName(String name) { this.name = name }
public String getName() { return name }
It only does this if there's no scope modifier, so public String name and protected String name would both stay as defined.
One benefit of this is that you can later add logic to the getter and/or setter to modify the value, do some validation checks or computations, etc., and in Groovy you would still read and write to the name property since property access always calls the underlying setters and getters, and since properties like this are a Groovy-only thing that Java can't access, you would have been calling the setter and getter from Java all along, so you wouldn't need to recompile the Java classes that used the class.
Declaring a hasMany like yours creates an exploits property, effectively
Set<Exploit> exploits
and that property (added by a Grails AST transformation) is likewise converted to a private field with a getter and setter. So to get this working from Java, use the getter:
for (Exploit exploit : scenario.getExploits()) {

Autofac get decorated QueryHandler by convention based on constructor parameter name?

We inject IQueryHandler<TQUery,TResult> into our MVC controllers. We globally register all of these in the container
We have written a decorator that can cache the results of IQueryHandler.
We want to sometimes get cached reults and other times not from the same handler.
Is it possible to conditionally get a decorated handler based on the name of the constructor parameter. e.g. inject IQueryHandler<UnemployedQuery, IEnumerable<People>> cachedPeopleHandler if we prefix constructor parameter name with cached we actually get it wrapped with decorator?
Just trying to use a more convention over configuration approach to simplify things.
Yes it's possible to do it. Below is a simple working example on how you can achieve it:
class Program
{
public interface IQueryHandler{}
private class QueryHandler : IQueryHandler
{
}
private class CacheQueryHandler : IQueryHandler
{
}
public interface IService
{
}
private class Service : IService
{
private readonly IQueryHandler _queryHandler;
private readonly IQueryHandler _cacheQueryHandler;
public Service(IQueryHandler queryHandler, IQueryHandler cacheQueryHandler)
{
_queryHandler = queryHandler;
_cacheQueryHandler = cacheQueryHandler;
}
public override string ToString()
{
return string.Format("_queryHandler is {0}; _cacheQueryHandler is {1}", _queryHandler,
_cacheQueryHandler);
}
}
static void Main(string[] args)
{
var builder = new ContainerBuilder();
// Register the dependency
builder.RegisterType<QueryHandler>().As<IQueryHandler>();
// Register the decorator of the dependency
builder.RegisterType<CacheQueryHandler>().Keyed<IQueryHandler>("cache");
// Register the service implementation
builder.RegisterType<Service>().AsSelf();
// Register the interface of the service
builder.Register(c =>
{
var ctor = typeof (Service).GetConstructors()[0];
var parameters =
ctor.GetParameters()
.Where(p => p.Name.StartsWith("cache"))
.Select(p => new NamedParameter(p.Name, c.ResolveKeyed("cache", p.ParameterType)));
return c.Resolve<Service>(parameters);
}).As<IService>();
using (var container = builder.Build())
{
var service = container.Resolve<IService>();
Console.WriteLine(service.ToString());
Console.ReadKey();
}
}
}
Update:
Basically you need to:
1. Think up a general convention. Prefix "cache" of ctor parameter name in your case.
2. Register your dependencies as usual.
3. Register your decorators, so they don't overwrite your original dependencies and you can easily resolve them basing on your convention. e.g. Keyed, Named, via Attribute, etc.
4. Register you actual implementation of class that uses decorators
5. Register your interface that describes the class via lambda expression that has all magic inside.
Note: I provided just a simple and working example. It's on you to make it nice, easy to use and fast e.g. make it as an extension, generic, cache reflection results etc. It's not difficult anyway.
Thanks.

Passing in the type of the declaring class for NLog using Autofac

Following on from this question I would like autofac to inject the type of the declaring object into the constructor of my NLog service, so that it can correctly log which type is logging entries.
My NLogService class looks like this...
public class NLogService : ILogService
{
private readonly Logger _logger;
public NLogService(Type t)
{
var consumerType = t.DeclaringType.FullName;
_logger = LogManager.GetLogger(consumerType);
}
However it fails on app startup because it obviously cannot work out what to inject into the constructor of the NLogService with the following error...
None of the constructors found with
'Public binding flags' on type
'MyProduct.Domain.Services.Logging.NLogService'
can be invoked with the available
services and parameters: Cannot
resolve parameter 'System.Type t' of
constructor 'Void .ctor(System.Type)'.
So, my question is - how do i instruct autofac to inject the type of the calling class?
I tried this...
public NLogService(Type t)
{
var method = MethodBase.GetCurrentMethod();
Type consumingType = method.DeclaringType;
var consumerType = consumingType.FullName;
var consumerType = t.DeclaringType.FullName;
_logger = LogManager.GetLogger(consumerType);
}
But i just end up with MyProduct.Domain.Services.Logging.NLogService
What i want is the type of the class that is doing the actual logging.
i have already tried this suggestion and it didnt work for me either.
Could make your NLogService generic, i.e. NLogService<T> and use Autofac's open generics support?
Then you could do this:
public class NLogService<T> : ILogger<T>
{
private readonly Logger _logger;
public NLogService()
{
_logger = LogManager.GetLogger(typeof(T).FullName);
}
}
There is no real good way to do this with Autofac, because does not have support for 'context based injection' (which is what you are trying to do). There is a workaround, but it aint pretty...
What you can do is revert to property injection and define a base class or interface for that ILogService property. For instance, you can define the following interface:
public interface ILoggerContainer
{
public ILogService Logger { get; set; }
}
Now you can implement this interface on all types that need a logger:
public class Consumer : IConsumer, ILoggerContainer
{
public ILogService Logger { get; set; }
}
With this in place you can configure Autofac as follows:
builder.RegisterType<ILoggerContainer>()
.OnActivating(e =>
{
var type = typeof(LogService<>)
.MakeGenericType(e.Instance.GetType());
e.Instance.Logger = e.Context.Resolve(type);
});
Another workaround, that you may find cleaner is to inject an ILogger<T> with the same type as the type of the parent type:
public class Consumer : IConsumer
{
public Consumer(ILogger<Consumer> logger) { }
}
This makes the configuration much easier and prevents you from having to have a base class. Which one is most appropriate is up to you.
As I said, these are workarounds, but to be honest, you might need to reconsider your logging strategy in your application. Perhaps you are logging at too many places. In the applications I write there is hardly ever a need to log, and when I do, I write an logging message that is expressive enough so that there is no need to communicate the type that triggered the event. And when you log exception, you will always have a complete stack trace (and exception logging should almost only happen in the outer layer of your application and not within services anyway).
The following technique works well in our experience:
Create an attribute like below, which can be applied at class level or at the injection site:
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class)]
public class LoggerAttribute : Attribute
{
public readonly string Name;
public LoggerAttribute(string name)
{
Name = name;
}
}
Create an Autofac module that you register with the ContainerBuilder:
public class LogInjectionModule : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
{
registration.Preparing += OnComponentPreparing;
}
static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
var typePreparing = e.Component.Activator.LimitType;
// By default, the name supplied to the logging instance is the name of the type in which it is being injected into.
string loggerName = typePreparing.FullName;
//If there is a class-level logger attribute, then promote its supplied name value instead as the logger name to use.
var loggerAttribute = (LoggerAttribute)typePreparing.GetCustomAttributes(typeof(LoggerAttribute), true).FirstOrDefault();
if (loggerAttribute != null)
{
loggerName = loggerAttribute.Name;
}
e.Parameters = e.Parameters.Union(new Parameter[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof (Logger),
(p, i) =>
{
// If the parameter being injected has its own logger attribute, then promote its name value instead as the logger name to use.
loggerAttribute = (LoggerAttribute)
p.GetCustomAttributes(typeof(LoggerAttribute),true).FirstOrDefault();
if (loggerAttribute != null)
{
loggerName = loggerAttribute.Name;
}
// Return a new Logger instance for injection, parameterised with the most appropriate name which we have determined above.
return LogManager.GetLogger(loggerName);
}),
// Always make an unamed instance of Logger available for use in delegate-based registration e.g.: Register((c,p) => new Foo(p.TypedAs<Logger>())
new TypedParameter(typeof(Logger), LogManager.GetLogger(loggerName))
});
}
}
You can now inject a named Logger in any one of these ways depending on individual scenarios:
By default, the injected logger name will be given the full type name of the class it is injected into:
public class Foo
{
public Foo(Logger logger)
{
}
}
Use a constructor parameter [Logger] attribute to override the logger name:
public class Foo
{
public Foo([Logger("Meaningful Name")]Logger logger)
{
}
}
Use a class-level [Logger] attribute to set the same logger name override for all constructor overloads:
[Logger("Meaningful Name")]
public class Foo
{
public Foo(Logger logger, int something)
{
}
public Foo(Logger logger, int something, DateTime somethingElse)
{
}
}
Use constructor parameter [Logger] attributes on each constructor overload to set different logger names depending on the context of how you were constructed:
public class Foo
{
public Foo(Logger("Meaningful Name")]Logger logger, int something)
{
}
public Foo(Logger("Different Name")]Logger logger, int something, DateTime somethingElse)
{
}
}
IMPORTANT NOTE: If you register types to be resolved with logger constructor injection using Autofac's delegate registration, you MUST use the two parameter overload like so: Register((c,p) => new Foo(p.TypedAs<Logger>()).
Hope this helps!
It is possible to do this without generics.
However, please note that in Autofac 6.x, the resolution process has changed to use a resolve pipeline. This doesn't matter for most scenarios, but it does when you want to use the lifetime events like OnPreparing, etc. Most of the answers here on SO around overriding the Preparing event are very old and are now outdated. You can't override Preparing directly anymore.
There is an example on the Autofac documentation site doing this for log4net, and it works with NLog with only minor changes. Here is the basic idea:
public class Log4NetMiddleware : IResolveMiddleware
{
public PipelinePhase Phase => PipelinePhase.ParameterSelection;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
// Add our parameters.
context.ChangeParameters(context.Parameters.Union(
new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(ILog),
(p, i) => LogManager.GetLogger(p.Member.DeclaringType)
),
}));
// Continue the resolve.
next(context);
// Has an instance been activated?
if (context.NewInstanceActivated)
{
var instanceType = context.Instance.GetType();
// Get all the injectable properties to set.
// If you wanted to ensure the properties were only UNSET properties,
// here's where you'd do it.
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);
// Set the properties located.
foreach (var propToSet in properties)
{
propToSet.SetValue(context.Instance, LogManager.GetLogger(instanceType), null);
}
}
}
}
Please also note that you have to understand how middleware works in Autofac. The documentation is a good place to start.

Set Inner Dependency by Type using Structuremap

I have a structuremap configuration that has me scratching my head. I have a concrete class that requires a interfaced ui element which requires an interfaced validation class. I want the outer concrete class to get the default ui element, but get a concrete-class-specific validation object. Something like this:
class MyView
{
IPrompt prompt
}
class GenericPrompt : IPrompt
{
IValidator validator
}
class MyValidator : IValidator
{
bool Validate() {}
}
How can I configure structuremap with the Registry DSL to only use MyValidator when creating dependencies for MyView. (And assumedly using BobsValidator when creating dependencies for BobsView)
Are you getting MyView (and BobsView) from the container? Can we assume that they will all take an instance of IPrompt?
One approach would be to register all of your validators with a name that matches the names of your view. You could implement your own type scanner that just removes the Validator suffix:
public class ValidatorScanner : ITypeScanner
{
public void Process(Type type, PluginGraph graph)
{
if (!typeof (IValidator).IsAssignableFrom(type)) return;
var validatorName = type.Name.Replace("Validator", "");
graph.AddType(typeof(IValidator), type, validatorName);
}
}
Now, if you assume an IPrompt will always be requested by a View that follows that naming convention, your registry could look like:
public class ValidatorRegistry : Registry
{
public ValidatorRegistry()
{
Scan(scan =>
{
scan.TheCallingAssembly();
scan.With<ValidatorScanner>();
});
ForRequestedType<IPrompt>().TheDefault.Is.ConstructedBy(ctx =>
{
var viewName = ctx.Root.RequestedType.Name.Replace("View", "");
ctx.RegisterDefault(typeof(IValidator), ctx.GetInstance<IValidator>(viewName));
return ctx.GetInstance<GenericPrompt>();
});
}
}
To retrieve your view with the appropriate validator, you would have to request the concrete type:
var view = container.GetInstance<MyView>();
Note that this will only work if you are retrieving your view with a direct call to the container (service location), since it depends on the "Root.RequestedType". Depending on how you plan to get your views, you might be able to walk up the BuildStack looking for a View (instead of assuming it is always Root).

Resources