Ни классу Page, ни докладчикам не нужно напрямую иметь дело с управлением конструированием или жизненным циклом любой из его зависимостей - все это должно обрабатываться вашим контейнером. Поскольку внедрение в конструктор не работает с WebForms, вам нужно будет указать все необходимые зависимости в качестве свойств класса. Например, вы можете изменить свой класс на:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
public _DefaultPresenter Presenter { get; set; }
}
Страница не должна нуждаться в какой-либо ссылке на хранилище, поскольку она будет вставлена в докладчик.
Остальная часть этого ответа относится к StructureMap - детали могут отличаться для других контейнеров.
Чтобы включить установку сеттера, вам нужно указать StructureMap, какие свойства заполнять. Одним из способов является применение атрибута [SetterProperty] к самому свойству. Тем не менее, это может показаться немного инвазивным, чтобы иметь детали StructureMap в ваших классах. Другой способ - настроить StructureMap так, чтобы он знал, какие типы свойств вводить. Например:
protected void Application_Start(object sender, EventArgs e)
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
x.ForRequestedType<IRepository>().TheDefaultIsConcreteType<Repository>().CacheBy(InstanceScope.Hybrid);
x.SetAllProperties(set => set.WithAnyTypeFromNamespaceContainingType<IRepository>());
});
}
Метод SetAllProperties позволяет вам сообщать StructureMap, как распознать свойства, которые он должен заполнить. В этом случае я говорю, что StructureMap внедряет всех докладчиков (при условии, что они все находятся в одном пространстве имен).
Вам по-прежнему необходимо вводить сеттер для каждого запроса. В StructureMap вы используете метод BuildUp () для внедрения зависимостей в существующий экземпляр. Вы можете сделать это в событиях Init или Load каждой страницы или базового класса страницы, но опять же, это кажется инвазивным. Чтобы полностью исключить контейнер из ваших классов страниц, вы можете использовать событие PreRequestHandlerExecute приложения (в global.asax или IHttpModule):
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var page = application.Context.CurrentHandler as Page;
if (page == null) return;
ObjectFactory.BuildUp(page);
}
Наконец, если вы хотите явно удалить свой IRepository, вы можете обработать это в событии EndRequest:
protected void Application_EndRequest(object sender, EventArgs e)
{
var disposable = ObjectFactory.GetInstance<IRepository>() as IDisposable;
if (disposable != null) disposable.Dispose();
}
Обратите внимание, что это работает правильно, потому что при инициализации мы указали StructureMap кэшировать IRepository с помощью Hybrid, что означает «дать мне один и тот же экземпляр для каждого HTTP-запроса (или потока, если он не работает на веб-сайте)». Когда вы получаете IRepository в EndRequest, вы получите тот же, который использовался в запросе, и вы можете его утилизировать.