2

Is it good design to use many user controls to help refactor a web application?

In my case, it's a VB.NET Webforms ASP.NET website. All our pages are organized into sections that, while related and belong on the same page, don't need to interact with each other. Each section contains many asp controls (text boxes, labels, grid views, buttons, etc). Most are forms the user will fill out to submit or view data. Due to the multiple sections and multiple number of controls per section, the code files can get very large.

Turning each section into a user control seems like it would be a nice way to refactor the pages. Each section now gets organized in its own file and the code gets taken out of the large page file.

We'll probably end up with 100+ user controls that aren't likely to be reused on other pages, however it seems like a better option compared to Partial Classes as it allows us to use Lazy Loading with the user controls to improve page load times.

Note: A lot of refactoring is going to happen whether we use user controls or not. If we choose to split up our page using user controls, the code that each control would contain is also going to be refactored, breaking things up into classes and functions where possible. We have the idea that user controls might be a way to help improve on our code in addition to the other refactoring as described in the question.

1

I've found that there is way more repetition of code in a WebForms application than is immediately noticeable. I've been banging my head with this exact problem, and I've come up with several remedies.

DRY Up Your UserControls (And Models, Too!)

When you look at each individual page, you see so many differences that you can't possibly imagine code is being repeated. Look at the data you are saving to the database. On an application I work on, we have about 10 different forms all saving "people" records to the database with the same fields, but different fields are available in each view. At first I thought there was too much custom code, but then I realized it's all the same data in the same table. I created a generic User Control to edit "people" objects -- Populate form fields based on a domain model, and repopulate the domain model based on the form fields.

I also created a view-model that encompassed display logic, and then use that to affect the user control.

Person Domain Model

public enum PersonType
{
    Applicant = 1,
    Foo = 2
}

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string TaxId { get; set; }
    public IEnumerable<PersonType> Types { get; private set; }

    public bool IsApplicant
    {
        get { return Types.Contains(PersonType.Applicant); }
    }

    public bool IsSomethingElse
    {
        get { return Types.Contains(PersonType.Foo); }
    }

    public bool RequiresTaxId
    {
        get { return IsApplicant; }
    }
}

Person View-Model

public class PersonViewModel
{
    public Person Person { get; private set; }

    public bool ShowTaxIdField { get { return Person.RequiresTaxId; } }

    public PersonViewModel(Person person)
    {
        Person = person;
    }
}

(A Snippet Of) The User Control

<p id="TaxIdField" runat="server">
    <asp:Label ID="TaxIdLabel" runat="server" AssociatedControlID="TaxId">Tax Id:</asp:Label>
    <asp:TextBox ID="TaxId" runat="server" />
</p>

The C# Code-Behind

public partial class PersonUserControl : System.Web.UI.UserControl
{
    public void SetModel(PersonViewModel model)
    {
        TaxId.Text = model.Person.TaxId;
        TaxIdField.Visible = model.ShowTaxIdField;
        // ...
    }

    public PersonViewModel GetModel()
    {
        var model = new PersonViewModel(new Person()
        {
            TaxId = TaxId.Text.Trim(),
            // ...
        });

        return model;
    }
}

Push Business Logic Into Your Domain Models

Removing duplicated code also means moving business logic into your domain models. Does this "person" require a tax Id? Well, the Person class should have a property or method called IsTaxIdRequired. Watch Crafting Wicked Domain Models for some additional ideas.

But I Use DataSets...

Stop! Immediately. You are intimately tying your C# code to the underlying database schema. Even if you have to hand write a mapping layer between DataSet objects and your domain models, you'll still be further ahead because now you can bundle behavior and business rules with your data in one neat, and tidy class. By using DataSets, your Design and Code-Behind files implement business logic! Stop this as soon as you can. Even though the WebForms framework doesn't strictly adhere to the Model-View-Controller pattern, the C# Code-Behind should be viewed as the "Controller" and the Design file should be seen as the "View". The only thing left is the "Model", which should not be a DataSet. DataSet objects give you data, but no behavior leading to repeated business logic.

Use Helper Classes For Initializing the User Interface

All to often I see this in 15 different UserControls:

public InitStateDropdownList()
{
    ddlState.DataSource = ...
    ddlState.DataBind();
    ddlState.Items.Insert(0, new ListItem("Select", string.Empty));
}

Push this into a helper class:

public static class DropDownListHelper
{
    public static void InitStates(DrowDownList list)
    {
        list.DataSource = // get options from database or session
        list.Items.Insert(0, new ListItem("Select", string.Empty));
        list.DataBind();
    }
}

And use it:

DropDownListHelper.InitStates(ddlState);

Some people rile against "helper" classes, but it's surely better than writing what is essentially the same 3 lines of code in multiple places. Even if you can't create super generic User Controls, at least weed out the repetitive code that initializes components upon page load.

When is the BIG Rewrite the Answer?

Almost never. For all my complaints about the WebForms framework, it at least compartmentalizes pages and forms, which is a great setup for a long term refactoring job. The key is adding test coverage. I've had a lot of success using CodedUI Tests and SpecFlow for testing WebForms applications, since most if not all business logic is scattered between C# and ASP in 19 different files. At least write some tests to validate existing business rules don't get broken during the many refactoring sessions you have in your future.

  • Thanks. The refactoring is being done for a few reasons. We're moving all SQL out of code behind and switching to use the Entity framework, covering things with tests, applying optimizations, code maintenance, etc. It's already a large amount of changes. User controls was just one change we thought would benefit maintainability and possibly optimization through lazy loading. So it seems like a valid addition to the list. – Jeremy K Oct 21 '14 at 18:12
0

In the real world if you are not going to re-use code/controls then do not waste your time making them re-useable. It just isn't worth the effort. However if you may re-use them in the future then make them re-usable.

You say "VB.NET Winforms ASP.NET website", however you cannot have it as Winforms and ASP.Net/Website it would have to be one or the other.

  • 1
    Presumably Jeremy means "webforms" – Brian Oct 21 '14 at 11:49
  • Yes, my mistake, fixed it. – Jeremy K Oct 21 '14 at 14:25
0

Don't ever make a user control that will only get used once, all it will do is hide the real problems and make maintaining things even more difficult. If you have huge code files from the sheer number of controls on a page, you need to make more separate pages not user controls. It already sounds like you have multiple sections on a page that could relatively easily be totally separate pages.

  • The problem with splitting sections into their own pages is that we greatly increase the size of our navigation menu then. It's already large enough. Sections are grouped into pages as they group related functionality. Also, additional refactoring is being done to improve design. User controls wont just be a copy/paste of current page sections. It's just one potential piece of the puzzle. – Jeremy K Oct 21 '14 at 18:19
  • Also, I see the potential of it organizing our site better. If Page A has sections 1, 2 and 3, and section 2 needs work, we should only need to look at the section 2 user control (and maybe Page A) – Jeremy K Oct 21 '14 at 19:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.