Wednesday, July 19, 2006

Applying PicoContainer.NET with Presentation Patterns - Part II

(Part I)

Let's think about what Pico is good at in practical sense. I think Pico is great at wiring dependencies together. That means, it is good at wiring dependencies starting from your Presentation Model/Presenter layer and back, because these are all classes that you have complete control over. For your Views, at least if you develop in Visual Studio .NET, the restriction on their default constructors conflicts with the way Pico works. There are ways around it (setter injection as said in Part I), but they are ugly.

But what if I don't register my Views into my Pico container like other classes? Then the question becomes: how can my View get a reference to its dependent Presentation Model/Presenter?

Before I show you my solution, I have to say one more thing. The method InitializeComponent() synonymously refer controls as components for a reason. Every control in .NET is a component. What is a component? It is something you put in a container. Every container has many components. So yes, I am telling you that these controls/widgets are all being put in a container (not Pico) in the .NET control hierarchy framework. So what does each control being a component in this .NET container hierarchy enable me to do? It allows you to have access to the functionalities from other components in the same container. Let me rephrase: each View, being a component, can have access to other classes like a Presentation Model/Presenter, so long as they are all in the same .NET container hierarchy framework. Didn't I just say that this container framework is not Pico? Yes, I did, but it doesn't mean we have to use only one over the other. We can use them both together to each of their strengths to achieve the best of both worlds. Let me explain.

We have already said using Pico container to host everything starting from the Presentation Model layer is a good thing. So here is some example code on how to do this:

        private static IMutablePicoContainer createMutablePicoContainer()

        {

            IMutablePicoContainer pico = new DefaultPicoContainer();

            pico.RegisterComponentImplementation(typeof(IPageOnePM), typeof(PageOnePM));

            pico.RegisterComponentImplementation(typeof(IPageTwoPM), typeof(PageTwoPM));

            pico.RegisterComponentImplementation(typeof(IWebService), typeof(WebService));

            return pico;

        }



In order to allow the Views to have access to other components (Presentation Model/Presenter), we have to create a .NET container. It is just a class that subclass from System.ComponentModel.Container. I am going to call this an ApplicationContainer to avoid being confused with our Pico container.

        internal class ApplicationContainer : System.ComponentModel.Container

        {

            // ...

        }



To put our Views into this ApplicationContainer, you instantiate an instance of it, and put the Form object that you will start your application with into it like this:

        [STAThread]

        static void Main()

        {

            ApplicationContainer applicationContainer = new ApplicationContainer();

 

            MainForm form = new MainForm();

            applicationContainer.Add(form);

 

            Application.Run(form);

        }



From our Views, the method that they will use to get access to other components is called GetService(Type serviceType). When this method is called from within our Views, if our Views are put in a System.ComponentModel.Container, by default the method will ask for its containing container to traverse all registered components and see who can provide this "service" to it. If it finds such component, the container will return it, and now the requesting component gets a reference to that object. How does the container traverse its registered components and decide to give the component requesting a service the service that it asks for? Well, interestingly a System.ComponentModel.Container also has its own GetService() method to do just that. Now, since we have our own subclass ApplicationContainer, what if we override its GetService(), and while our ApplicationContainer object receives a request for service from any of its components, we can instruct it also look to see if Pico has the stuff that the requesting component has. More concretely, when a View uses GetService(typeof(MyPresentationModel) to get its Presentation Model/Presenter dependency, ApplicationContainer will ask for a Pico container that has already been fully registered to return an instance of that class if it is found, like such:

        internal class ApplicationContainer : System.ComponentModel.Container

        {

            private IMutablePicoContainer _pico = createMutablePicoContainer();

 

            protected override object GetService(Type service)

            {

                object instance = _pico.GetComponentInstanceOfType(service);

 

                if (instance != null)

                {

                    return instance;

                }

 

                return base.GetService (service);

            }

        }



To sum it all up, you need to do the following to get things to work:
1. Create an ApplicationContainer class subclassing System.ComponentModel.Container.
2. Add your starting Form into the ApplicationContainer instance, prior to starting it.
3. Set up a Pico container within the ApplicationContainer instance.
4. Register all dependencies that your Presentation Model/Presenter classes will need in your Pico container. You do not need to register your Views.
5. Override the ApplicationContainer's GetService() method, make it to look further into its already setup Pico container for anything that it should return.

Now from your Views, you can gain access to its dependency, in this case a Presentation Model/Presenter, by calling:

        private void PageOneView_Load(object sender, System.EventArgs e)

        {

            // Add this View to ApplicationContainer. Otherwise we have to instantiate

            // each and every view in public static void Main() and do the adding there.

            base.FindForm().Container.Add(this);

 

            // This GetService() call will now find what we need in ApplicationContainer.

            IPageOnePM service = (IPageOnePM)base.GetService(typeof(IPageOnePM));

        }



And modify your PresentationModel's constructor to include an additional parameter to take in the web service class. Then your class can start using the web service functionality, while in unit testing you can mock/stub it out!

    public interface IWebService

    {

    }

 

    public class WebService : IWebService

    {

    }

 

    public class PageOnePM : PresentationModel, IPageOnePM

    {

        private IWebService webservice;

 

        public PageOnePM(IWebService webservice)

        {

            this.webservice = webservice;

        }

    }

 

    // ApplicationContainer class

    private static IMutablePicoContainer createMutablePicoContainer()

    {

        IMutablePicoContainer pico = new DefaultPicoContainer();

        // ...

        pico.RegisterComponentImplementation(typeof(IWebService), typeof(WebService));

        // ...

        return pico;

    }



So now, we have eliminated the child user control not knowing how to get a Presentation Model/Presenter problem, because a user control is also, well, a component in the same ApplicationContainer. We have also solved the ugly setter injecting child user controls' dependencies problem. You also did not modify a single View default constructor! We can now happily use our Visual Studio .NET IDE to do WYSIWYG design and manage good OO design, plus Inversion of Control-ing our dependencies.

After-thought: Though the title of these two posts say Presentation Patterns, in my mind they are more geared towards just for Presentation Model, due to in my opinion the difference in directional references between Presentation Model and Model-View-Presenter. I mentioned briefly about this in my earlier post here.

By the way, note that now each page can have access to multiple Presentation Model objects, instead of the what-it-used-to-be 1-to-1 relationship between a page and its Presentation Model, so one can do something similar to Ruby on Rails where each View can make calls to multiple Controllers which operates on various Models to complete the desired action! This makes code-sharing between Presentation Models much easier, and also each Presentation Model can be named not after their page but by application functionality!

7 comments:

Mike Roberts said...

Hi Stephen,

A great idea that - I'm going to try and use it where I am now (albeit without Pico, I'm currently using a non-container DI setup.) I'll blog about it, linking to your's, if I get anywhere!

Mike

Chris Donnan said...

Hey

My collegue - Mike Roberts sent me to look over here. Interesting. I have been working on the Spring.net Desktop Application Framework. That said - I have done something similar - make a UIApplicationContainer that manages DI for user controls. The service registry type stuff is provided by an application context - but it is present as well. Since it is in the works - you can only see it in the CVS - but browse the CVS via the web on spring.net's sourcegorge site - or anon download the CVS and look in the /sandbox/Spring.Daf area - or load the Spring.Daf solution (VS2005 only). It is very closely related :)

Good work!
-Chris

Stephen Chu said...

Thanks for the comments! I would love to hear from you guys how things look if you happen to use this technique later. Thanks for dropping by =)

Anonymous said...

Great article. One small note. Technically "native" containers in .Net are any class that implement IContainer and not just the ones subclassing Container. :)

Hilbert said...

Hello! I read this article! Big thanks to author, very interesting. Write more.

Anonymous said...

How do you suggest overcoming the issue that services are not passed to grand-children of the Form?

To pass a service down through the entire hierarchy of components, one must make an association between parent/child containers during initialize-component, and hence, during_the_constructor...

This means some seriously tricky manipulation of Visual Studios serialization support.

Any ideas?

Nguyen Manh said...

夫の浮気 相談
浮気 相談
企業調査 相談
身上調査 相談
所在調査 相談
不倫調査 相談
盗聴調査 相談
池袋 風俗
渋谷 風俗
新宿 風俗
性病検査
大人のおもちゃ
アダルトDVD
av 写真
大人のおもちゃ
アダルトグッズ
アダルトグッズ
アダルト ブルーレイ
アダルトショップ
ペニス増大
電マ
TENGA
SM 通販
メンズセクシー下着
男性用下着
Tバック下着
大規模修繕 工事
決済代行
SEO
SEO
花植物大図鑑
決済代行
ブライダルエステ
FX ランキング
クレジットカード 申込