This post is intended for a beginner to mid-senior level developers who need to create a new asp.net web application. While starting any new application or project, there is a set of questions that need to be answered and answers of most of the questions may remain same from project to project. If you are an architect you may already like to create your framework around this, that you can reuse in different applications.
- The typical questions are:
- Which database are we using?
- How should we access the database?
- Do we need Data Entity Objects?
- How should the database transactions be handled?
- How do we separate presentation/UI layer from business layer?
- What should we do for application logging?
- How to handle exceptions and application errors?
- What are configuration items and where should we store them?
- Is Load Balancing needed? How to handle it?
- How to make application run faster?
Which database are we using?
The answer to this may be beyond the developer’s authority as it is up to the product owners to decide which database they will use for a project. But while deciding about ‘which’ database, following points should be taken into consideration:
- Do we have budget to pay for licensing cost of the database? If not then the choices will be free databases like MySQL or MS SQL Express edition.
- How many users should be able to access the database in parallel and how many transactions we are expecting per minute? If the site is expecting higher number of users per minute (or even hour) then you need a full fledge database system.
- What is the maximum size of the database at a time? MS SQL Express edition has limit of 4GB per database. So, if your database may grow to be 4 GB or more then you cannot use Express Edition.
- What is the amount data added per month? This is important to determine the total size of database in a year or 2 year. Because if you are adding 100 MB of data per month- you will roughly have 1.2 GB of data in a year and 4 GB in 3+ years. If you are designing database and application, you need to plan for purging the database periodically so that the size remains small and it does not grow beyond its planned size.
- Backup schedule – do you need to take periodic back up of the database? And do you have enough space on the drive where you have setup the scheduled backup? If this is not planned, then you may get error (may be to spoil your holiday) while taking backup and many other things can go wrong if the disk is full.
Most of the typical .net applications use MS SQL Server – though the application can very well use other types of databases like MySQL, MS Access, Oracle and others. As long as a database provides ODBC or OLEDB driver, .Net is able to connect to these databases and developer can work with them.
How should we access the database?
The developers will have to pay for whatever decision they take regarding the method to access the database. By reading some blogs or sample examples from web they may employ methods that may ultimately prove to be a mistake.
.Net has its own classes for ODBC and OLEDB via which developers can access the database. But a better practice should be to use the Microsoft Enterprise Architect Data Access Block. The Data Access Block allows developers a common interface to accessing any physical database. The best practice in any project is to write your business layer such a way that business layer does not have to know what physical database is being used.
You should use the Data Access Block to create a Data Access Layer (this is nothing but a Class Library project) that has a method for every atomic database operation that you want to carry out. Make sure that a method in your DAL does not call more than one stored procedure. Because it should be the responsibility of Business Layer to call one or more DAL methods – and DAL should not combine two separate operations into one method. For example, if you need to save a record in Customer table but at the same time need to add records in some Audit/Log table as well – DAL should provide one method to add ‘Customer’ and another methods to add ‘Log’ record. Then Business layer should call these separate DAL methods in a transaction to correctly add customer. It is responsibility of Business Layer (a separate class library project) to add ‘Log’ records while adding ‘Customer’ record.
Do we need Data Entity Objects or Plain Old CLR Objects (POCO)?
POCOs are object representation of each table in the database. When the data is loaded from the database, ADO.Net allows you to store them in DataSet, DataTable, DataRow objects. But as you advance in programming you will understand that you should minimize the use of weakly typed objects such as datatable or datarow. Because these System.Data objects are heavy and large when transferring over the wire via web-service or storing them in asp.net View State.
You can create POCOs by representing each table by a class in your choice of language – C# or VB.Net. If you care about creating POCO, chances are you like working with C#. Once you have POCO, you will be using collection of POCOs instead of DataTable. You may even have methods to convert the DataTable into collection of POCO. Your CRUD methods in the data access layer will have following type of definition:
Void Save(TablePoco1 obj);
Void SaveAll(List<TablePoco1> objList);
List<TablePoco1> GetAll();
TablePoco1 GetById(int id);
The advantage of using POCO is that you can return them via Web-Service and the total response size will be much smaller compared to having DataSet or DataTable. You can also store them in ViewState or Session. But the real advantage to programmer is intellisense. Since it’s a class defined in the solution, Visual Studio will show you all field/column names that you don’t get when you are using DataTable. You are less likely to make mistakes of storing Integer values in the String fields and String values into Integer fields. Your programming becomes easier.
You may think that creating POCO for each table is lot of time consuming work – but you don’t have to write each class by hand. You can employ code generation tools like Code Smith or T4 in such cases. You want to create a template for a POCO yourself (preferable over the default templates such tools have).
How should the database transactions be handled?
A Database Transaction is a single set of operations that need to be completed in entirety or it needs to fail altogether. For example, if you add a record to table A, it must be ensured to update some status or counter in table B. if the insertion in table A fails then update in table B should not occur. This is typical of a simple database transaction.
The visual studio project or the piece of code that handles such transaction should be considered your business layer. If you are placing BeginTransaction/CommitTransaction code in your code behind then you have some serious exercise to do. If you are doing this in your data access layer then you have a chance to separate it out.
- Create a layer (dll) separate from your UI layer (code behind or web project) that handles the transaction.
- Within transaction access the methods of Data Access Layer to keep DAL separate from this layer. Do not call SP directly or queries directly from this layer. Use DAL for that purpose.
- Do not make long wire calls within the transaction like calling web-service. You want to keep your transaction code as fast as possible.
- Keep the business layer running on a server very close to the database server. This way the transaction code will be faster to execute and you will avoid any unnecessary deadlocks.
In one of the projects I had joined earlier team made a mistake by creating web-service over the data access layer and then calling the web-service methods within transaction. This does not solve any problem but rather creates a problem – transaction should make local calls to the database to be able to rollback it.
How do we separate presentation/UI layer from business layer?
Each class in the business layer needs to implement an interface. Having an interface between the UI layer and business layer will allow separating them out.
A novice programmer may think what does ‘separating’ mean? ‘Separating’ mean that if we have to replace the business layer with some other code library then the presentation layer code does not need to change because it is making calls to business layer via interface only. Similarly any changes in the presentation layer do not affect the UI layer as long as the business layer implements the interface.
A common practice is that the interfaces are implemented by web-service client. Web-service client makes call to web-service and web-service is a wrapper over the business service that implements the same interface.
What should we do for application logging?
Error Logging and Exception Handling are two important aspects of any project. The logging should be used for Debugging as well as error logging. You should not let any error go without logging it. And when the error is logged, you must log the function name or parameters and possibly the stack trace.
Every application generates error when it is put in production. When an error is shown to user and if user does not have any other alternative then you will not only lose that user but also lose how to fix the error and what conditions generate the error.
You can use error logging tools like Log4net or Microsoft’s Logging Application Block. We like Log4net for its ease of configuration. You can configure Log4net to write errors to a Log File or a database table or there are other options like MSMQ that we never used. Many web-servers are restricted from creating any local file for security reasons – in that case you can log the errors to database table.
Most important rule(s) for logging is:
- In web application create a global.asax page and handle the Application_Error event. In this event log the error and clear the server error. Because if you don’t clear IIS may shut down your application and it won’t be available to any users. I had seen a client having such problem where their vendor did not clear the server error or did not handle the application-error event.
- Optionally, you can log exceptions from each of the User-Event – like buttonClick, selectIndexChanged, PageLoad etc. If you handle any of such events, you want to provide appropriate message to the user and let user take different course of action to avoid such errors.
- In DAL, you could log the parameters of methods whenever exception occurs that way you can find out what parameters are causing the error and fix the code.
- Remember your goal – information from the log file should help you recreate the problem and fix it so it never occurs again. To achieve this, whatever information you want to log, you should.
How to handle exceptions and application errors?
Many novice programmers don’t understand the meaning of ‘handling’ exception. So if you don’t understand, you are still a beginner. I have worked with programmers with 4-6 years of experience not understanding the concept – though they may know the syntax.
Merely knowing the syntax of try/catch/finally does not help you ‘handle’ the exception.
1st rule is when you ‘handle’ exception, you must ‘do’ something. For example,
Try {
//do something, make some dal call etc.
}
Catch(Exception ex)
{
}
The dangerous part in above is it loses the exception information. So, programmer will never know an error occurred in this code and will never be able to fix it. You have to do something in that catch block. If you want to deliberately ignore the exception then at least write a comment.
2nd – what you do in catch block should add value.
Try {
//do something, make some dal call etc.
}
Catch(Exception ex)
{
throw ex;
}
Above code adds no value whatsoever.
3rd – When you handle exception, based on the error, offer some solution or take corrective measures. For example, if user entered invalid value for an integer and you got exception while converting it to integer, ask user to enter valid value. In case of db calls, the most common thing to do in Exception block is to rollback the transaction.
In any user event entry, you should make sure that there is no exception thrown or if thrown, you are going to display appropriate message to the user. This way, you will guarantee that user does not see incorrect behavior of the application.
What are configuration items and where should we store them?
Each application should have some configuration items or some options. If you don’t have options then you have hardcoded some rules in your system which is a bad practice. Examples of items that you need to make configurable:
- Location of any input/output files
- Email addresses
- Number of records to display in a page
- Theme Name if you have any
You can store these options in two ways:
- In database create SystemOptions (or any name) table to store such options. – this is better
- Web.config file
If you use Web.config file, a better programming practice is to create following type of static class to fetch the config values.
public static class Config
{
public string FromEmailAddress {
get {
string email = ConfigurationSettings[“FromEmailAddress”];
if (string.isnullorempty(email)) email = default@email.com;
return email;
}
}
}
The advantage of such class is if the value is missing in the config file you can still supply a default value. Also, IntelliSense can help you get the Configuration values from such class easily. If you plan to store the options in a database table or some other settings file, you can modify this class to access each value.
Is Load Balancing needed? How to handle it?
Load Balancing is a concept by which you can install the same web-application on two different IIS servers but users will be accessing it via single URL as if it was only one server. The other term is web-farm where your IIS server creates multiple instances of the same web application running in parallel.
In all scenarios, why Load Balancing is important factor to consider during development is because of ‘Session’. For example, when user connects via Server A – a session starts for the user and session data is stored locally on server A. User’s subsequent request may be diverted to Server B for load balancing purpose, now, if the B is unable to get the user’s session data then application will display errors.
To handle such scenarios, applications need to store their sessions in database. This is very easy and web.config allows configuring the application session to use database. Microsoft also provides scripts to create session tables in the database.
Now, if you are creating any files on the web-server you will have to guarantee that it’s available from other web-server too in the load-balance environment. Any web-server specific action has to be handled similarly.
How to make application run faster?
This becomes the most important issue after the application is developed and it goes live. Programmers or even project managers do not worry about the performance till the end. And it may be too late by then. Many times programmer will suggest rewriting the application to make it run faster. And this does not go down well with the management.
Better practice is to think about the performance from the beginning. Even after that, you may still need to revisit the performance again in future as you are nearing the end. But if you have already thought about the performance from the beginning it will be much easier to fix if needed.
Some of the ideas that we have talked about earlier for better performance is:
- Try to keep all database transactions run as fast as possible.
- Use Data Entity Objects and Business Objects and do not use Data Set or Data Tables
- Keep the ASP.Net Page View State as small as possible. When not needed, do not keep the EnableViewState=True and specify it to False. Keeping View State smaller helps making the application much faster.
- Create Cache for commonly used database items. There may be some tables/records in database that are not updated often, load them in cache to reduce the database hits.
- Create database indexes appropriately to run the queries faster.
- If you have to use third party web service calls, you may consider using Asynchronous calls.
- Many non-critical items can be called asynchronously to reduce the page response time. For example, logging some entries or making some database auditing entries should be done asynchronously.