views:

98

answers:

1

In ASP.Net Web Forms there are cases that I've come across that create order dependent code. As an obvious code smell I'm looking for solutions to solve this problem.

A pseudo-code example would be:

Calling Code :: Page.aspx

protected void Page_Load(...) {
    var control = LoadControl("ControlX.ascx");

    // Ugly since the control's constructor is not used by LoadControl 
    control.SetDependencies(...);
}

Control Code :: ControlX.ascx

public void SetDependencies(...) {
}

protected void Page_Load(...) {
    if (dependencies are null)
        throw Exception();
    else
        ContinueProcessing();
}

LoadControl has two signatures, the one used above accepts a string for the control classes physical location and correctly creates the child controls. Whereas the second signature accepts the control class as a class type, and any parameters for the constructor, however the child controls are not created as detailed in TRULY Understanding Dynamic Controls.

So how can I eliminate this order dependency in the cleanest way? My first thought is that if I dynamically created the child controls in ControlX, but then that can be cumbersome for larger controls. Thoughts?

+3  A: 

(I hope in understood the problem correctly) You could invert the dependecy like this:

The host of ControlX.ascx (either another control or a page) must implement a certain interface (defined by ControlX). ControlX can then access its dependencies from its host through that interface.

A small example would be this:

public interface IControlXHost
{
  // methods, properties that allow ControlX to access its dependencies
  int GetStuff();
}

public partial class ControlX : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var host = (Parent as IControlXHost) ?? (Page as IControlXHost);
        if (host == null) {
            throw new Exception("ControlX's parent must implement IControlXHost");
        }
        else {
            int number = host.GetStuff();
        }
    }
}

The host (the page or the control hosting ControlX) would then have to implement that interface, e.g:

public partial class Default4 : System.Web.UI.Page, IControlXHost
{
    public int GetStuff() {
       return 33;
    }

    protected void Page_Load(object sender, EventArgs e) {
        var control = LoadControl("ControlX.ascx");    
    }
}

IMO, this approach makes controls easier reusable, since they "automatically" tell you about the requirements that have to be fulfilled to host the control. You don't have to find out which methods of the control you have to call in which order.

M4N
Yes you understood correctly. This is definitely something that would work.
Gavin Miller