.NET and me Coding dreams since 1998!

16Aug/070

Design patterns in real world use case scenarios – Dependency injection (set type)

To get a detail explanation of what this pattern stands for check out the Dependency injection post where I posted detail article about dependency injection pattern

How injection can be a pleasant thing

A good development practice is to encapsulate page functionality in a set of controls, because it is easier to maintain that code, performances can be tuned on more granular level, multiple developers can work on the functionality of the same page, we can reuse the control on some other page with similar UI. (I'll stop here writing ode to web controls - I'm sure you read this in almost every ASP .NET book you've seen)

But in real world this composite UI implementation usually comes with a cost due to several reasons:

a) Strict control encapsulation creates redundant code among controls and multiplies calls to database because every control has to retrieve it's own piece of data, update it's own piece of data etc. (Btw, this can be handled with a little async programing, but we today speak about cleaner way)

b) To solve redundancy in a) developers pretty often put shared logic on a page level and then do some "blind casting" like
((SomeSpecificPage)this.Page).SomeSharedElement

This is also a bad practice because sooner or later we would want to use this control on another page and then that require "spaghetti" coding such as"

if (this.Page as SomeSpecificPage!=null)

...

elseif (this.Page as SomeSpecificPage2!=null)

This burden can be eased by pages implementing the same interface, but sooner or later we would like to use a control on page X which is not implementing that interface and creating interfaces everywhere so the control could work doesn't sound clean to me.

Also, to me the fact that control is aware of it's surroundings  looks as a bad OOP practice too

c) To solve the hard coupling done in c), developers usually strive to event publishing, observer pattern, registry pattern etc. Problem from b) is solved by that but we also got a page for which maintenance we need a "map".

The solution here which would be presented tries to solve all 3 problems (redundant db calls, control coupled to its surroundings, high complexity) on a very simple and pleasant way - by using dependency injection setter type, where the page would inject itself to controls on highly simple and decoupled way

 

Problem - use case

Make a web page which would be used by user to update their data.

User data are: last name, first name, street, city, yahoo, email.

Page should be made of 3 different controls: one for names, one for address and one for Internet attributes. Desired functionality is that controls should encapsulate and separate concerns between them on class level as they would do on UI level without additional overhead caused

Why dependency injection pattern?

Dependency injection pattern in short is a pattern where some type is having a property  and implementing its own functionality referring to it, but doesn't set that property by himself. Instead, some external type sets the property value either through the type constructor or by using property setter.

So all you need is a class with a property and: either constructor setting the field property wraps or a property has to implement set block.

If we would take a look at our problem we would see that we need that someone somehow inject into the control user data required by control without control knowing who, how and when. Usage of web page constructors is unusual in web development so we would pick the solution of using property set block - dependency injection setter type

Rule of thumb

In a way, DI can be treated as a "Stranger strategy" because that is a strategy variation where the behavior is defined outside of the type implementing strategy

Usage of dependency injection pattern is usually appropriate when we have a situation where one object for its functionality needs information about the state of another object but we don't want to have any kind of coupled relation between the two objects (such as inheritance, composition).

Sound like our case!

Problem - solution example

Class Diagram of the solution

Dependency injection solution diagram

Page level code

Our page would contain those three controls  (ControlA, ControlB and ControlC) which are supposed to handle their own part of editing user data

image

The page would also have a "SaveUser" button which clicking should persist the updates done by page user

User service

Let's imagine that page for loading user data requires to call a UserService component which makes a very expensive call to database (web service etc) and retrieves whole set of user data.

Something like this diagram

image

and this code

    public class UserService

    {

        public static User GetUserFromDatabase()

        {

            // let's imagine there's been a very expensive database call

            //which retreive user data

            return

                new User("Prague", "malovicn at vuscode dot com", "Nikola",

                "Malovic", "Bryksova", "nmalovic_monster at yahoo dot com");

        }

    }

Allowing every control to repeat this call to retrieve their subset of data is not acceptable so we would make that call only once from page level and store the result on page level in a UserData property.

So the class diagram of the page would look like this:

image

(I'll touch later initControl method)

UserAwareControl

Even I like more interfaces, in this use case usage of abstract classes is more appropriate, because there is a common functionality (not only behavior) of the user controls. They all need user data so they could present them.

So the base control class would look like this:

    public abstract class UserAwareControl : UserControl

    {

        private User _userData;

 

        public User UserData

        {

            set

            {

                _userData = value;

                UpdateUIControls();

            }

            get { return _userData; }

        }

 

        public abstract void UpdateUIControls();

    }

 

There is a property UserData of type User which setter would be used for injection and there is an abstract method UpdateUIControls() which all controls have to implement and which enforce the update of controls when the page would set control user data

Control code

Every control would follow the same pattern as the ControlA does. Only difference would be then that the ControlB would have Street and City text boxes and access appropriate properties; ControlC Email and IM text boxes with accessing appropriate user data properties;

They all look like this:

public partial class ControlA : UserAwareControl

{

    protected void Page_Load(object sender, EventArgs e)

    {

        UserData.FirstName = FirstName.Text;

        UserData.LastName = LastName.Text;

    }

 

    public override void UpdateUIControls()

    {

        LastName.Text = UserData.LastName;

        FirstName.Text = UserData.FirstName;

    }

}

As you can see, control overrides abstract method and takes the property value and sets the ui elements with it.

On a page load event, control takes the content of the text boxes and sets the base control property value.

The injection

The injection would occur on a page level, during the Page_Init event. (Page level because page knows about controls; Page_Init - we need it just once). Page would first made expensive call to retrieve user data and then it would recurevly iterate through the control collections checking if the control is of UserAwareControl type. For all the controls found UserData property value would be set to reference to page property containing  user data and by that page would by that inject itself to control

The same thing said through the code could look like this

    private User PageUser;

 

    protected void Page_Init(object sender, EventArgs e)

    {

        // we load this only once

        PageUser = UserService.GetUserFromDatabase();

 

        initControls(Controls);

    }

 

    private void initControls(ControlCollection controls)

    {

        foreach (Control control in controls)

        {

            /// a little bit of composite recursion

            if (control.Controls.Count > 0)

                initControls(control.Controls);

 

            UserAwareControl userAwareControl = control as UserAwareControl;

            if (userAwareControl != null)

            {

                // setting the control to be aware of page user

                userAwareControl.UserData = PageUser;

            }

 

        }

    }

So, when some control sets a UserData property of the base control, the PageUser property is been set by that, because what we inject to control property is the reference to the page property.

That's how control gets the page single loaded data without coupling itself to page and without complex wire ups between them!

Testing the solution

When we start the web page we see that the control ui elements have been populated

image

After that I have updated some of the text boxes values

image

and then I clicked the button and the result showed that the code is working properly

image

Problem solved!

To download source code click here: Example source code

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.