Unable to inject Entity Repository into service - dependency-injection

I'm currently setting up an application with Symfony 3 and Doctrine 2.5 and I'm trying to inject an entity repository into a service and I keep getting the following error:
Type error: Argument 1 passed to UserService::setUserRepository() must
be an instance of UserRepository, instance of
Doctrine\ORM\EntityRepository given, called in
appDevDebugProjectContainer.php on line 373
This is how wire things up in my services.yml:
service_user:
class: UserService
calls:
- [setUserRepository, ["#service_user_repository"]]
service_user_repository:
class: UserRepository
factory: ["#doctrine.orm.entity_manager", getRepository]
arguments: [Entity\User]
This is my UserService:
<?php
class UserService {
protected $userRepository;
public function setUserRepository( UserRepository $userRepository )
{
$this->userRepository = $userRepository;
}
}
And this is my UserRepository:
<?php
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository {
}
I have checked and double checked my namespaces and class names, all seem to check out fine.
How do I inject an entity repository into a service with Symfony 3 service wiring?

As mentioned in comment, everything you've showed looks fine.
But since you're getting from entity manager an EntityRepository instance instead of UserRepository, it means that you didn't configured User entity to have custom (UserRepository) repository class.
If you use YAML mapping, it should be something like:
Entity\User:
repositoryClass: UserRepository
# rest of mapping

Related

Resolve all already created service instances from .NET service provider

I would like to request all created instances from a transient service via the IServiceProvdier. My problem is that requesting them seems to create additional instances instead of retrieving only the already existing instances.
I have a service interface and implementation
public interface ISomeService {}
public class SomeService : ISomeService
{
public SomeService()
{
}
}
It is registered transient
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeService, SomeService>();
}
Another service where I try to get all already created services
public class AnotherService
{
// calls the constructor of SomeService
//public AnotherService(IEnumerable<ISomeService> instances) {}
public AnotherService(IServiceProvider serviceProvider)
{
// calls the constructor of SomeService
//IEnumerable<ISomeService> instances = serviceProvider.GetServices<ISomeService>();
// calls the constructor of SomeService
//IEnumerable<ISomeService> instances = serviceProvider.GetRequiredServices<ISomeService>();
}
}
I don't know why the constructor of SomeService is called but it definitly seems to do it due to the calls of Get...
Anyone managed to get the list of instances without creating one?
By definition a transient service will always be created each time you request the service provider or the DI system to resolve it.
If you want to reuse the same instances you can register services with a different lifetime
services.AddSingleton<ISomeService, SomeService>();
or
services.AddScoped<ISomeService, SomeService>();
if you register the dependency as a singleton than there will be a single instance of there service for the entire application lifetime.
if you register the dependency with the scoped lifetime than a new instance will be created for each scope. In Asp.Net a scope consists of a request.
If you want your dependency to be transient and want to have track of all the instances that have been created you can do a little trick using static references:
public static class SomeServiceReferences {
public static readonly IList<ISomeService> References { get; } = new List<ISomeService>();
}
public class SomeService: ISomeService {
public SomeService() {
SsomeServiceReferences.References.Add(this);
}
}
but I don't reccomend this approach cause holding the reference of those dependencies might cause performance problems and if you need to do such a thing there might be some problems with the design of your application.

Injecting Different Implementations of Same Interface in ASP.NET 5

