Created
April 11, 2011 22:33
-
-
Save sheastrickland/914515 to your computer and use it in GitHub Desktop.
Windsor MvcFacility - Early hacks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Web.Mvc; | |
using System.Web.Routing; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
public class ControllerContextHost | |
{ | |
private ControllerBase controller; | |
public void SetController(ControllerBase controller) | |
{ | |
if (this.controller != null) | |
{ | |
return; | |
//An exception cannot be thrown here because child request actions will fail. | |
//throw new InvalidOperationException("Controller was already set!"); | |
} | |
this.controller = controller; | |
} | |
public ControllerContext GetContext() | |
{ | |
if (controller == null) | |
{ | |
return null; | |
} | |
return controller.ControllerContext; | |
} | |
} | |
public class RequestContextHost | |
{ | |
private RequestContext context; | |
public void SetContext(RequestContext context) | |
{ | |
if (this.context != null) | |
{ | |
//An exception cannot be thrown here because child request actions will fail. | |
return; | |
} | |
this.context = context; | |
} | |
public RequestContext GetContext() | |
{ | |
if (context == null) | |
{ | |
throw new InvalidOperationException("Context was not set!"); | |
} | |
return context; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web.Mvc; | |
using VMS.Domain.Extensions; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] | |
public sealed class ModelBinderForTypesAttribute : Attribute | |
{ | |
public ModelBinderForTypesAttribute(params Type[] targetTypes) | |
{ | |
if (targetTypes == null) | |
throw new ArgumentNullException("targetTypes"); | |
TargetTypes = targetTypes; | |
} | |
public ModelBinderForTypesAttribute(Type targetType) | |
{ | |
if (targetType == null) | |
throw new ArgumentNullException("targetType"); | |
TargetTypes = new[] {targetType}; | |
} | |
public IEnumerable<Type> TargetTypes { get; set; } | |
public static IEnumerable<Type> ExtractTypesFor(IModelBinder modelBinder) | |
{ | |
return modelBinder.GetType().IsDecoratedWith<ModelBinderForTypesAttribute>() | |
? modelBinder.GetType().GetAttribute<ModelBinderForTypesAttribute>().TargetTypes | |
: Enumerable.Empty<Type>(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Reflection; | |
using System.Web; | |
using System.Web.Mvc; | |
using System.Web.Routing; | |
using Castle.MicroKernel.Facilities; | |
using Castle.MicroKernel.Registration; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
public class MvcFacility : AbstractFacility | |
{ | |
private Assembly _bindersAssembly; | |
private Assembly _controllersAssembly; | |
protected override void Init() | |
{ | |
TransientScopedInWebRequestLifestyleModule.Init(Kernel); | |
DependencyResolver.SetResolver(new WindsorDependencyResolver(Kernel)); | |
RegisterAspNetContextImpl(); | |
RegisterControllersImpl(); | |
RegisterModelBindersImpl(); | |
} | |
public MvcFacility RegisterControllers() | |
{ | |
_controllersAssembly = Assembly.GetCallingAssembly(); | |
return this; | |
} | |
public MvcFacility RegisterModelBinders() | |
{ | |
_bindersAssembly = Assembly.GetCallingAssembly(); | |
return this; | |
} | |
private void RegisterModelBindersImpl() | |
{ | |
if (_bindersAssembly == null) return; | |
Kernel.Register(AllTypes.FromAssembly(_bindersAssembly) | |
.BasedOn<IModelBinder>() | |
.WithService.AllInterfaces() | |
.Configure(c => c.LifeStyle.PerWebRequest)); | |
//MVC will hold the references to this component in a static dictionary, so lifetime is a moot point | |
Kernel.Register(Component.For<IModelBinderProvider>().ImplementedBy<WindsorModelBinderProvider>()); | |
} | |
private void RegisterControllersImpl() | |
{ | |
if (_controllersAssembly == null) return; | |
Kernel.Register(Component.For<ControllerContextHost>().LifeStyle.PerWebRequest, | |
Component.For<Func<ControllerContext>>() | |
.UsingFactoryMethod<Func<ControllerContext>>(k => k.Resolve<ControllerContextHost>().GetContext) | |
.LifeStyle.Transient, | |
AllTypes.FromAssembly(_controllersAssembly) | |
.BasedOn<IController>() | |
.If(t => t.Namespace.EndsWith(".Controllers")) | |
.If(t => t.Name.EndsWith("Controller")) | |
.Configure((c => c.LifeStyle.Transient | |
.OnCreate((k, v) => k.Resolve<ControllerContextHost>() | |
.SetController(v as ControllerBase))))); | |
Kernel.Register(Component.For<IControllerFactory>().ImplementedBy<WindsorControllerFactory>().LifeStyle.PerWebRequest); | |
} | |
private void RegisterAspNetContextImpl() | |
{ | |
Kernel.Register(Component.For<RequestContextHost>().LifeStyle.PerWebRequest, | |
Component.For<RequestContext>() | |
.UsingFactoryMethod(k => k.Resolve<RequestContextHost>().GetContext()) | |
.LifeStyle.Transient, | |
Component.For<HttpContextBase>() | |
.UsingFactoryMethod(k => k.Resolve<RequestContext>().HttpContext) | |
.LifeStyle.Transient, | |
Component.For<HttpRequestBase>() | |
.UsingFactoryMethod(k => k.Resolve<HttpContextBase>().Request) | |
.LifeStyle.Transient, | |
Component.For<HttpServerUtilityBase>() | |
.UsingFactoryMethod(k => k.Resolve<HttpContextBase>().Server) | |
.LifeStyle.Transient, | |
Component.For<UrlHelper>() | |
.UsingFactoryMethod(k => new UrlHelper(k.Resolve<RequestContext>(), RouteTable.Routes)) | |
.LifeStyle.Transient); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Castle.MicroKernel.Context; | |
using Castle.MicroKernel.Lifestyle; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
/// <summary> | |
/// This lifetime should be used for any transient components manually resolved within MVC | |
/// </summary> | |
/// <remarks>These are tracked and released via <see cref="TransientScopedInWebRequestLifestyleModule"/></remarks> | |
public class TransientScopedInWebRequestLifestyleManager : AbstractLifestyleManager | |
{ | |
public override object Resolve(CreationContext context) | |
{ | |
var instance = base.Resolve(context); | |
TransientScopedInWebRequestLifestyleModule.RegisterForEviction(instance); | |
return instance; | |
} | |
public override bool Release(object instance) | |
{ | |
TransientScopedInWebRequestLifestyleModule.Evict(instance); | |
return base.Release(instance); | |
} | |
public override void Dispose() | |
{ | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web; | |
using Castle.MicroKernel; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
/// <summary> | |
/// This module tracks any manually resolved transient components, where MVC doe not provide any explicit release hooks | |
/// </summary> | |
public class TransientScopedInWebRequestLifestyleModule : IHttpModule | |
{ | |
private const string Key = "trolls_and_goblins"; | |
public static IKernel Kernel; | |
public void Dispose() | |
{ | |
} | |
public void Init(HttpApplication context) | |
{ | |
context.EndRequest += ApplicationEndRequest; | |
} | |
public static void Init(IKernel kernel) | |
{ | |
Kernel = kernel; | |
} | |
protected void ApplicationEndRequest(object sender, EventArgs e) | |
{ | |
var application = (HttpApplication)sender; | |
var candidates = (IEnumerable<object>)application.Context.Items[Key]; | |
if (candidates == null) return; | |
foreach (var instance in candidates.Reverse()) | |
Kernel.ReleaseComponent(instance); | |
application.Context.Items.Remove(Key); | |
} | |
public static void RegisterForEviction(object instance) | |
{ | |
var context = HttpContext.Current; | |
var list = (List<object>)context.Items[Key]; | |
if (list == null) | |
{ | |
list = new List<object>(); | |
context.Items[Key] = list; | |
} | |
list.Add(instance); | |
} | |
public static void Evict(object instance) | |
{ | |
var context = HttpContext.Current; | |
var list = (List<object>)context.Items[Key]; | |
if (list != null) | |
{ | |
list.Remove(instance); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Web.Mvc; | |
using Castle.MicroKernel; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
public class WindsorActionInvoker : ControllerActionInvoker | |
{ | |
private readonly IKernel _kernel; | |
public WindsorActionInvoker(IKernel kernel) | |
{ | |
_kernel = kernel; | |
} | |
private void ExecuteFiltersFromWindsor(AuthorizationContext filterContext) | |
{ | |
if (_kernel.HasComponent(typeof(IAuthorizationFilter)) == false) | |
return; | |
var resolved = _kernel.ResolveRegisteredHandlers<IAuthorizationFilter>(); | |
foreach (var filter in resolved) | |
{ | |
try | |
{ | |
filter.OnAuthorization(filterContext); | |
} | |
finally | |
{ | |
_kernel.ReleaseComponent(filter); | |
} | |
} | |
} | |
private void ExecuteFiltersFromWindsor(ExceptionContext filterContext) | |
{ | |
var filter = _kernel.Resolve<IExceptionFilter>(); | |
try | |
{ | |
filter.OnException(filterContext); | |
} | |
finally | |
{ | |
_kernel.ReleaseComponent(filter); | |
} | |
} | |
protected override object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) | |
{ | |
var result = _kernel.HasComponent(parameterDescriptor.ParameterType) | |
? _kernel.Resolve(parameterDescriptor.ParameterType) | |
: base.GetParameterValue(controllerContext, parameterDescriptor); | |
return result; | |
} | |
protected override ExceptionContext InvokeExceptionFilters(ControllerContext controllerContext, | |
IList<IExceptionFilter> filters, | |
Exception exception) | |
{ | |
var filterContext = new ExceptionContext(controllerContext, exception); | |
ExecuteFiltersFromWindsor(filterContext); | |
foreach (var filter in filters) | |
{ | |
filter.OnException(filterContext); | |
if (filterContext.Result != null) | |
{ | |
return filterContext; | |
} | |
} | |
return filterContext; | |
} | |
protected override AuthorizationContext InvokeAuthorizationFilters( | |
ControllerContext controllerContext, | |
IList<IAuthorizationFilter> filters, | |
ActionDescriptor actionDescriptor) | |
{ | |
var filterContext = new AuthorizationContext(controllerContext, actionDescriptor); | |
ExecuteFiltersFromWindsor(filterContext); | |
foreach (var filter in filters) | |
{ | |
filter.OnAuthorization(filterContext); | |
if (filterContext.Result != null) | |
{ | |
return filterContext; | |
} | |
} | |
return filterContext; | |
} | |
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) | |
{ | |
var filters = base.GetFilters(controllerContext, actionDescriptor); | |
if (_kernel.HasComponent(typeof(IActionFilter))) | |
{ | |
var resolved = _kernel.ResolveRegisteredHandlers<IActionFilter>(); | |
foreach (var filter in resolved) | |
{ | |
filters.ActionFilters.Add(filter); | |
} | |
} | |
if (_kernel.HasComponent(typeof(IResultFilter))) | |
{ | |
var resolved = _kernel.ResolveRegisteredHandlers<IResultFilter>(); | |
foreach (var filter in resolved) | |
{ | |
filters.ResultFilters.Add(filter); | |
} | |
} | |
return filters; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Web; | |
using System.Web.Mvc; | |
using System.Web.Routing; | |
using Castle.MicroKernel; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
public class WindsorControllerFactory : DefaultControllerFactory | |
{ | |
private readonly IKernel _kernel; | |
public WindsorControllerFactory(IKernel kernel) | |
{ | |
_kernel = kernel; | |
} | |
public override void ReleaseController(IController controller) | |
{ | |
_kernel.ReleaseComponent(controller); | |
} | |
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) | |
{ | |
if (controllerType == null) | |
throw new HttpException(404, | |
string.Format( | |
"The controller for path '{0}' could not be found or it does not implement IController.", | |
requestContext.HttpContext.Request.Path)); | |
var host = _kernel.Resolve<RequestContextHost>(); | |
host.SetContext(requestContext); | |
var controller = (IController)_kernel.Resolve(controllerType); | |
if (controller is Controller && _kernel.HasComponent(typeof(ITempDataProvider))) | |
((Controller) controller).TempDataProvider = _kernel.Resolve<ITempDataProvider>(); | |
return controller; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Castle.MicroKernel; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
public class WindsorDependencyResolver :System.Web.Mvc.IDependencyResolver | |
{ | |
private readonly IKernel _kernel; | |
public WindsorDependencyResolver(IKernel kernel) | |
{ | |
_kernel = kernel; | |
} | |
public object GetService(Type serviceType) | |
{ | |
#if DEBUG | |
System.Diagnostics.Debugger.Log(0, "GetService", string.Format("{0:T}-{1}\n", DateTime.Now, serviceType)); | |
#endif | |
return _kernel.HasComponent(serviceType) ? _kernel.Resolve(serviceType) : null; | |
} | |
public IEnumerable<object> GetServices(Type serviceType) | |
{ | |
#if DEBUG | |
System.Diagnostics.Debugger.Log(0, "GetServices", string.Format("{0:T}-{1}\n", DateTime.Now, serviceType)); | |
#endif | |
return _kernel.ResolveRegisteredHandlers(serviceType).OfType<object>(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using Castle.MicroKernel; | |
using Castle.MicroKernel.Context; | |
using Castle.MicroKernel.Handlers; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
public static class WindsorExtensions | |
{ | |
/// <summary> | |
/// Returns all the valid component instances by the service type and have been explicitly registered | |
/// </summary> | |
/// <remarks> Based on the original ResolveAll()</remarks> | |
/// <see cref="https://github.com/castleproject/Castle.Windsor/blob/2.5.3/src/Castle.Windsor/MicroKernel/DefaultKernel_Resolve.cs#L261"/> | |
public static Array ResolveRegisteredHandlers(this IKernel container, Type service) | |
{ | |
var resolved = new Dictionary<IHandler, object>(); | |
foreach (var handler in container.GetHandlers(service)) | |
{ | |
var actualHandler = handler; | |
if (handler is ForwardingHandler) | |
{ | |
actualHandler = ((ForwardingHandler)handler).Target; | |
} | |
if (resolved.ContainsKey(actualHandler)) | |
{ | |
continue; | |
} | |
var component = handler.TryResolve(CreationContext.Empty); | |
if (component != null) | |
{ | |
resolved.Add(actualHandler, component); | |
} | |
} | |
var components = Array.CreateInstance(service, resolved.Count); | |
((ICollection)resolved.Values).CopyTo(components, 0); | |
return components; | |
} | |
/// <summary> | |
/// Returns component instances that implement TService and have been explicitly registered | |
/// </summary> | |
/// <typeparam name="TService"></typeparam> | |
/// <returns></returns> | |
public static TService[] ResolveRegisteredHandlers<TService>(this IKernel container) | |
{ | |
return (TService[])ResolveRegisteredHandlers(container, typeof(TService)); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web.Mvc; | |
namespace VMS.Domain.WindsorMvcFacility | |
{ | |
/// <summary> | |
/// This is in essence a "static" class as when Mvc resolves the instance, it will live in a static collection | |
/// </summary> | |
public class WindsorModelBinderProvider : IModelBinderProvider, IDisposable | |
{ | |
private readonly Func<IEnumerable<IModelBinder>> _modelBinderFactory; | |
/// <summary> | |
/// Ensure all components injected are factories, as these will in essence be static | |
/// </summary> | |
/// <param name="modelBinderFactory"></param> | |
public WindsorModelBinderProvider(Func<IEnumerable<IModelBinder>> modelBinderFactory) | |
{ | |
_modelBinderFactory = modelBinderFactory; | |
} | |
public void Dispose() | |
{ | |
} | |
public IModelBinder GetBinder(Type modelType) | |
{ | |
return _modelBinderFactory() | |
.FirstOrDefault(binder => ModelBinderForTypesAttribute.ExtractTypesFor(binder) | |
.Any(modelType.IsAssignableFrom)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment