| William's profileBill PierceBlogLists | Help |
|
8/18/2006 Moving DayI really do apologize. I know it is a pain to update feed subscriptions but I've given up on MSN Spaces. Further blog entries in the Chronicles of Bill Pierce can be found at: 8/15/2006 UserControl Component Activation in Castle MicroKernelYesterday I announced to my large user base that I am working on an MVP Framework for ASP.Net. I gave a little preview regarding the View and Presenter interfaces as well as an introduction to some of the internal plumbing of the Castle MicroKernel dependency injection framework. Today I want to continue that trend and talk about how the Kernel will activate (instantiate) the web UserControl components. When a component is initally added to the Kernel, meta data is collected on the constuctors, properties, attributes, and methods that make up the component. An additional piece of information gathered is the class responsible for intantiating that component when an instance is requested from the Kernel. The MicroKernel comes with a DefaultComponentActivator that eventually calls Activator.CreateInstance, supplying a "best guess" of constructor arguments based on other components loaded in the Kernel. Activator.CreateInstance won't do us much good in the context of creating an instance of a UserControl. ASP.Net developers who dynamically load UserControls at runtime are familiar with Page.LoadControl. In ASP.Net 1.1, LoadControl took a single argument, the path to the .ascx file for the UserControl. LoadControl would then perform some magic under the covers, checking for a cached version, parsing and compiling the .ascx markup, then returning a Control object that was ready for you to include in your page. ASP.Net 2.0 adds an overload to LoadControl that allows you to pass the Type of control you want to load along with any constructor arguments. The only catch with the overloaded method is that the UserControl must be pre-compiled. If the control is not pre-compiled, you will get an instance of the UserControl, but anything declared in markup in the .ascx portion of the control will be null and usesless e.g. TextBoxes, Labels, etc. I addressed the precompilation issues in my previous post "Dependency Injection and UserControls with Castle MicroKernel". To address the LoadControl issue I created the WebUserControlComponentActivator. My custom activator is based on the DefaultComponentActivator so using it will still resolve Property based dependencies as well as constructor dependencies. The main thrust of the activator is to get a reference to the currently executing Page and then call LoadControl passing the type of the component to be created. The complete listing for WebUserControlComponentActivator is below: public class WebUserControlComonentActivator : DefaultComponentActivator The question you all are asking is how do I tell the Kernel that I want to use the WebUserControlComponentActivator to activate all the UserControl components I put in the Kernel? That's an excellent question and you did the right thing by calling. I needed to create two more pieces to finish this little puzzle. First, the ComponentActivatorInspector, allows you to specify an additional attribute ("componentActivatorType") on components when creating the .config file for your Kernel. <component id="Dashboard" If config files are not your style, you can also use a custom class attribute to specify which component activiator to use when you code up your component. using System; The code for the ComponentActivator attribute is trivial, if you really want to see it let me know and I will email it to you. The code for the ComponentActivatorInspector is based direcly on the LifestyleModelInspector performing a very similar function. First, the code looks to the configuration information, if any. If no configuration information is found we then look for the ComponentActivator attribute. public class ComponentActivatorInspector : IContributeComponentModelConstruction public void ProcessModel(IKernel kernel, ComponentModel model) protected virtual bool ReadComponentActivatorFromConfiguration(ComponentModel model) if (componentActivatorType != null) protected virtual void ReadComponentActivatorFromType(ComponentModel model) if (attributes.Length != 0) private void ValidateComponentActivator(Type customComponentActivator) One more problem solved on the way to our MVP Framework. 8/14/2006 Dependency Injection and UserControls with Castle MicroKernelThe Castle MicroKernel is a lovely software construct that allows you to create some extremely powerful and configurable systems while abastracting most of the complexity. I could spend several blog entries describing what the Kernel is and why it is cool, but smarter folks than I have already done that. What I wanted to talk about in this post is channeling the Kernel's power into an ASP.Net web application. One of the key aspects to the Kernel is Dependency Injection. The Kernel will examine the constructors for the objects it manages and make sure all of the required external objects are provided when you create an instance of that object. For Example: public DashboardPresenter(IDashboardView view) { ... } When you request a DashboardPresenter object from the Kernel, it will try and instanitate an IDashboardView object and pass that to the DashboardPresenter constructor for you. This saves you from having to know about and create all of the objects your Presenter relies on. That was the 30 second DI tutorial :) The problem I am trying to address is achieving this injection functionality in the case of Web UserControls. The problem with UserControls is they are usually compiled the very first time someone accesses the Web Applicaiton that uses the control. The ASP.Net runtime parses and compiles the control on the fly to a temporary assembly. Anytime you change something on the control, it is re-compiled. ASP.Net 2.0 now offers the ability to pre-compile Pages and UserControls before you deploy them to your production server. This removes the need for the runtime to compile it on the fly. The problem with pre-compiling UserControls is three fold:
K. Scott Allen has an excellent article to address problem number 1 entitled Using MSBuild and ILMerge to Package User Controls For Reuse. Scott explains how to pre-compile your controls and package them all into a single assembly. With our UserControls nicely packaged, we are almost ready to use them with our DI Kernel. When we add components (Classes) to the Kernel we specify them with a fully qualified name ala "BatchTracker.Views.DashboardView, BatchTracker.Views". As I mentioned in point 2 above, the class we really want to add to the Kernel is the DashboardView_ascx class. Because I'm lazy don't want to have to add my compoents with a _ascx tacked on the end, I wrote a custom inspector for the MicroKernel that will determine if a component is a UserControl and if so, it will add the pre-compiled subclass inplace of the specified class. Below I've listed the code for the WebUserControlModelInspector. using System; using Castle.Model; namespace WCPierce.MicroKernel.ModelBuilder.Inspectors The code is fairly self explanatory, if the compenent derives from System.Web.UI.UserControl we search its assembly for a subclass (the precompiled version). The check for the __initialized field is just incase someone actually adds the precompiled version directly to the Kernel. I couldn't figure out a better way to determine if the component was already precompiled. Any input is appreciated. To use the Inspector simply call Kernel.ComponentModelBuilder.AddContributor(new WebUserControlModelInspector()) before you add any components to the Kernel. My next post will address the issue of Activating (instantiating) a UserControl derived component in the context of the Kernel. MVP FrameworkLately between baby feedings, baby burpings, baby sittings, baby sleepings, baby poopings, baby peeings, baby playing, and baby photo shoots I've been working an an MVP Framework for ASP.Net 2.0. My Framework is more along the lines of Martin Fowler's Supervising Presenter. I am putting together something I think will be very useful for quickly creating highly dynamic and configurable web applications. The framework relies on the Windsor Container from the Castle Project. The Web framework does all the MVP plumbing for you. All you need to do is create a small Web Project with a single Page to act as the generic host for UserControls/Server Controls. Then you create your Models, Presenters, and Views and the framework will hook everything together using Dependency Injection. So far it has been a lot of fun and I have been digging deeply into the Castle MicroKernel and the Windsor container. Specifically creating some custom contributors to allow UserControls to be added, configured, and resolved by the Container. So far the Framework is 36 KB and it's toight like toiger. I borrowed heavily from MonoRail, also part of the Castle project. I love the concept of MonoRail but I think it is too big of a jump for most ASP.Net developers. I'm hoping my MVP Framework can be a happy medium between the two-tier DataSet ASP.Net approach and the full on MVC approach in MonoRail. The "Views" are simply pre-compiled UserControls or custom Server controls. The Presenters encapsulate all the business logic and Service interaction. You can see the simple Interfaces for these two portions of the Framework below. I want to do some finishing touches and put together an example before I release anything, but I will keep you all posted. I'll be posting regularly in the next few days explaining why MVP and why my Framework is beneficial. using System;
namespace WCPierce.MVP.Framework { public interface IPresenter { IView View { get; }
void Initialize(bool isFirstCall); } } using System;namespace WCPierce.MVP.Framework{public interface IView {void SetPresenter(IPresenter presenter); void Initialize(bool isFirstCall); } } |
|
|