当函数属于必须解析的类时,如何向 IServiceCollection 注册委托或函数?

Sco*_*nen 2 .net c# dependency-injection

我正在使用 Microsoft.Extensions.DependencyInjection 中的 IServiceCollection/IServiceProvider。

我想将一个委托注入一个类:

public delegate ValidationResult ValidateAddressFunction(Address address);

public class OrderSubmitHandler
{
    private readonly ValidateAddressFunction _validateAddress;

    public OrderSubmitHandler(ValidateAddressFunction validateAddress)
    {
        _validateAddress = validateAddress;
    }

    public void SubmitOrder(Order order)
    {
        var addressValidation = _validateAddress(order.ShippingAddress);
        if(!addressValidation.IsValid)
            throw new Exception("Your address is invalid!");
    }
}
Run Code Online (Sandbox Code Playgroud)

ValidateAddressFunction我要注入的实现来自一个必须从容器解析的类,因为它有自己的依赖项:

public class OrderValidator
{
    private readonly ISomeDependency _dependency;

    public OrderValidator(ISomeDependency dependency)
    {
        _dependency = dependency;
    }

    public ValidationResult ValidateAddress(Address address)
    {
        // use _dependency
        // validate the order
        // return a result
        return new ValidationResult();
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我使用了一个委托,但我也可以注入Func<Address, ValidationResult>.

我可以只注入OrderValidator,但我不想只用一种方法创建一个接口。如果我的班级需要的只是一种方法,那么我宁愿直接依赖它。

我如何注册委托或Func以这样的方式在解析时,包含该方法的类将被解析,然后我可以使用解析实例中的方法?

Sco*_*nen 5

注册委托或Func<Address, ValidationResult>使用解析提供方法的类型的工厂方法,然后返回该方法。

在您的示例中,您希望解析OrderValidator并返回其ValidateAddress方法。

// register the class that provides the method and its dependencies.
services.AddSingleton<OrderValidator>();
services.AddSingleton<ISomeDependency, SomeDependency>();

// register the delegate using a factory method that resolves 
// the type that provides the method and then returns the method.
services.AddSingleton<ValidateAddressFunction>(serviceProvider =>
{
    var validator = serviceProvider.GetRequiredService<OrderValidator>();
    return validator.ValidateAddress;
});
Run Code Online (Sandbox Code Playgroud)

如果您注册Func<Address, ValidationResult>而不是委托,这将完全相同:

services.AddSingleton<Func<Address,ValidationResult>>(serviceProvider =>
{
    var validator = serviceProvider.GetRequiredService<OrderValidator>();
    return validator.ValidateAddress;
});
Run Code Online (Sandbox Code Playgroud)

您可以使用扩展程序简化注册。它并没有那么短,但如果您可能有多个这样的注册,它仍然有帮助。它也可能有助于表达您的意图,因此很明显您正在注册要注入的委托,而不是类实例或接口实现:

public static class ServiceCollectionDelegateExtensions
{
    public static IServiceCollection RegisterDelegateFromService<TService, TDelegate>(
        this IServiceCollection serviceCollection,
        Func<TService, TDelegate> getDelegateFromService)
        where TDelegate : Delegate
    {
        return serviceCollection.AddTransient(serviceProvider =>
            getDelegateFromService(serviceProvider.GetRequiredService<TService>()));
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,委托是注册使用的,AddTransient因为类的生命周期实际上是在注册类时确定的。

现在注册看起来像这样:

services.RegisterDelegateFromService<OrderValidator, ValidateAddressFunction>
    (validator => validator.ValidateAddress);
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。这吸引我的原因是接口更容易污染。我发现自己创建了很多单一方法的接口,并且我不希望将随机的东西添加到其中。使用委托有助于避免这种情况。这是最终的接口隔离。 (3认同)