1

我有一个Product对象的扩展类。

有什么方法可以注入我需要的服务类而不将它们作为方法参数传递?

public static class ProductExtensionMethods
{
    public static void CalculatePrice(this Product product, 
        ICalculateProductPriceService _calculatePriceService, 
        IUnitOfWork _unitOfWork)
    {
        // Can I inject the unitOfWork and Service without 
        // passing them as parameters?

        product.Price = _calculatePriceService.Calculate();
        _unitOfWork.Commit();
    }
}
4

2 回答 2

3

您不能对任何 DI 容器 afaik 执行此操作,因为尽管您可以在静态类上定义构造函数,但该构造函数必须是无参数的。

我能看到这对你有用的唯一方法是

  • 在 Product 实例上定义 CalculatePrice,但实例函数只是通过对静态方法的调用。通过构造函数将依赖项注入到实例中,并通过实例调用中的静态调用传递它们。

  • 创建一个执行相同操作的“帮助器”类(例如 ProductPriceHelper)并在其上实现 Setup 或 Initialise 方法以获取依赖项,但您仍然无法*为您自动注入 DI - 您必须在你的作文腐烂的某个地方手动进行(即你当前在哪里进行所有的 Ninject 绑定)。

  • 秘密门#3:我倾向于重新安排您的价格计算方式;我会谦虚地建议您ICalculateProductPriceService应该有一个接受 Product 实例并在其中执行其魔法的方法。然后ICalculateProductPriceService在构造过程中将依赖项注入到产品中,然后您调用一个方法Product.GetPrice()来调用ICalculateProductPriceService.Calculate(this)...如果您不能或不会在 ctor 期间注入服务(例如,如果它是 EF 实体等),那么您可以制作它对 GetPrice 的必需参数依赖项。

我意识到,话虽如此,毫无疑问有人会想出一个技术上出色的解决方法来实现这一点,但它永远是一个黑客......

于 2013-09-10T08:05:35.893 回答
3

似乎您正在扩展方法中实现用例。我认为这样做比创建普通类没有任何好处。因此,不要使用扩展方法,只需将此方法设为非静态,将其包装在非静态类中,并通过构造函数注入依赖项。

这可能看起来像这样:

public class CalculateProductPriceUseCase
{
    private ICalculateProductPriceService _calculatePriceService;
    private IUnitOfWork _unitOfWork;

    public CalculateProductPriceUseCase(
        ICalculateProductPriceService calculatePriceService,
        IUnitOfWork unitOfWork)
    {
        _calculatePriceService = _calculatePriceService;
        _unitOfWork = unitOfWork;
    }

    public void Handle(Product product)
    {
        product.Price = _calculatePriceService.Calculate();

        _unitOfWork.Commit();
    }
}

然而,这个解决方案仍然困扰我的是Commit电话。为什么用例本身必须调用commit。这对我来说似乎是一个基础设施,很容易忘记实现它。特别是因为该类不创建 unitOfWork 本身,而是从外部获取它。

相反,您可以将此用例包装在为您执行此操作的装饰器中,但是...如果您这样做,则需要添加适当的抽象,以便可以将所有用例包装在此类装饰器中。它可能看起来像这样:

public class CalculateProductPrice
{
    public int ProductId { get; set; }
}

public class CalculateProductPriceUseCaseHandler
    : IUseCase<CalculateProductPrice>
{
    private ICalculateProductPriceService _calculatePriceService;
    private IUnitOfWork _unitOfWork;

    public CalculateProductPriceUseCaseHandler(
        ICalculateProductPriceService calculatePriceService,
        IUnitOfWork unitOfWork)
    {
        _calculatePriceService = _calculatePriceService;
        _unitOfWork = unitOfWork;
    }

    public void Handle(CalculateProductPrice useCase)
    {
        var product = _unitOfWork.Products.GetById(useCase.ProductId);

        product.Price = _calculatePriceService.Calculate();
    }
}

消费者现在可以依赖一个IUseCase<CalculateProductPrice>,你可以给他们一个CalculateProductPriceUseCaseHandler实例,或者将该实例包装在一个装饰器中。这是此类装饰器的示例:

public class TransactionUseCaseDecorator<T> : IUseCase<T>
{
    private IUnitOfWork _unitOfWork;
    private IUseCase<T> _decoratedInstance;

    public TransactionUseCaseDecorator(IUnitOfWork unitOfWork,
        IUseCase<T> decoratedInstance)
    {
        _unitOfWork = unitOfWork;
        _decoratedInstance = decoratedInstance;
    }

    public void Handle(T useCase)
    {
        // Example: start a new transaction scope 
        // (or sql transaction or what ever)
        using (new TransactionScope())
        {
            _decoratedInstance.Handle(useCase);

            // Commit the unit of work.
            _unitOfWork.Commit();
        }
    }
}

现在你有了一个可以包裹任何IUseCase<T>实现的通用装饰器。

于 2013-09-10T10:12:35.627 回答