Common Design and Coding Problems in ASP.NET Applications
This article describes the common design and coding problems I discovered in
several ASP.NET web applications.
How did I discover these problems? I inherited several ASP.NET web applications
over the past several years. My responsibility was to review the web application,
locate problems, correct the problems, and create working web applications.
Poorly designed and coded web applications impact clients in several ways:
- Higher development costs.
These clients paid two (or more) developers to create the web application. The initial
developer made a non-working application. Clients had to locate and pay a second
developer to correct the problems created by the first developer.
- Lost revenue.
A web application with many problems can not be put into production. The client
is not collecting fees from customers and thus loses revenue.
- Lost Productivity
The client must explain to two (or more) developers how the web application must work. The client
can't focus on other business tasks while they are spending time directing additional developers on how
to create the web application.
Here are some suggestions on how clients can save money and collect revenues sooner when hiring
web developers for their projects.
- Chunk your project into several small projects.
- Write carefully thought out descriptions of each feature.
- Think "Continuous Improvement". Create a basic web site with the minimal features.
Put it into production. Add a few features or improve upon the existing features. Put the
new version into production. Repeat as needed.
For each problem in this article I:
- Describe the problem.
- Give an example of the problem.
- List the disadvantages caused by the problem.
- Give a solution.
- Give an example of the solution.
- List the value of the solution.
Using Fixed Numerical Indexing to Access Arrays
The code indexes into arrays using fixed numerical indexes.
Bad Example 1 - Stored Procedure Parameters
Insert or update a record with 60 values:
SqlParameter[] Params;
Params = new SqlParameter[60];
Params[0] = new SqlParameter("PropertyId", SqlDbType.Int);
Params[0].Value = PropertyId;
...
Params[59] = new SqlParameter("ZipCode", SqlDbType.VarChar, 20);
Params[59].Value = ZipCode;
// Call SqlCommand.ExecuteQuery to insert or update the database.
Bad Example 2 - Reading Data from DataRow
Read a record from the database and get the values
from the DataRow:
DataRow Dr;
DataTable Dt;
int PropertyId;
string ZipCode;
// Select property records from database
Dr = Dt.Rows[0];
PropertyId = (int)Dr[0];
...
ZipCode = (string)Dr[59];
Disadvantages
- Difficult to update code to get new values from arrays.
- Difficult to update code to put new values into arrays.
- Difficult to know whether values are copied to the correct variables.
Solution
Use alphabetic keys to index into arrays instead of numerical indices.
Better Example
DataRow Dr;
DataTable Dt;
int PropertyId;
string ZipCode;
// Select property records from database into DataTable.
PropertyId = (int)Dr["PropertyId"];
...
ZipCode = (string)Dr["ZipCode"];
Value
- Easily update code to get values from or put values into arrays.
- Higher probability values are copied to correct corresponding variables.
In-Line SQL Statement Composition
The code composes SQL statements in-line by concatenating strings.
Bad Example
Code composes an SQL statement to insert a new record into the database.
string Sql = "";
Sql = "INSERT INTO Properties (Address,City,State) ";
Sql += "VALUES ('" + Address + "','" + "','" + City + "','" + State +"')";
Disadvantages
- Difficult to ensure SQL syntax is correct: Are single quotes balanced?
- Difficult to add new parameter values and ensure SQL syntax is correct.
- Difficult to test the SQL statement by itself.
- Difficult to use SQL statement from multiple areas in the application.
- Vulnerable to SQL injection attacks. Parameter values can contain malicious SQL when values obtained from web forms.
Solution
The solution is to create a SQL stored procedure to access the database and
write code to execute the stored procedure.
Better Example
SPR_Database DbObj;
SqlParameter ParamAddress;
SqlParameter ParamCity;
SqlParameter ParamState;
SqlParameter ParamZipCode;
using(DbObj = new SPR_Database())
{
DbObj.Command.CommandText = "PropertiesInsert";
DbObj.Command.CommandType = CommandType.StoredProcedure;
ParamAddress = DbObj.Command.Parameters.Add("@Address", SqlDbType.VarChar, 100);
ParamAddress.Value = Address;
...
DbObj.Command.ExecuteNonQuery();
}
Value
- Easier to ensure SQL syntax is correct. SQL tools validate SQL syntax.
- Easier to add new parameter values.
- Easier to test SQL stored procedures.
- SQL stored procedures can be used from multiple spots in the application.
Wrong Database Data Types
The database is designed with the wrong data types.
Bad Examples
A boolean true\false values represented with a VARCHAR data type:
Approved varchar(1)
A record id represented with the wrong numeric data type:
PropertyId NUMERIC(18,0)
Disadvantages
- Potential for invalid values to be stored in the database.
- Potential for invalid conversion between data type at code level and database levels.
- Consumes unneeded database space.
Solution
Use the correct data type to represent the data.
Better Examples
- Use BIT instead of VARCHAR data type.
- Use BIGINT instead of NUMERIC(18,0)
Approved bit
PropertyId bigint
Value
- Faster database performance.
- Less database space required.
- Only valid values can be stored in database.
- Eliminates potential for invalid data type conversions.
Unneccessary Data Conversions
Code converts between boolean values and string "Y" and "N".
Bad Example
Here's code for a property. The get accessor converts the string "Y" or "N"
to a boolean, the set accessor converts the input boolean value to a string.
public bool Approved
{
get
{
bool Status = true;
if (m_Approved == "Y") { Status = true; }
else if (m_Approved == "N") { Status = false; }
return Status;
}
set
{
if (value == true) { m_Approved = "Y"; }
else { m_Approved = "N"; }
}
}
Disadvantages
- Increases potential for invalid values to be stored in the class.
- Difficult to understand code due to code clutter.
Solution
Be consistent in using same data values.
Better Example
public bool Approved
{
get
{
return m_Approved;
}
set
{
m_Approved = value;
}
}
Value to This Approach
- No invalid values stored in class variable.
- Easier to understand code due to less code clutter.
Replicated Code Blocks
The developer made multiple copies of the same code blocks in multiple
in the web project. I have seen the contents of entire web forms replicated: one web form to
add a new record, and a second web form (with the same fields) to update the record. The web
form contained 50-60 fields.
Disadvantages
- Difficult to keep all code blocks in sync when making changes.
- Difficult to test.
- Obscures code functionality due to code clutter.
Solution
Create a single module, such as, a class, user control, server control, or web form, to
contain the code. Use the module as needed throughout the application.
Value
- Easier to update single module with changes.
- Can be used in multiple projects.
- Reduces potential for bugs.
- Easier to test.
- Easier to understand code due to less code clutter
Read All The Data Every Time
The code reads all columns for a record every time data is required for any and all tasks
such as, display or processing.
Suppose an application has a table with 60 columns. The application read all 60 columns
from the database, copied the values into class member variables every time the code
needed even a single value.
Disadvantages
- Slower application performance.
- Slower database performance.
Solution
Read only the data required for each task in the application.
Create a single class and create a method for each set of data required by each task.
Suppose, a table exists to store residential home property information.
The application may need different sets of information at different times:
Better Example
public class Property
{
public DataRow AddressRead()
{
}
public DataRow LoginRead()
{
}
}
Value
- Faster application performance.
- Faster database performance.
In-Line HTML
The code contains dynamically constructed HTML. In some applications I have seen the same
code to generate the same HTML replicated in multiple locations in the application.
Bad Example
string Html = "";
Html = "<div class='Code'>" + "The hungry cow jumped over the moon." + "</div>";
Disadvantages
- Slow code performance when concatenating strings to build HTML.
- Generated HTML might be browser incompatible.
- Generated HTML might have syntax errors.
Solution
Ideas:
- Use ASP.NET HTML OR ASP.NET Web Controls
- Sublcass ASP.NET HTML or ASP.NET Web Controls to create a custom control.
- Create a single custom server control and call as needed in your application.
Value
- Improved code performance.
- HTML is browser compatible when using standard ASP.NET controls.
- Easier to debug browser compatibilities when using a single server custom control.
Embedded CSS
The code contains embedded CSS. In many instances embedded CSS was replicated
in many areas of the application.
Disadvantages
- Developer must update embedded CSS. Client or graphic designer can't update CSS.
- More time needed to debug CSS related issues due to needing to recompile web project for every change.
- Difficult to maintain multiple copies of the same embedded CSS in sync across application.
Solution
Move CSS to a separate CSS stylesheet file. Link to the CSS file using the <link /> tag
Better Example
<link href="SPSC_StyleSheet.css" rel="stylesheet" type="text/css" />
Value
- Client or graphic designer can update CSS without developer involvement.
- Client or graphic designer can debug CSS related issues independent of developer.
- Easier to maintain consistent CSS for whole application.
Storing String Constants
Database tables store string constants.
Bad Example
Suppose the goal of the databaes is to store data related to residential homes:
address, property information, such as square footage, etc.
The database was designed to store a U.S. state name string in the property record.
Disadvantages
- Difficult to maintain a valid list of string constants.
- Difficult to modify string constants to correct spelling errors, etc.
- Difficult to populate drop down lists, etc with the correct string contants.
- Potential for invalid values to be stored in the table.
- Slow database performance when searching on the string field.
Solution
- Create a table to hold the set of constant strings.
- Create a foreign key to the records in the string constants table.
- Create a database relationship between the tables and keys.
- Create ASP.NET user or server conrtols to display the string constants for drop down lists, etc.
Better Example
Value
- Easier to maintain the valid list of string constants.
- Easy to correct incorrect string constants.
- Easy to populate drop down lists, etc. with the valid set of string constants.
- Reduces potential for invalid values to stored in the table.
- Faster database performance when searching based on the set of string constants.
Multiple Classes Per Source Code File
A single source code file contains two or more classes.
Bad Example
A single file containing two or more class definitions.
public class Property
{
...
}
public class Images
{
...
}
Disadvantages
- Challenging to locate a class when the file name differs from the class name.
- Creates code clutter hindering comprehension of the code.
Solution
Put one class per source code file.
Value
- Easier to lcoate a classes.
- Less code clutter.
Hard-Coded Text
The code contains hard-coded text. In many instances the same
text phrases are replicated in multiple locations in the code.
Disadvantages
- Client can't change text.
- Developer involvement required to change text. Increases costs.
- Difficult to keep the multiple copies of text phrases in sync when making changes.
Solution
Put text in the database:
- Create a database table for the text.
- Insert text into the databae.
- Write a class to retrieve the text from the database using a key.
- Write a method in the class to display the text phrase.
Create a simple administrative user interface for the client to add and modify text phrases.
Value
- Client can change text at any time without developer involvement.
- Ensures same text phrase used consistently throughout application.
Credit Card Data Stored in PlainText in Database
The code accepts credit card number, expiration date, security code,
and credit card holders name from the user and stores the information
in the database without enciphering the data.
Disadvantages
- Malicious users can hack into the database and steal credit card data.
- Security breach can result in loss of reputation for client.
Solutions
- Seek credit card payment solutions where it is not required to store the credit card data
in your database.
- Encipher the credit card data. Secure the encipher keys.
Value
- Malicious users can't get the credit card data if it's not stored in your database.
- Malicious users can't decipher the credit card should they access the database.
Input Data Not Validated
The code does not validate the data or performs poor validation of the data
retrieved from web form input controls, such as, TextBoxes,
FileUpload, etc.
Bad Examples
Example 1: The user enters an invalid zip code into a TextBox. The code retrieves the value
from the TextBox and attempts to compute a latitude and longitude using Google Map services.
Example 2: The code successfully validates a single character for a password.
Disadvantages
- Invalid data can cause program exceptions.
- Invalid data can cause downstream data computations to be incorrect.
- Easier for malicious users can hack into the system (single character password).
Solution
Validate input data using ASP.NET Validator controls, or custom code.
Review validation algorithms to ensure data is correctly validated.
Value
- Reduces or eliminates potential for program exceptions.
- Ensures downstream data compurations are correct.
- Reduces potential for malicious users to hack into the system.
Embedded JavaScript
The ASP.NET C# code contains embedded JavaScript.
Disadvantages:
- Difficult to test.
- Difficult to use in multiple locations in the application.
Solutions
Option 1:
- Put the JavaScript in a separate file.
- Include the JavaScript in the HTML by using <script src="FileName"></script>
Option 2:
- Put the JavaScript in a separate file.
- Code a method to read the JavaScript file and add it to the HTML by calling:
Page.ClientScript.RegisterClientScriptBlock().
Value
- Easier to test.
- Can be used in multiple locations.
Hard Coded Data
Code contains hard coded data which should be stored in the database.
Disadvantages
- Difficult to maintain consistent data.
- Difficult to change data. Must search for all places in code where data resides.
Solution
Create database tables for each set of related data, populate the tables
with the data, and remove the data from the source code.
Value
- Easier to maintain data.
- Data always consistent.
Master Pages Not Used
The developer replicated the same HTML formatting tags for page layout in many or all web pages
for the application.
Disadvantages
- Difficult to apply page layout and formatting changes consistently across all web pages.
- Difficult to understand due to code clutter.
Solution
Use ASP.NET Master Pages:
- Create a master page
- Edit the master page with the common page elements and page layouts.
- Apply the master page to all web pages.
Value
- Easier to update with common layout changes.
No Subclassing
The code did not use subclasses in cases where needed, although
web project used Object-Oriented Programming languages, such as, C# or VB.
Disadvantages
- Additional code required to perform tasks.
- Obscures code function due to code clutter.
Solution
Create subclasses.
Value
- Reuse base class variables and methods.
- Easier to see purpose of code due to reduced code clutter.
Same Data Stored in Multiple Tables
Several database tables contain the same data.
Disadvantages
- Difficult to maintain data consistency.
- Extra code required to update multiple instances of the same data. Creates code clutter.
Solution
Create a single database table, migrate the data to
the new table, and remove the data from the original tables.
Value
- Easier to maintain single set of data.
- Less code clutter, easier to comprehend purpose of code.
Hard-Coded Absolute Urls
The code uses hard-coded absolute urls to redirect to other pages
in the application.
Disadvantages
- Difficult to execute and debug the application in development, beta test, and live environments.
- Extra development time required to update base urls run application in desired environment.
Solution
Use relative urls.
Value
- No development time required to update urls based on run environment.
- Easier to run and execute application in development, beta text, and live environments.
Error Dialog Boxes
System displays error messages in a popup dialog.
Disadvantages
- User forced to perform extra steps to dismiss dialog box.
- Difficult for user to see correspondence between errors and related fields.
Solution
Display error messages near the fields which caused the error.
For ASP.NET applications one can use and place Validator controls, such as,
RequiredFieldValidator, RegularExpressionValidator, etc. near the data
input or data selection controls.
<asp:TextBox ID="ZipCode" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator ControlToValidate="ZipCode"
ErrorMessage="Bad ZipCode"
ValidationExpression="[0-9]{5}-[0-9]{4}"
runat="server"></asp:RegularExpressionValidator>
Value
- Less steps required of user to see errors.
- Easy for user to know correspondence between errors and related fields.
User Forced to Re-Enter Data
For a login form: User enters email, clicks on Login button,
system displays error message informing user email invalid.
User clicks on Forgot Password button\link, system displays
a Forgot Password form with an empty email input text box. User is
forced to re-enter email.
Disadvantages
- User must perform additional and unneceesary steps to accomplish a simple task.
- User can become frustrated.
Solution
Design the login form and code to allow the user to login and request their password.
Value
- User accomplishes task in fewer steps resulting in less frustration.
Data List Stored in Database
A set of data formatted in a comma-separated list is stored
in a VARCHAR field in the database.
Bad Examples
1,3,5,7,11,13,15
Alaska, Minnesota, Massachusetts, Wyoming
Disadvantages
- Potential for code to incorrectly format comma-separated list.
- Potential for errors when parsing data list.
- Can't enforce database relationships (if data items are record ids).
- Slow database search performance if searching performed on data list.
Solutions
The solution is dependent on the specific problem and needs of the application.
One potential solution assuming the data list contains record ids:
- Create a "link" table between the source and destination tables.
- Create SQL stored procedures to insert, update, or delete records into or from the "link" table.
Value
- Ensures only valid data is used.
- Faster database search performance.
Procedural Programming
Portions of the applications were programmed in a procedural programming paradigm (although in some
cases the web projects used an object-oriented language like ASP.NET C# or VB).
Additionally, the procedural code to perform a single function was distributed across several source code
files.
Disadvantages
- Difficult to reuse.
- Difficult to replace the procedural code with a new improved version.
Solution
Redesign the code into object-oriented programming paradigm.
Value
- Easier to reuse the oop classes.
- Easier to improve the code.
Unnecessary Conversions
The code performed unnecessary conversions between data types.
Bad Example
string Count;
int Count2;
// Store value in session variable.
// Note: value stored as integer data type.
Session("Count") = 23;
...
// Code performs unnecessary conversion to string.
Count = Session("Count").ToString();
// Code performs additional conversion to an integer data type.
Count2 = Convert.ToIn32(Count);
// Code should be:
Count2 = Session("Count");
Good Example
Session("Count) = 23;
...
Count = Session("Count");
|