Я попробовал это сам, и, к сожалению, я не могу заставить его работать должным образом. В моем решении я делаю следующее:
public class WindsorViewPageActivator : IViewPageActivator
{
private readonly IKernel _kernel;
/// <summary>
/// Initializes a new instance of the <see cref="WindsorViewPageActivator"/> class.
/// </summary>
/// <param name="kernel">The kernel.</param>
public WindsorViewPageActivator([NotNull] IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public object Create(ControllerContext controllerContext, Type type)
{
if (!_kernel.HasComponent(type))
{
if (IsSupportedView(type))
{
_kernel.Register(Component.For(type).LifestylePerWebRequest());
}
else
{
return Activator.CreateInstance(type);
}
}
var viewPageInstance = _kernel.Resolve(type);
return viewPageInstance;
}
/// <summary>
/// Determines whether the specified type is of a supported view type.
/// </summary>
/// <param name="viewType">Type of the service.</param>
/// <returns>
/// <c>true</c> if the specified type is of a supported view type; otherwise, <c>false</c>.
/// </returns>
private static bool IsSupportedView(Type viewType)
{
return viewType.IsAssignableTo<WebViewPage>()
|| viewType.IsAssignableTo<ViewPage>()
|| viewType.IsAssignableTo<ViewMasterPage>()
|| viewType.IsAssignableTo<ViewUserControl>()
;
}
}
Это работает до тех пор, пока вы ничего не измените в своей разметке. Если вы это сделаете, вы получите ошибку регистрации, так как представление теперь сгенерирует новый тип, который не существует в контейнере (но он имеет то же имя!).
Я подумал о том, чтобы агрессивно освободить компонент представления, как только он будет разрешен, но по какой-то причине я не могу избавиться от него в контейнере. Даже явный вызов _kernel.ReleaseComponent(viewPageInstance)
не сделает этого (но это, конечно, просто освобождение экземпляра, а не типа).
Что нам действительно нужно сделать, так это заставить Виндзор вводить свойства в существующие экземпляры. То есть используйте Activator.CreateInstance(type)
, а затем скажите Виндзору, чтобы он вставил свойства в экземпляр. Но Виндзор не поддерживает внедрение свойств в существующие экземпляры, поэтому нам нужно что-то взломать, и это сделает это за нас.
Я видел это http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/ (внизу), но это не очень хорошо работает.
Мое решение состояло в том, чтобы просто вручную установить мои свойства в активаторе просмотровой страницы (у вас есть базовый тип просмотровой страницы), но, может быть, есть какое-то лучшее решение?
EDIT
Мне все-таки удалось заставить его работать!
Мое решение состоит в том, чтобы просто создать собственный активатор компонента и имитировать то, что делается в инфраструктуре MVC, например:
public class ViewPageComponentActivator : DefaultComponentActivator
{
public ViewPageComponentActivator(ComponentModel model, IKernel kernel, ComponentInstanceDelegate onCreation, ComponentInstanceDelegate onDestruction)
: base(model, kernel, onCreation, onDestruction)
{
}
protected override object CreateInstance(CreationContext context, ConstructorCandidate constructor, object[] arguments)
{
// Do like the MVC framework.
var instance = Activator.CreateInstance(context.RequestedType);
return instance;
}
}
Активатор компонента просто всегда возвращает новый экземпляр представления. Поскольку компонент зарегистрирован как временный, всегда вызывается CreateInstance
. Здесь могут быть некоторые возможности настройки.
Активатор просмотра страниц теперь намного проще. Обратите внимание, что тип службы меняется каждый раз, когда вы меняете представление, поэтому мы должны зарегистрировать тип, основываясь на его уникальном имени (я еще не настроил это, но может быть более хороший способ назвать компонент).
/// <summary>
/// An activator using Castle Kernel for activating views.
/// </summary>
public class WindsorViewPageActivator : IViewPageActivator
{
private readonly IKernel _kernel;
/// <summary>
/// Initializes a new instance of the <see cref="WindsorViewPageActivator"/> class.
/// </summary>
/// <param name="kernel">The kernel.</param>
public WindsorViewPageActivator([NotNull] IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public object Create(ControllerContext controllerContext, Type type)
{
if (!_kernel.HasComponent(type.FullName))
{
_kernel.Register(Component.For(type).Named(type.FullName).Activator<ViewPageComponentActivator>().LifestyleTransient());
}
return _kernel.Resolve(type.FullName, type);
}
}
Надеюсь, это может пригодиться кому-то в подобных ситуациях.