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