Как зарегистрировать делегат или функцию в Autofac, когда функция принадлежит к классу, который должен быть разрешен? - PullRequest
0 голосов
/ 06 июня 2019

Я использую Autofac. Я хочу внедрить делегата в класс:

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!");
    }
}

Реализация 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();
    }
}

В этом примере я использую делегата, но я также мог бы вводить Func<Address, ValidationResult>.

Я мог бы просто ввести OrderValidator, но я бы не хотел создавать интерфейс только одним методом. Если все мои потребности в классе - это один метод, я бы предпочел напрямую зависеть от этого.

Как зарегистрировать делегат или Func таким образом, чтобы при его разрешении класс, содержащий метод, был разрешен, а затем я мог использовать метод из разрешенного экземпляра?

1 Ответ

2 голосов
/ 06 июня 2019

Зарегистрируйте делегата или Func<Address, ValidationResult>, используя фабричный метод, который разрешает тип, который предоставляет метод, а затем возвращает метод.

В вашем примере вы захотите разрешить OrderValidator и вернуть его ValidateAddress метод.

var builder = new ContainerBuilder();

// register the class that provides the method and its dependencies.

builder.RegisterType<OrderValidator>();
builder.RegisterType<SomeDependency>().As<ISomeDependency>();

// register the delegate using a factory method that resolves 
// the type that provides the method and then returns the method.

builder.Register<ValidateAddressFunction>(context =>
{
    var validator = context.Resolve<OrderValidator>();
    return validator.ValidateAddress;
});

Это будет работать точно так же, если вы зарегистрируете Func<Address, ValidationResult> вместо делегата:

builder.Register<Func<Address, ValidationResult>>(context =>
{
    var validator = context.Resolve<OrderValidator>();
    return validator.ValidateAddress;
});

Вы можете упростить регистрацию, используя расширение. Это не намного короче, но все равно помогает, если у вас есть несколько таких регистраций. Это также может помочь выразить ваше намерение, поэтому совершенно очевидно, что вы регистрируете делегата для внедрения, а не для реализации класса или реализации интерфейса.

public static class AutofacDelegateExtensions
{
    public static IRegistrationBuilder<TDelegate, SimpleActivatorData, SingleRegistrationStyle> RegisterDelegateFromService<TService, TDelegate>(
        this ContainerBuilder builder,
        Func<TService, TDelegate> getDelegateFromService,
        string sourceComponentName = null)
        where TDelegate : class
    {
        var registrationFunction = new Func<IComponentContext, TDelegate>(context =>
        {
            var source = sourceComponentName == null
                ? context.Resolve<TService>()
                : context.ResolveNamed<TService>(sourceComponentName);
            return getDelegateFromService(source);
        });

        return builder.Register(registrationFunction);
    }
}

Регистрации Autofac по умолчанию являются временными, поэтому время жизни класса, который предоставляет делегат, определяется регистрацией этого класса.

Теперь регистрация будет выглядеть так:

builder.RegisterDelegateFromService<OrderValidator, ValidateAddressFunction>(validator =>
    validator.ValidateAddress);

Или, если зависимость, которая предоставляет метод - в этом случае OrderValidator зарегистрирована с именем, и мы должны разрешить его, используя это имя:

builder.RegisterDelegateFromService<OrderValidator, ValidateAddressFunction>(validator =>
    validator.ValidateAddress, "RegistrationName");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...