Ninject:注入扩展方法

时间:2013-09-10 07:36:53

标签: c# dependency-injection ninject extension-methods

我有一个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();
    }
}

2 个答案:

答案 0 :(得分:3)

你不能用任何DI容器来做这件事,因为虽然你可以在静态类上定义构造函数,但构造函数必须无参数。

我能看到这个适合你的唯一方法就是

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

  • 创建一个执行相同操作的“帮助器”类(例如ProductPriceHelper),并在其上实现一个设置或初始化方法来获取依赖关系,但您仍然不能*为您自动注入DI - 你必须在你的作品腐烂的某个地方手动完成(即你现在所有你的Ninject绑定的地方)。

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

我意识到说过这个人无疑会想出一个技术上优秀的解决方案来实现这个目标,但它总是会被黑客攻击......

答案 1 :(得分: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>实现的通用装饰器。