依赖注入 - 将参数传递给构造类型

6

我正在向我的项目引入依赖注入。我有一个对话框窗口,用作新实体或现有实体的编辑器。其ViewModel如下:

public class ContractWindowViewModel
{
    private IRepository<Contract> contractRepository;
    private Contract model;
    private bool isNew;

    public ContractWindowViewModel(Contract contract, IRepository<Contract> contractRepository)
    {
        if (contract == null)
            throw new ArgumentNullException(nameof(contract));
        if (contractRepository == null)
            throw new ArgumentNullException(nameof(contractRepository));

        this.contractRepository = contractRepository;
        this.model = contract;
        this.isNew = false;
    }

    public ContractWindowViewModel(IRepository<Contract> contractRepository)
    {
        this.contractRepository = contractRepository;
        this.model = new Contract();
        this.isNew = true;
    }

    // (...)
}

计划是注入IRepository<Contract>。但有时我需要将Contract传递给ViewModel(如果我想编辑现有的),或者不需要(如果我想创建新的)。如何使用Unity实现这个功能呢?


你需要在应用程序运行时能够创建这个视图模型,对吧?例如当用户点击某个按钮时?也许根据用户点击的按钮,你会确定调用哪个构造函数,对吧? - Yacoub Massad
你是否应该让你的 ViewModel 仅依赖于 Contract 类,而不是直接与仓库通信?为什么需要知道 Contract 是否为新的呢? - Yacoub Massad
@YacoubMassad 当然,但按钮单击处理程序将调用 unityContainer.Resolve<ContractWindowViewModel>()。 我如何通过该结构传递参数? - Spook
2
你的问题源于将运行时数据(在这种情况下是“Contract”实例)注入到组件的构造函数中(即“ContractWindowViewModel”)。你的对象图应该是无状态的,并且运行时数据应该通过方法调用传递到现有的对象图中。每当你违反这个想法时,你会让一切变得更加困难(正如你已经经历的那样)。因此,不要使用构造函数注入“Contract”,而是在你的视图模型上定义一个允许设置现有合同的方法。 - Steven
2个回答

7

在代码中使用DI容器(例如,在按钮事件处理程序中)被称为服务定位,被认为是一种反模式

相反,你应该有一个工厂,允许你从按钮处理程序(或ViewModel命令)内部创建你的视图模型。

这是一个工厂的例子:

public interface IContractWindowViewModelFactory
{
    ContractWindowViewModel CreateForNewContract();

    ContractWindowViewModel CreateForExistingContract(Contract existing_contract);
}

public class ContractWindowViewModelFactory : IContractWindowViewModelFactory
{
    private readonly IRepository<Contract> m_Repository;

    public ContractWindowViewModelFactory(IRepository<Contract> repository)
    {
        m_Repository = repository;
    }

    public ContractWindowViewModel CreateForNewContract()
    {
        return new ContractWindowViewModel(m_Repository);
    }

    public ContractWindowViewModel CreateForExistingContract(Contract existing_contract)
    {
        return new ContractWindowViewModel(existing_contract, m_Repository);
    }
}

现在您需要将 IContractWindowViewModelFactory 注入到需要能够创建 ContractWindowViewModel 视图模型的类中(例如通过构造函数注入)。
由于您正在使用 DI 容器,您需要在组合根中注册IContractWindowViewModelFactoryContractWindowViewModelFactory。您还需要注册IRepository<Contract>(我猜您已经完成了这个步骤)。
现在,在您的按钮处理程序(或命令处理程序)中,您可以使用工厂为新的或现有的Contract创建一个ContractWindowViewModel
如果出于某种原因,您仍然想要在按钮处理程序中使用容器(我建议您不要这样做),那么您可以像这样使用命名注册
在您的组合根中:
container.RegisterType<ContractWindowViewModel>(
    "ForNew",
    new InjectionConstructor(
        new ResolvedParameter<IRepository<Contract>>()));

container.RegisterType<ContractWindowViewModel>(
    "ForExisting",
    new InjectionConstructor(
        new ResolvedParameter<Contract>(),
        new ResolvedParameter<IRepository<Contract>>()));

在您的处理程序中,您可以使用此功能来创建新的合同:
var viewmodel_for_new = container.Resolve<ContractWindowViewModel>("ForNew");

或者对于现有的合同:

Contract existing_contract = ...
var viewmodel_for_existing = container.Resolve<ContractWindowViewModel>(
    "ForExisting",
    new ParameterOverride("contract", existing_contract));

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接