开发者

Creating WindsorViewPageActivator

开发者 https://www.devze.com 2023-01-30 14:53 出处:网络
I was playing with new Asp net mvc 3 RC2. I have created a WindsorViewPageActivator class as follows public class WindsorViewPageActivator : IViewPageActivator

I was playing with new Asp net mvc 3 RC2. I have created a WindsorViewPageActivator class as follows

public class WindsorViewPageActivator : IViewPageActivator
    {
        object IViewPageActivator.Create(ControllerContext controllerContext, Type type)
        {
            return DependencyResolver.Current.GetService(type);              
        }
    }

and then a WindsorDependencyResolver class

public class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
            this.container = container;
    }

    #region IDependencyResolver Members

    public object GetService(Type serviceType)
    {
        return Resolve(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return container.ResolveAll(serviceType).Cast<object>();
    }

    public IEnumerable<TService> GetAllInstances<TService>()
    {
        return container.ResolveAll<TService>();
    }

    public TService GetInstance<TService>()
    {
        return (TService)Resolve(typeof(TService));
    }

    #endregion
    private object Resolve(Type serviceType)
    {
        try
        {
            return container.Resolve( serviceType);
        }
        catch (Exception ex)
        {
            return null;
        }
    }
}

}

Now I am doing in Global.asax something like this

container.Register(Component.For<IControllerActivator>().ImplementedBy<WindsorControllerActivator>());
container.Register(Component.For<IViewPageActivator>().ImplementedBy<WindsorViewPageActivator>());
container.Register(Component.For<IControllerFactory>().ImplementedBy<DefaultControllerFactory>());               
DependencyResolver.SetResolver (new WindsorDependencyResolver(container));

' Now I am getting the followin开发者_如何转开发g error The view found at '~/Views/Account/LogOn.cshtml' was not created. Do I need to register each view page in windsor container If yes then how can I register each view. I am using Razor view engine. Thanks


Yes, in order to resolve stuff you need to register it. Have a look at the documentation to familiarise yourself with the API.


I have tried this myself, and unfortunately I can't seem to get it to work properly. I do the following in my solution:

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>()
            ;
    }
}

This works as long as you don't change anything in your markup. If you do, you'll get a registration error, as the view now will generate a new type, that doesn't exist in the container (but it has the same name!). What I thought of doing, was to aggressively release the view component as soon as it's resolved, but I can't get rid of it in the container for some reason. Not even an explicit call to _kernel.ReleaseComponent(viewPageInstance) would do it (but that's of course just releasing the instance, not the type).

What we really need to do, is make Windsor inject properties into existing instances. That is, use Activator.CreateInstance(type) and then tell Windsor to inject the properties into the instance. But Windsor doesn't support injecting properties into existing instances, so we need to hack something together, that will do that for us. I've seen this one http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/ (at the bottom), but that wouldn't perform very well.

My solution was simply to manually set my properties in the viewpage activator (you have a base viewpage type), but maybe there's some better solution?

EDIT

I managed to get it working after all!

My solution is to simply create a custom component activator and mimic what's being done in the MVC framework, like so:

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;
    }
}

The component activator simply always return a new instance of the view. Because the component is registered as being transient, CreateInstanceis always called. There might be some tweaking possibilities here.

The viewpage activator is now much simpler. Note that the type of service is different whenever you change the view, so we must register the type based on it's unique name (I haven't tweaked this yet, but there might be a nicer way to name the component).

/// <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);
    }
}

I hope this might be of use to someone in similar situations.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号