In ASP.NET 5, I have an IRepository interface that I used to access some databases, like this:
public interface IRepository {
IQueryable<T> QueryItems(string sql);
}
public class Repository : IRepository {
private readonly string ConnectionString;
public Repository(string connStr) {
// Save the injected connection string
this.ConnectionString = connStr;
}
public IQueryable<T> QueryItems(string sql) {
// Implementation ignored here
}
}
In my Startup.cs class, I am registering the IoC/DI like this:
services.AddTransient<IRepository>(s => new Repository("DUMMY_CONNSTR"));
That all works fine if I only have one connection string. However, how can I register and subsequently inject the correct IRepository if I use the Repository to connect to 2+ different databases with different connection strings?
services.AddTransient<IRepository>(s => new Repository("DUMMY_CONNSTR"));
services.AddTransient<IRepository>(s => new Repository("DIFFERENT_CONNSTR"));
In older IoC/DI systems, I would have use "named" implementations that could be resolved with something like a [Dependency("DUMMY")] attribute on the constructor parameter.
Any help would be appreciated.
There are a few approach that you can take one is to inject a factory and base on the specific criteria you can produce a repository, the other approach is use a Dispatcher that also produce the repository base on the criteria, below is a question that I ask with the same problem. The question below have both approach but they were codding a beta version of .net core
See this question for reference and code
You can substitute StructureMap or Autofac for the default DI container (see my blog post for detailed instructions). Both support "named" interface registration (StructureMap named instances and Autofac named and keyed services).
Additionally, if you target dnx451, you can use Autofac's WithKey attribute. Using the Visual Studio sample project from the blog post, add the following dependency in project.json:
"frameworks": {
"dnx451": {
"dependencies": {
"Autofac.Extras.AttributeMetadata": "4.0.0"
}
}
},
Given a test class with the following constructor:
public MyClass([WithKey("logging")] IRepository repository)
{
Repository = repository;
}
you would register everything in ConfigureServices (note the use of WithAttributeFilter():
containerBuilder.Register(c => new Repository("DEFAULT_CONNSTR")).Keyed<IRepository>("default");
containerBuilder.Register(c => new Repository("LOGGING_CONNSTR")).Keyed<IRepository>("logging");
containerBuilder.RegisterType<MyClass>().WithAttributeFilter();

how to Tell StructureMap to use a specific constructor?

have two services that require an XPathDocument. I want to be able to define named instances of XPathDocumnet to use in the configuration of the two services. I also want to be able to tell StuctureMap which constructor of XPathDocument to use. When I try to get an instance of XPathDocument it tells me that it can't find the plugged type for XmlReader. I want to use the constructor that requires a string uri for the xml file. I cannot seem to get this to work. Here is the StructureMap configuration code.
public class Service1 : IService1 {
public Service1(XPathDocument document) {}
}
public class Service2 : IService2 {
public Service2(XPathDocument document) {}
}
public class Registry1 : Registry {
ForRequestedType<IService1>().TheDefault.Is.OfConcreteType<Service1>()
.CtorDependency<XPathDocument>().Is(x => x.TheInstanceNamed("XPathDocument1"));
ForRequestedType<IService2>().TheDefault.Is.OfConcreteType<Service2>()
.CtorDependency<XPathDocument>().Is(x => x.TheInstanceNamed("XPathDocument2"));
ForRequestedType<XPathDocument>().AddInstances(x => {
x.OfConcreteType<XPathDocument>()
.WithCtorArg("uri").EqualToAppSetting("XmlFile1")
.WithName("XPathDocument1");
x.OfConcreteType<XPathDocument>()
.WithCtorArg("uri").EqualToAppSetting("XmlFile2")
.WithName("XPathDocument2");
});
}
Since XPathDocument is a framework type that is not under your control, you should register it with a factory delegate.
container.Configure(r => r.For<XPathDocument>()
.Use(() => new XPathDocument("XmlFile1"));
But since you need a dofference instance per service, you might be better of not registering this class itself, but only configure your services with a constructor argument where you specify the lambda delegate.

What does exactly Dependency Injection in Symfony2 do?

I registered my services.yml file like below :
services:
PMI.form.users_tasks:
class: PMI\UserBundle\Form\UsersTasksType
arguments:
EntityManager: "#doctrine.orm.default_entity_manager"
I can list it by php app/console container:debug, so that mean my service is registered properly.
In my UsersTasksType class I have like below :
class UsersTasksType extends AbstractType
{
protected $ur;
public function __construct(EntityManager $ur )
{
$this->setUr($ur);
}
// Get and setters
}
Does Dependency Injection mean that I don't have to pass the EntityManager to the class constructor anymore? Or what ?
Because when I have to run the code below :
$form = $this->createForm(new UsersTasksType(), $entity);
I get this error:
Catchable Fatal Error: Argument 1 passed to PMI\UserBundle\Form\UsersTasksType::__construct() must be an instance of Doctrine\ORM\EntityManager, none given, called in C:\wamp\www\PMI_sf2\src\PMI\UserBundle\Controller\UsersTasksController.php on line 74 and defined in C:\wamp\www\PMI_sf2\src\PMI\UserBundle\Form\UsersTasksType.php line 19
And I have to do something below :
$em = $this->container->get('doctrine.orm.entity_manager');
$form = $this->createForm(new UsersTasksType($em), $entity);
So what would be the whole purpose of Dependency Injection ?
Dependency Injection basically gives one service (in this case, your UserTasksType) access to another service (in this case, your the entity manager).
arguments:
EntityManager: "#doctrine.orm.default_entity_manager"
These two lines tell Symfony to expect the entity manager service to be passed into the constructor when you instantiate a new UserTasksType object, which effectively gives your UserTasksType access to the entity manager.
If you aren't using the entity manager in your UserTasksType, there is no need to inject it in the constructor and you could get rid of the two lines above and the __construct() / setUr() methods in your UserTasksType.
A better example to help you understand DIC might be that you have a service that is written specifically to send emails (Swiftmail, for e.g.) and you need to inject it into another service so that service can send emails.
By adding
arguments: [ #mailer ]
to your service definition, your services constructor will expect your mailer service
__construct ($mailer)
{
$this->mailer = $mailer;
}
which will give it access to send emails
someFunction()
{
//do something useful, then send an email using the swift mailer service
$this->mailer->sendEmail();
}
Check out the latest Symfony docs for more of an explanation.
http://symfony.com/doc/current/book/service_container.html

How do you output the context class using log4net as a service?

I am using Log4Net as a service which is injected into other services using StructureMap.
How do I ensure the log file includes the calling service class context (class name and/or thread) which is making the log4net calls?
Surely the calling class or thread will always be the logging service which doesn't help me understand where the logging calls are really coming from.
EDIT:
Register code:
ObjectFactory.Initialize(x =>
{
x.For<ILog>().AlwaysUnique().Use(s => s.ParentType == null ?
LogManager.GetLogger(s.BuildStack.Current.ConcreteType) :
LogManager.GetLogger(s.ParentType));
});
Service layer:
public class LoggerService : ILoggerService
{
private readonly ILog log;
public LoggerService(ILog logger)
{
log = logger;
log.Info("Logger started {0}".With(logger.Logger.Name));
}
public void Info(string message)
{
log.Info(message);
}
}
In the logging, I am still always getting the LoggerService as the context so I'll never see what actually called the logger. It doesn't seem to be working correctly. I feel like I'm missing something here...
Edit 2:
I've added a pastie link for a console app here:
http://pastie.org/1897389
I would expect the parent class to be logged but it isn't working at the simplest of levels.
You might want to have a look at Castle Dynamic proxy in order to solve it using AOP. There is an example of using it with Structure Map on the Structure Map Google Group.
Ayende has an example of AOP based logging using Log4Net and Windsor.
I use StructureMap in a lot of the code I generate and I have a StructureMap registry which I use to hook the logger into the context of the class that it is injected into.
For Reference, I'm using the 2.6.2 version of StructureMap but should be fine with 2.5+ where the new .For<>().Use<>() format is utilized.
public class CommonsRegistry : Registry
{
public CommonsRegistry()
{
For<ILogger>().AlwaysUnique().Use(s => s.ParentType == null ? new Log4NetLogger(s.BuildStack.Current.ConcreteType) : new Log4NetLogger(s.ParentType.UnderlyingSystemType.Name));
XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location), "Log.config")));
}
}
What this registry is doing is for anywhere the ILogger is injected, use the class that it's injected into is where the logging messages are logged to/context of.
*Also, in the second line (XmlConfigurator.ConfigureAndWatch) is where I tell Log4Net to get the logging information from the file "Log.config" instead of the application configuration file, you may or may not like that and can be omitted.
The code I use is a common IOC.Startup routine where I would pass if I would like to use the default registery.
ObjectFactory.Initialize(x =>
{
x.AddRegistry<CommonsRegistry>();
...
}
This gives me the calling class name in the logging instance where messages are logged to automatically and all that is required is to inject the logger into the class.
class foo
{
private readonly ILogger _log;
public foo(ILogger log)
{
_log = log;
}
}
Now the messages are logged as context/class "foo".

Resources