保持模块依赖规则下使用mediatr与dependency_injector注入报错求助
依赖注入错误排查与解决:
AttributeError: 'Provide' object has no attribute 'get_person' 问题背景
模块依赖关系如下:
- application(依赖domain)
- domain(无依赖)
- persistence(依赖application)
- main.py(依赖所有模块)
使用mediatr和dependency_injector实现依赖注入时,触发错误:
AttributeError: 'Provide' object has no attribute 'get_person'
错误原因分析
- 装饰器顺序错误:
@inject装饰器放在@Mediator.handler之前,导致dependency_injector无法正确拦截Handler类的实例化,依赖未被注入,self.db仍是Provide对象而非实际的DBConnection实例。 - 容器依赖未正确传递:主容器
Container中,application_container和persistence_container独立创建,未建立依赖关联,导致ApplicationContainer的dbconnection依赖未被填充。 - 接口与实现参数不匹配:
IDBConnection定义的get_person参数id类型为str,但DBConnection实现中为int,会触发类型错误。 - Wire范围不足:仅对
__name__(main模块)进行wire,未覆盖application层模块,导致Handler类的注入逻辑未生效。
修复步骤
1. 调整装饰器顺序
将@inject移至@Mediator.handler之后,确保注入逻辑优先执行:
@Mediator.handler @inject class PersonQueryHandler: # ... 原有代码 ...
2. 修正主容器的依赖关联
在main.py的Container中,让application_container依赖persistence_container提供的database实例:
class Container(containers.DeclarativeContainer): mediator = providers.Singleton(Mediator) persistence_container = providers.Container(PersistenceContainer) application_container = providers.Container( ApplicationContainer, dbconnection=persistence_container.database )
3. 统一参数类型
修改persistence/storage.py中get_person的参数类型为str,与接口定义一致:
def get_person(self, id: str) -> Person: return Person(id=id, name="Razali", gender="Male")
4. 扩大Wire覆盖范围
在main函数启动时,将application层的模块加入wire范围:
container.wire(modules=[__name__, "application.personcontext.queries.person"])
完整修复代码
domain/models.py(无修改)
from dataclasses import dataclass @dataclass class Person: id: str name: str gender: str
application/interfaces/database.py(无修改)
from abc import ABC, abstractmethod from domain.models import Person class IDBConnection(ABC): @abstractmethod def get_person(self, id: str) -> Person: pass
application/containers.py(无修改)
from dependency_injector import containers, providers class ApplicationContainer(containers.DeclarativeContainer): dbconnection = providers.Dependency()
application/personcontext/queries/person.py(修改装饰器顺序)
from domain.models import Person from mediatr import Mediator from application.interfaces.database import IDBConnection from dependency_injector.wiring import inject, Provide from application.containers import ApplicationContainer class PersonQuery: def __init__(self, id: str) -> None: self.id = id @Mediator.handler @inject class PersonQueryHandler: def __init__(self, db: IDBConnection = Provide[ApplicationContainer.dbconnection]) -> None: self.db: IDBConnection = db def handle(self, request: PersonQuery): return self.db.get_person(request.id)
persistence/containers.py(无修改)
from dependency_injector import containers, providers from application.interfaces.database import IDBConnection from persistence.storage import DBConnection from application.containers import ApplicationContainer class PersistenceContainer(containers.DeclarativeContainer): database = providers.Singleton(IDBConnection, DBConnection()) application_container = providers.Container( ApplicationContainer, dbconnection=database )
persistence/storage.py(修改参数类型)
from application.interfaces.database import IDBConnection from domain.models import Person class DBConnection(IDBConnection): def get_person(self, id: str) -> Person: return Person(id=id, name="Razali", gender="Male")
main.py(修改容器关联与wire范围)
from mediatr import Mediator from application.personcontext.queries.person import PersonQuery from dependency_injector import containers, providers from dependency_injector.wiring import inject, Provide from persistence.containers import PersistenceContainer from application.containers import ApplicationContainer class Container(containers.DeclarativeContainer): mediator = providers.Singleton(Mediator) persistence_container = providers.Container(PersistenceContainer) application_container = providers.Container( ApplicationContainer, dbconnection=persistence_container.database ) @inject def main(mediator: Mediator = Provide[Container.mediator]): response = mediator.send(PersonQuery("1")) print(response) if __name__ == '__main__': container = Container() container.init_resources() # Wire main模块和application的handler模块 container.wire(modules=[__name__, "application.personcontext.queries.person"]) main()
验证结果
运行main.py后,将输出:
Person(id='1', name='Razali', gender='Male')
内容的提问来源于stack exchange,提问作者RStyle
相关产品推荐
相关产品推荐

