Время жизни Autofac и поставщик по умолчанию в соответствующей области времени жизни
У меня есть веб-приложение ASP.NET MVC, использующее Autofac для внедрения зависимостей. Иногда это веб-приложение запускает поток, выполняющий некоторую работу отдельно от потока запроса. Когда этот фоновый поток запускается, он устанавливает новую область действия Autofac из корневого контейнера и выполняет некоторое действие.
public IAsyncResult Run<T>(Action<T> action)
{
var NewTask = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
using (var Scope = Runtime.Container.BeginLifetimeScope())
{
var Input = Scope.Resolve<T>();
action(Input);
}
});
return NewTask;
}
Одна из моих зависимостей, зарегистрированных в Autofac, имеет две разные реализации: одна, подходящая для времени жизни http-запроса, и другая, подходящая для всех других времен жизни. Я попытался зарегистрировать их следующим образом:
builder
.Register(c => new Foo())
.As<IFoo>()
.InstancePerLifetimeScope();
builder
.Register(c => new FooForHttp(HttpContext.Current))
.As<IFoo>()
.InstancePerMatchingLifetimeScope(WebLifetime.Request);
Автофак выбирает FooForHttp
для http запросов (как и ожидалось). Однако, когда мой фоновый поток раскручивается, любая попытка разрешить IFoo
приводит к исключению:
Из области, в которой был запрошен экземпляр, не видна область с тегом, совпадающим с httpRequest. Как правило, это указывает на то, что компонент, зарегистрированный в качестве запроса HTTP, запрашивается компонентом SingleInstance() (или аналогичным сценарием). При веб-интеграции всегда запрашивают зависимости из DependencyResolver.Current или ILifetimeScopeProvider.RequestLifetime, а не из самого контейнера.,
Я знаю, что Autofac всегда использует последнего зарегистрированного поставщика в качестве поставщика по умолчанию для определенного компонента. Я сделал здесь предположение, что он будет использовать последний зарегистрированный подходящий провайдер.
Я что-то упустил или есть лучший подход к выбору провайдера на основе тега текущей области действия?
2 ответа
Зарегистрируйте веб-Foo как обычно, но не регистрируйте другой Foo. При создании области действия для асинхронной задачи используйте перегрузку BeginLifetimeScope(), которая выполняет действие над ContainerBuilder. Зарегистрируйте фоновое Foo в этом действии (b => b.Register()), и это должно переопределить веб-действие. (Маленькая клавиатура здесь извините:))
Это также может быть решено с помощью тегов времени жизни. Зарегистрируйте свой первый Foo как экземпляр вашей помеченной области:
builder.RegisterType<Foo>().As<IFoo>.InstancePerMatchingLifetimeScope("YourScopeTag");
И создайте область с тем же тегом, что вы зарегистрировали свою зависимость:
using (var Scope = Runtime.Container.BeginLifetimeScope("YourScopeTag"))
{
var Input = Scope.Resolve<T>();
action(Input);
}
Не проверял, но это должно работать
http://docs.autofac.org/en/latest/lifetime/instance-scope.html