无法自动装配服务:参数引用类但不存在此类服务

Posted

技术标签:

【中文标题】无法自动装配服务:参数引用类但不存在此类服务【英文标题】:Cannot autowire service: Argument references class but no such service exists 【发布时间】:2018-06-09 23:46:46 【问题描述】:

我正在将一个项目从 Symfony 3 升级到 Symfony 4 (https://github.com/symfony/symfony/blob/master/UPGRADE-4.0.md) 我有很多这样的存储库/服务:

namespace App\Entity;

use App\Entity\Activation;
use Doctrine\ORM\EntityRepository;
use Predis\Client;

class ActivationRepository extends EntityRepository

    // ...

当我尝试像这样在浏览器中运行项目时:

http://localhost:8000/login

我收到此错误:

(1/1) RuntimeException
Cannot autowire service "App\Entity\ActivationRepository": 
argument "$class" of method 
"Doctrine\ORM\EntityRepository::__construct()" 
references class "Doctrine\ORM\Mapping\ClassMetadata" 
but no such service exists.

这是否意味着您必须在 services.yaml 文件中为 "Doctrine\ORM\Mapping\ClassMetadata" 创建服务?

感谢自动装配,与包含 2000 多行的旧文件相比,我的新 services.yaml 文件相当小。 新的 services.yaml 中只有几个(到目前为止):

App\:
    resource: '../src/*'

# Controllers
App\Controller\:
    resource: '../src/Controller'
    autowire: true
    public: true
    tags: ['controller.service_arguments']

# Models
App\Model\:
    resource: '../src/Model/'
    autowire: true
    public: true

// etc

问题: 你真的需要为第三方供应商类添加服务定义到 services.yaml 吗?如果是这样,我可以举一个如何做到这一点的例子吗? 任何已经从 Symfony 3 升级到 Symfony 4 的人的任何建议都会很棒。

php 7.2.0-2+ubuntu16.04.1+deb.sury.org+2 (cli)(构建时间:2017 年 12 月 7 日 20:14:31)(NTS) Linux Mint 18,Apache2 Ubuntu。

编辑/仅供参考:

这是 ActivationRepository 扩展的“Doctrine\ORM\EntityRepository::__construct()”:

/**
     * Initializes a new <tt>EntityRepository</tt>.
     *
     * @param EntityManager         $em    The EntityManager to use.
     * @param Mapping\ClassMetadata $class The class descriptor.
     */
    public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
    
        $this->_entityName = $class->name;
        $this->_em         = $em;
        $this->_class      = $class;
    

位于此处:

/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php

【问题讨论】:

是的。吉姆说什么。问题是,即使您有正确的依赖关系,您也无法简单地新建一个学说存储库。您需要使用 EntityManager::getRepository 方法,否则将无法正常工作。而且 autowire 还不够聪明,无法识别出这只是一个类型提示。 【参考方案1】:

从 1.8 版本的 DoctrineBundle 开始,您可以使用 Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository 而不是 Doctrine\ORM\EntityRepository 来扩展您的类。结果将是相同的,但这确实支持自动装配。

例子:

use App\Entity\Activation;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;

class ActivationRepository extends ServiceEntityRepository

    public function __construct(ManagerRegistry $registry)
    
        parent::__construct($registry, Activation::class);
    

    // ...

【讨论】:

谢谢 Federkun 和 Massimiliano Arione。不知道你们之间做了什么,但你只是帮助我在我的 Symfony 3.4 到 4 旅程结束时又迈出了一步。 请注意谁在使用doctrine-bundle 2.x 使用的doctrine/persistence &gt;= 1.3,使用Doctrine\Persistence\ManagerRegistry 而不是Doctrine\Common\Persistence\ManagerRegistry 我会提到这一点,因为他们必须创建一个构造函数并定义 entityClass,就像您在示例中所做的那样。【参考方案2】:

您真的需要为第三方供应商类添加服务定义到 services.yaml 吗?

不,不要那样做。我个人的建议是:不要扩展EntityRepository。曾经。您不希望您的存储库的接口具有像 createQueryflush 这样的方法。至少,如果您将存储库视为对象集合,则您不希望出现这种情况。如果你扩展 EntityRepository 你会有一个泄漏的抽象。

相反,您可以将 EntityManager 注入到您的存储库中,就是这样:

use App\Entity\Activation;
use App\Repository\ActivationRepository;
use Doctrine\ORM\EntityManagerInterface;

final class DoctrineActivationRepository implements ActivationRepository

    private $entityManager;
    private $repository;

    public function __construct(EntityManagerInterface $entityManager)
    
        $this->entityManager = $entityManager;
        $this->repository = $this->entityManager->getRepository(Activation::class);
    

    public function store(Activation $activation): void
    
        $this->entityManager->persist($activation);
        $this->entityManager->flush();
    

    public function get($id): ?Activation
    
        return $this->repository->find($id);
    

    // other methods, that you defined in your repository's interface.

不需要其他步骤。

【讨论】:

Doctrine 的存储库不仅仅是对象的集合。也不清楚存储方法如何适合对象集合。我明白你在说什么,但你的解决方案是矫枉过正。 是的,我知道,我在谈论collections 并使用store 方法的示例,它更面向持久性而不是面向集合。但是如果我用add 方法替换它,下一个问题是where I commit my changes?,我们使示例过于复杂。不过,我不认为这是矫枉过正。但我想这只是我个人的看法。就像所有事情一样,有利也有弊。使用 EntityManager 服务定位器也有利有弊。 注入 EntityManager 来获取存储库对象就像注入 Symfony DI 容器。如果无法自动装配存储库类,唯一的方法是在 services.yml 中定义它们。主题不是关于将持久逻辑放在哪里,而是关于自动装配 Doctrine 存储库类。 @VadimAshikhman,我不介意提供该特定问题的答案:***.com/a/48026278/711206 但您会注意到该解决方案甚至比这个更糟糕。 @Federkun,哇,不错的解决方案!在我个人看来,我认为这比注入管理器并调用它的 getRepository() 方法要好,因此您只能实例化仅对应于 1 个实体的存储库对象。【参考方案3】:

我的问题是命名空间错误。 文件真实位置为App\Infrastructure\mysql\Rubric\Specification 但是命名空间设置为App\Infrastructure\Rubric\Specification

结果“[blah blah] 但不存在这样的服务”。

【讨论】:

以上是关于无法自动装配服务:参数引用类但不存在此类服务的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot自动配置/装配(SPI)

无法自动装配服务 FOSUserBundle,Symfony 3.4

无法在控制器中自动装配多个服务

Autofac之自动装配

Symfony 3.4 自动装配服务

Spring框架的新手收到此错误:无法自动装配字段: