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.
Best Answer
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
Person View-Model
(A Snippet Of) The User Control
The C# Code-Behind
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 calledIsTaxIdRequired
. 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:
Push this into a helper class:
And use it:
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.