diff --git a/src/Havit.CastleWindsor.WebForms.Example/Default.aspx b/src/Havit.CastleWindsor.WebForms.Example/Default.aspx index 042d3ec..52cb500 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/Default.aspx +++ b/src/Havit.CastleWindsor.WebForms.Example/Default.aspx @@ -1,11 +1,12 @@ <%@ Page Language="C#" AutoEventWireup="false" CodeBehind="Default.aspx.cs" Inherits="Havit.CastleWindsor.WebForms.Example.DefaultPage" %> +<%@ Register Src="~/DemoControl.ascx" TagPrefix="havit" TagName="DemoControl" %> Hello Havit.CastleWindsor.WebForms! - Demo 8: - +
Demo1:
+
Demo2:
diff --git a/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.cs b/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.cs index 6384acf..78b47e6 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.cs +++ b/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.cs @@ -1,22 +1,10 @@ using System; +using System.Linq; using System.Web.UI; namespace Havit.CastleWindsor.WebForms.Example { public partial class DefaultPage : Page { - private readonly IMyDependecy myDependecy; - - public DefaultPage(IMyDependecy myDependecy) - { - this.myDependecy = myDependecy; - } - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - litHello.Text = myDependecy.MyMethod(); - } - } + } } \ No newline at end of file diff --git a/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.designer.cs b/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.designer.cs index 3c6d7ae..737145f 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.designer.cs +++ b/src/Havit.CastleWindsor.WebForms.Example/Default.aspx.designer.cs @@ -11,14 +11,5 @@ namespace Havit.CastleWindsor.WebForms.Example { public partial class DefaultPage { - - /// - /// litHello control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal litHello; } } diff --git a/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx b/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx new file mode 100644 index 0000000..454ddcc --- /dev/null +++ b/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx @@ -0,0 +1,2 @@ +<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="DemoControl.ascx.cs" Inherits="Havit.CastleWindsor.WebForms.Example.DemoControl" %> + \ No newline at end of file diff --git a/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx.cs b/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx.cs new file mode 100644 index 0000000..0d1ac92 --- /dev/null +++ b/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +namespace Havit.CastleWindsor.WebForms.Example +{ + public partial class DemoControl : System.Web.UI.UserControl + { + private readonly IMyDependecy myDependecy; + + public DemoControl(IMyDependecy myDependecy) + { + this.myDependecy = myDependecy; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + DemoLiteral.Text = myDependecy.SayHello(); + } + } +} \ No newline at end of file diff --git a/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx.designer.cs b/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx.designer.cs new file mode 100644 index 0000000..9337248 --- /dev/null +++ b/src/Havit.CastleWindsor.WebForms.Example/DemoControl.ascx.designer.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Havit.CastleWindsor.WebForms.Example { + + + public partial class DemoControl { + + /// + /// DemoLiteral control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal DemoLiteral; + } +} diff --git a/src/Havit.CastleWindsor.WebForms.Example/Global.asax.cs b/src/Havit.CastleWindsor.WebForms.Example/Global.asax.cs index 723cddd..4ffdcb3 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/Global.asax.cs +++ b/src/Havit.CastleWindsor.WebForms.Example/Global.asax.cs @@ -9,7 +9,7 @@ public class Global : HttpApplication public void Application_Start(object sender, EventArgs e) { var container = this.AddWindsorContainer(); - container.Register(Component.For().ImplementedBy()); + container.Register(Component.For().ImplementedBy().LifestyleSingleton()); } } } \ No newline at end of file diff --git a/src/Havit.CastleWindsor.WebForms.Example/Havit.CastleWindsor.WebForms.Example.csproj b/src/Havit.CastleWindsor.WebForms.Example/Havit.CastleWindsor.WebForms.Example.csproj index d23fe58..c3a30c5 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/Havit.CastleWindsor.WebForms.Example.csproj +++ b/src/Havit.CastleWindsor.WebForms.Example/Havit.CastleWindsor.WebForms.Example.csproj @@ -119,6 +119,7 @@ + @@ -131,6 +132,13 @@ Default.aspx + + DemoControl.ascx + ASPXCodeBehind + + + DemoControl.ascx + Global.asax diff --git a/src/Havit.CastleWindsor.WebForms.Example/IMyDependecy.cs b/src/Havit.CastleWindsor.WebForms.Example/IMyDependecy.cs index 7375af3..85a432e 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/IMyDependecy.cs +++ b/src/Havit.CastleWindsor.WebForms.Example/IMyDependecy.cs @@ -2,6 +2,6 @@ { public interface IMyDependecy { - string MyMethod(); + string SayHello(); } } diff --git a/src/Havit.CastleWindsor.WebForms.Example/MyDependency.cs b/src/Havit.CastleWindsor.WebForms.Example/MyDependency.cs index 7385e8d..f378b75 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/MyDependency.cs +++ b/src/Havit.CastleWindsor.WebForms.Example/MyDependency.cs @@ -2,7 +2,7 @@ { public class MyDependency : IMyDependecy { - public string MyMethod() + public string SayHello() { return "Hello from a dependency!"; } diff --git a/src/Havit.CastleWindsor.WebForms.Example/WebService.asmx.cs b/src/Havit.CastleWindsor.WebForms.Example/WebService.asmx.cs index 140dda1..ba515f1 100644 --- a/src/Havit.CastleWindsor.WebForms.Example/WebService.asmx.cs +++ b/src/Havit.CastleWindsor.WebForms.Example/WebService.asmx.cs @@ -15,7 +15,7 @@ public class WebService : InjectableWebServiceBase [WebMethod] public string SayHello() { - return MyDependecy.MyMethod(); + return MyDependecy.SayHello(); } } } diff --git a/src/Havit.CastleWindsor.WebForms/ContainerServiceProvider.cs b/src/Havit.CastleWindsor.WebForms/ContainerServiceProvider.cs index 64d5b68..488b900 100644 --- a/src/Havit.CastleWindsor.WebForms/ContainerServiceProvider.cs +++ b/src/Havit.CastleWindsor.WebForms/ContainerServiceProvider.cs @@ -20,8 +20,9 @@ internal class ContainerServiceProvider : IServiceProvider, IRegisteredObject internal IServiceProvider NextServiceProvider { get; } - private const int TypesCannotResolveCacheCap = 100000; - private readonly ConcurrentDictionary _typesCannotResolve = new ConcurrentDictionary(); // there is no ConcurrentHashSet in .NET FW. + private readonly ConcurrentDictionary typesToCreateByActivator = new ConcurrentDictionary(); // there is no ConcurrentHashSet in .NET FW. + private readonly ConcurrentDictionary typesToCreateByWindsorContainer = new ConcurrentDictionary(); // there is no ConcurrentHashSet in .NET FW. + private readonly ConcurrentDictionary typesToCreateByNextServiceProvider = new ConcurrentDictionary(); // there is no ConcurrentHashSet in .NET FW. public ContainerServiceProvider(IServiceProvider next) { @@ -41,53 +42,60 @@ public ContainerServiceProvider(IServiceProvider next) /// public object GetService(Type serviceType) { - // Try unresolvable types - we cache them - if (_typesCannotResolve.ContainsKey(serviceType)) + // Performance: + // duration(WindsorContainer.Resolve+Release) = 4x duration(Activator.CreateInstance) + if (typesToCreateByActivator.ContainsKey(serviceType)) // >90% { - return DefaultCreateInstance(serviceType); + return CreateInstanceByActivator(serviceType); } - // Try the container - object result = null; + if (typesToCreateByWindsorContainer.ContainsKey(serviceType)) // < 10% + { + return CreateInstanceByWindsorContainer(serviceType); + } + + if (typesToCreateByNextServiceProvider.ContainsKey(serviceType)) // 0% + { + return CreateInstanceByNextServiceProvider(serviceType); + } - // protects repeated registration to CastleWindsor in case of parallel requests + // we continue only for types which was never before resolved + + // We must register dynamically compiled resources (pages, controls, master pages, handlers ...) + // lock protects repeated registration to WindsorContainer in case of parallel requests lock (serviceType) { - // We must register dynamically compiled resources (pages, controls, master pages, handlers ...) - if ((typeof(UserControl).IsAssignableFrom(serviceType) || // User controls (.ascx) and event Master Pages (.master) inherit from UserControl - typeof(IHttpHandler).IsAssignableFrom(serviceType)) && // Geneirc handlers (.ashx) and also pages (.aspx) inherit from IHttpHandler - !Container.Kernel.HasComponent(serviceType)) + if (ShouldBeRegisteredToWindsorContainer(serviceType) + && !Container.Kernel.HasComponent(serviceType)) // protects repeated registration to WindsorContainer in case of parallel requests { // Lifestyle is *Transient* // If it would be PerWebRequest, we couldn't use the same control on one page twice - resolved would be only the first, and the second would be reused) + // NamedAutomaticaly with serviceType.AssemblyQualifiedName - enables multiple compilations of a page during development, every compilation is in a new assembly Container.Register(Component.For(serviceType).ImplementedBy(serviceType).LifestyleTransient().NamedAutomatically(serviceType.AssemblyQualifiedName)); } } + object result = null; + // If we have component registered, we will resolve the service if (Container.Kernel.HasComponent(serviceType)) { - result = Container.Resolve(serviceType); - // And because transient, we must release component on end request - else we would make memory leaks - HttpContext.Current.AddOnRequestCompleted(_ => Container.Release(result)); + result = CreateInstanceByWindsorContainer(serviceType); + typesToCreateByWindsorContainer.TryAdd(serviceType, true); - return result; } // Try the next provider if we don't have result - if (result == null) - { - result = NextServiceProvider?.GetService(serviceType); + if ((result == null) && (NextServiceProvider != null) && (result = CreateInstanceByNextServiceProvider(serviceType)) != null) + { + typesToCreateByNextServiceProvider.TryAdd(serviceType, true); } // Default activation - if (result == null && (result = DefaultCreateInstance(serviceType)) != null) + if ((result == null) && (result = CreateInstanceByActivator(serviceType)) != null) { - // Cache it - if (_typesCannotResolve.Count < TypesCannotResolveCacheCap) - { - _typesCannotResolve.TryAdd(serviceType, true); - } + // Remember it + typesToCreateByActivator.TryAdd(serviceType, true); } return result; @@ -103,14 +111,45 @@ public void Stop(bool immediate) Container.Dispose(); } - private object DefaultCreateInstance(Type type) + + private bool ShouldBeRegisteredToWindsorContainer(Type serviceType) + { + return ((typeof(Control).IsAssignableFrom(serviceType) // User controls (.ascx), Master Pages (.master) and custom controls inherit from Control class + || typeof(IHttpHandler).IsAssignableFrom(serviceType)) // Generic handlers (.ashx) and also pages (.aspx) implements IHttpHandler + && (serviceType.GetConstructor(Type.EmptyTypes) == null)); // Performance for controls (LiteralControl, Labels, ...): When there is parameterless constructor, Castle Windsor is not required + } + + /// + /// Creates serticeType instance by Activator. + /// + private object CreateInstanceByActivator(Type serviceType) { return Activator.CreateInstance( - type, - BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance, - null, - null, - null); + serviceType, + BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance, + null, + null, + null); + } + + /// + /// Creates serticeType instance by Windsor Container. + /// + private object CreateInstanceByWindsorContainer(Type serviceType) + { + object instance = Container.Resolve(serviceType); + // And because transient, we must release component on end request - else we would make memory leaks + HttpContext.Current.AddOnRequestCompleted(_ => Container.Release(instance)); + + return instance; + } + + /// + /// Creates serticeType instance by the next service provider. + /// + private object CreateInstanceByNextServiceProvider(Type serviceType) + { + return NextServiceProvider.GetService(serviceType); } } } diff --git a/src/Havit.CastleWindsor.WebForms/Havit.CastleWindsor.WebForms.csproj b/src/Havit.CastleWindsor.WebForms/Havit.CastleWindsor.WebForms.csproj index 942da0e..572da5d 100644 --- a/src/Havit.CastleWindsor.WebForms/Havit.CastleWindsor.WebForms.csproj +++ b/src/Havit.CastleWindsor.WebForms/Havit.CastleWindsor.WebForms.csproj @@ -8,7 +8,7 @@ true - 1.8.7 + 1.8.8 true HAVIT .NET Framework Extensions - Castle Windsor support for WebForms