During my work on ASP.NET projects I passed by a number of problems. Some of these problems may be common and others may be somehow rare. Anyway, I decided to focus on these problems and come up with some sort of patterns to overcome these problems in the future. Don't get me wrong when I said patterns, they are not design patterns, they are just some arrangements which work together to help you not fall in the same mistakes every time.
So, you can look at this topic as a list of tips and hints which may be useful for you while working on ASP.NET projects. May be some of them won't work with your specific business but at least they are good to know.
So, let's start with these tips.
- Centralized logging module: you should spend sometime before going deep into your business code to set some grounds for your project to stand on. These grounds include all the non-business related code which you may use repeatedly throughout your project. One of these grounds/modules is the logging module. To know more about this point, you can check Extensible Logging Library For Sharepoint With ULS Logging Support
- Centralized settings module: the same concept applies here on the settings module. You should prepare a module which handles all aspects of your code related to saving and retrieving business and non-business settings. For sure you can do this by just using key and value pairs through you code but this may cause you troubles and difficulties when you face a change request or a new security constrain. To know more about this point, you can check How To Centralize Your Web Application Settings And Decouple Your Code From Back-End Dependent Logic & Code
- Exposing business related settings: try to expose business related settings and avoid hard-coded values as far as you can because these settings will be subject to frequent change according to the client's needs
- Centralized error handling module: one of the basic and frequently used modules is the error handling module. You will find that at many points in your code you need to direct the system user to an error page with a descriptive message beside adding a record in the logging back-end for further investigation and tracking. For sure you can do this on spot every time you need but this is not a good thing as you will need to keep track of every spot you used this code to apply any further changes. So, the best way to do this is to write a single class with the methods you usually use when you handle an error ans start using this class whenever you need.
- Centralized constants class: throughout your code you will find yourself use some constants which are related to the environment (ie.: hostname, port number,......) or some internal constants which are used in your code and the client doesn't need to manage or some computed values. Instead of hard-coding these values in more than one place, you can centralize these constants into one class which you can use anywhere in your code when you need to use any of these constants. This way, when you need to change any of these values you will have only one place to visit
- Common utilities class: the same applies on utility methods. You will find yourself use some logic repeatedly through your code like converting from string to int with some validation or something like that. In this case, it would be nice to isolate this common logic into one place/class to be used whenever needed
- Always use a masterpage: sometimes you feel like you don't need a masterpage but believe me you may regret this. Always use a master page even if it will be just a dummy parent page. This will save you a lot of time and effort when you need to apply some common logic or UI beside it will be painful to re-visit all your pages to apply changes to connect these pages with a masterpage, so, let's do it at the early beginning
- Take care of static constructors and try to use "Double Locking": We use static constructors to do some initialization or logic the first time our static class is used. This is very useful and already used in may applications and even design patterns like "Singleton". But, you need to be careful when using static constructors when the code inside deals with an external source or by any mean is subject to exceptions and errors because once an exception raises inside the static constructor you will have to live with the consequences till the application is reset. If you want to know more you can check How To Handle Exceptions Inside Static Constructors - Double Locking Concept
- Use separate folder for third party dlls: if you use external third party dlls in your application, create a folder for these dlls other than the "Bin" folder and always add references from this folder
- One bind method: write only one method which is responsible for binding the control and handling the paging and sorting. This way you will avoid repeated logic and handling the same cases more than once.
- Avoid binding when postback: don't bind your control in the page load outside the "!IsPostback" because this will cause some errors and misleading logic. You should keep in mind that every time you click on a button on the page or any other control which causes postback the page load event is executed first before going into the the postback event handler. So, for example if you click on a page number on the pager control, then the page load will be executed first then the "PageIndexChanged" event handler will be executed. So, if you re-bind the control every time you go into the page load event, you will face an error which is telling you you may need to stop the event validation. This is happening because ASP.NET was trying to change the page index in the control as per your request, but before reaching the page index changed event handler you re-bound the control which resets the control and at this point ASP.NET is confused. So, always make sure to not bind the control unless you really need it
- Always bind when data is changed: sometimes you think that you don't need to re-bind your control as the change is already done and the control doesn't need to know more. This is not right, every time the data in the datasource change you will need to re-bind the control. You can trust me on this or you can try it yourself
- Keep current page index in hidden field: the common behavior between some data-bound controls is that they reset the page index on sorting so that when you change the sorting expression the control returns back to the first page. If you need to keep the page index upon sorting you can keep the page index in a hidden field and always use this field, only update this field when an action happens which updates the page index
- Get search values from hidden fields not controls: if you apply searching with some search criteria, then when retrieving data don't get your search criteria values from their corresponding controls directly but get them from hidden fields. This helps you avoid retrieving data upon dummy search criteria unless these criteria are confirmed by hitting the search button. To understand what I am saying, let's say that you have a data grid with paging, user enters value "1" in your search criteria textbox, hits "search", results appear matching the "1", then enters "2" in textbox, hits the arrow on the grid to browse to the second page, then your code will get results matching to the search criteria "2" not "1". This is not logical. So, the best practice is to create a hidden field for every search criteria and update the hidden field value upon clicking the search button. Any time in your code you want to get the value of a search criteria get it from the corresponding hidden field. This way, the criteria will only change when system user hits the search button.
- Use URL encoding when needed: whenever you are passing values in a URL use URL encoding and for sure decoding on the other side
- Use ClientID: whenever you try to access a server side control from client-side make sure to get its id using ClientID property
- Use Encryption/Decryption when needed: whenever you need to pass valuable data between sever and client side think of encrypting and decrypting this data to keep it secure
- Always try to use code generation: always try to use a code generation tool to generate your layers or any other standard code. This makes your code more stable and reliable as these code generation tools are tested and widely used.
That's what I have on mind right now and I will try to keep this list updated. Hope you find something useful in this list.