Подтвердить что ты не робот

Autofac - как разрешить Func для ISomething от Singleton, где ISomething является InstancePerHttpRequest

Я пытаюсь использовать Autofac для установки зависимостей в FluentValidation в приложении MVC 4. Я думаю, что у меня есть стратегия, но я застрял в решении моего запроса ISomething из одноэлементного.

Здесь сценарий: У меня есть валидатор, основанный на FluentValidation AbstractValidator. Я читал, что валидаторы FluentValidation лучше всего работают как одиночные, поэтому мой конструктор ожидает Func и сохраняет этот Factory для использования позже. Когда используется валидатор, он должен запросить сохраненный Factory для IDataStore, получить экземпляр, созданный для этого запроса, и использовать его. Это теория. Я хочу отдать должное https://github.com/robdmoore/UnobtrusiveMVCTechniques, что помогло мне решить это решение. Здесь валидатор...

public class SiteAdminViewModelValidator : AbstractValidator<SiteAdminViewModel> {
    private readonly Func<IDataStore> _dbFactory;

    public SiteAdminViewModelValidator(Func<IDataStore> dbFactory) {
        _dbFactory = dbFactory;

        RuleFor(model => model.SiteCode).Length(1, 40).Must(BeSpecial);
    }

    public bool BeSpecial(string siteCode) {
        var db = _dbFactory();
        List<Stuff> stuffs = db.All<Stuff>().ToList();

        return true;
    }
}

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

Здесь моя регистрация валидатора...

public class FluentValidatorModule : Module {
    protected override void Load(ContainerBuilder builder) {
        base.Load(builder);
        builder.RegisterType<AutofacValidatorFactory>().As<IValidatorFactory>().SingleInstance();

    var validators = AssemblyScanner.FindValidatorsInAssembly(System.Reflection.Assembly.GetExecutingAssembly());
    validators.ToList().ForEach(v => builder.RegisterType(v.ValidatorType).As(v.InterfaceType).SingleInstance());
    }
}

Здесь моя регистрация для IDataStore factory...

builder.RegisterType<SuperDB>().As<IDataStore>().InstancePerHttpRequest();
builder.Register<Func<IDataStore>>(c => {
                                       var context = c.Resolve<IComponentContext>();
                                       return context.Resolve<IDataStore>;
                                   });

Здесь ошибка, которую я получаю, когда мой валидатор запрашивает у IDataStore строку - var db = _dbFactory();

Никакая область с тегом, соответствующим "AutofacWebRequest", видна из область применения экземпляра. Это обычно указывает что компонент, зарегистрированный в качестве HTTP-запроса, запрашивается компонент SingleInstance() (или аналогичный сценарий.) В Интернете интеграция всегда запрашивает зависимости от DependencyResolver.Current или ILifetimeScopeProvider.RequestLifetime, никогда не из самого контейнера.

... это именно то, что я получил, когда пробовал это, прежде чем писать свою собственную регистрацию Factory - регистрацию Func. От чтения различных ответов на похожие вопросы было похоже, что то, над чем я работал выше, должно работать, потому что я решил, что теперь я разрешаю Func для получения текущего разрешения.

Любая помощь будет принята с благодарностью.

4b9b3361

Ответ 1

Я согласен с тем, что это должно сработать - Func<IDataStore> определяет factory, который будет производить зависимость в каждом методе по мере необходимости.

Способ, которым я получил этот метод, - использовать DependencyResolver.Current, как это предлагает сообщение об ошибке. Основная причина в том, что я уже установил его с помощью пакета Autofac.Mvc4 nuget...

DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

Итак, чтобы на самом деле настроить метод, у меня есть следующая функция

public Func<T> PerHttpSafeResolve<T>()
{
    return () => DependencyResolver.Current.GetService<T>();
} 

И при построении контейнера

builder.RegisterType<SuperDB>().As<IDataStore>().InstancePerHttpRequest();
builder.RegisterInstance(PerHttpSafeResolve<IDataStore>());

EDIT: вторая строка, регистрирующая экземпляр, говорит: если вам нужно Func<IDataStore>, используйте значение, переданное в этот метод. Результат PerHttpSafeResolve<IDataStore> - это просто функция (factory), поэтому он может жить как один экземпляр.

Ответ 2

Проблема заключается в объеме валидаторов. Autofac всегда разрешает зависимости SingleInstance от контейнера приложения, что означает, что зависимости валидаторов также поступают из контейнера приложения.

Autofac запрашивает экземпляр Func<IDataStore> оттуда, а не из контейнера запроса. Когда вы разрешаете IComponentContext, вы получаете контейнер, в котором разрешается валидатор: контейнер приложения. Поскольку Func<IDataStore> привязан к запросу, Autostac не может предоставить его на уровне приложения, следовательно, ошибка.

Правильный подход - зарегистрировать валидаторы как InstancePerHttpRequest. Время жизни компонента определяется его долговременной зависимостью; валидаторы работают лучше всего, как одиночные, когда у них нет зависимостей. Однако в этом случае валидатор, зависящий от IDataStore, вынужден жить не более чем на протяжении жизни экземпляра IDataStore. (С яркой стороны вы можете избавиться от Func.)

Подумайте, как пойти на вечеринку: если вы поймаете поездку с хозяином, вы должны остаться там, пока вечеринка не закончится. Вы не можете ехать с хозяином, если хотите рано уйти.