High-Performance .NET Application Development & Architecture

Introduction

It has always been a goal of project architects to plan an effective strategy from the ground up in regards to an new application. All relevant factors are taken into consideration with respect to the application, from its design and layout to a functional website infrastructure. Pre-.NET strategies and design guidelines still effective now were developed with Microsoft's DNA (Distributed interNet Application) platform. This model successfully served the purpose of architecting N(any number of)-Tier (levels) applications. In its basic sense, as in most robust, distributed applications, you'll architect 3 main layers or Tiers: presentation, business rules and data access.
Now, each independent layer within itself, is another issue altogether. Each one has its own methodologies regarding its perspective best practices. In other words, many factors will determine its proper design, efficient data access and visually pleasing presentation. That is why each Tier, with respect to the overall success and health of your application, should be maximally optimized. So in light of such, we'll go through each Tier and dependent of your situation, offer the very best methods resulting in one robust, secure and high-performance distributed application.
One issue that is slightly bemused is that of Performance and Scalability. However similar these may sound in end-result, none could be further from the truth. Performance is a matter of responsiveness, how well a application performs, as our General .NET Best Practices section below will help you capitalize on. Scalability is another subject slightly paralleling performance only within the confines that it has to be high-performance for all users and not affect anyone else nor the tie up the server for that matter. Highly-scalable applications do their best to absolutely minimize network traffic, and server-side database interaction. By these measures then, could scalability be in collaboration with performance.
At any rate, as aforementioned, architecting a robust application involves the time-tested, structurally sound methodology in having an application made up of separate, easily maintainable components that would require modification only once to effect site-wide changes. This, among other things, creates harmony among IT teams, guaranteeing no one will interfere with or break someone else's work. The parts individually, in this sense, are on par to the sum total. To digress, how many audiophiles would buy a all-in-one stereo unit, rather than various individual high performance components? Right, I think none would.
Anyway, before proceeding on, this article assumes you have some basic .NET and or development experience. Still, it's important to realize that this article is a codified manual of tips and optimization techniques for the more or less experienced developer and, or expert looking for more ways in maximizing an application. Beginners may find it hard in following some sections. Nevertheless, I have made ample effort to direct beginners to any outside sources where more in-depth information is available, should one require it, and recommended.
In any case, we'll begin.

Planning

Planning is the first stage all developers embark on - what to do, how to do it or the best way to do it. Proper planning is key in determining that all aspects of the application are architected correctly and efficiently, and the right direction is chosen. Once this is established, the actual development begins to take place. From this point on you'll begin to deal with the application at all levels and its individual components, and how each requires individual optimization. Thus as a whole it becomes one solid, cohesive application.
Good robust, application architecture insists on it being functional, responsive, secure and user friendly. I'll briefly point out that when presenting an finished application, it must be faultless. People like a speedy application with complementing visually appealing elements. Most users will never fully appreciate all the sophistication it took behind the scenes to make the application work, they initially focus on its design and layout. Although, I won't demonstrate web site and graphics design, I felt it a point needing attention, even thought this article will not demonstrate such. So keep this in mind, that for a fully robust, cohesive application, design and back end do go hand-in-hand, not productively but ultimately. So all parts must be on equal level in making it an cohesive application. Again, even thought this article is really more for .NET project architects, it's a fact that some developers out there simply do it all. For some more info of project planning read - Web Application Development - A Guide to Success.
That aside, prior to dealing with the three archetypal levels inherent to "N-Tier," we'll be discussing some conventional information regarding application and server security. After that, we'll dive right into offering general .NET Best Practices. Then move on to error handling, and further deal with some common .NET errors you may encounter, and finally look into performance testing.
As a final note, .NET's documentation offers an absolutely exhaustive collection of information which is the reason why you'll notice numerous links throughout this article directing you to more in-depth information where applicable and necessary.
Now let's get started with .NET security, and rest assured that our application is being built on a secured platform, and that all security precautions were taken and are intact.

Application/Server Security

Security in .NET, and really in any application, is and should be of the highest importance. It is critical that at all times your application be unbreakable and tamperproof, as much as possible, from anything. Anywhere from someone fiddling with your query string to server / data access authorization to preventing SQL Injection attacks.


As the topic of security is really too vast and complex to be dealt with here in any great detail, we'll offer some generalized pointers and tips to set you off in the right direction with a good overview of the various security methods available. Oh, btw, work closely with your Network Admin, make sure you're both on the same page.
Security in .NET works with two types of concepts:
  1. Authentication : Confirms users identity and credentials in allowing them access, through either valid Windows Accounts or IIS, that include methods such as certificate (SSL), Windows (NLTM or Kerberos), Forms, and Passport authentication.
  2. Authorization : Allows or denies file or url access to a given user based on certain criteria, or on ACL (Access Control List) settings in Windows. Authorization parallels .NET's users and roles settings within its web.config file's <authorization> element node. Note all authorization is always done after authentication.
With which introduces us to the following kinds of security implementations.:

IIS Security

IIS (Internet Information Services) is the first line of defense for application security, since all requests must go through here first. This method of security rests solely on setting certain elements found in IIS's Directory Security / Anonymous Access and Authentication Control tab. This verifies IP/Domains and user accounts via ACLs, that are preset Windows or Active Directory User Accounts. Incidentally, ACL access is verified whether or not anonymous access is enabled. Disabling Anonymous Access will bring up the logon dialog box, that would require you a valid Windows account, unless you're on the same domain.

Generally, for any fully public sites you should leave all default settings to manage Anonymous Access . Windows (Windows Integrated Security) handles this, whereby it assigns its own IUSR_computername account the privilege, and all users of this site fall under the Guest group account.

Moreover, you could also disable anonymous access and integrate Basic Authentication (with disabled Windows Integrated Security) that presents the user with the common dialog-driven box to enter in their credentials. Basic security mode further allows control via a domain controller, though this should not be the sole method in doing such. At any rate, Basic Authentication is the most frequently used security measure because this works well among all browsers, and is commonly used in concert with SSL (Secure Sockets Layer) technology for added security.

Another IIS security measure, though not widely used since all browsers do not support it, is Digest Authentication . This was Microsoft's way of remedying one issue that has befallen Basic Authentication which was sending passwords in plain text, and the solution here sends a digest (Hash encryption) in place of the actual password.

Finally, is Integrated Windows Authentication or NTLM challenge/response protocol. This increased security method, since it doesn't work with Netscape, is not really suited for the Internet, but rather a fully IE-inherent Intranet.
Now that we've got a grasp on the IIS's security options, IIS has many more options within its properties that are adjustable for not only additional security measures, but even performance.

.NET/IIS Permissions

Moving on now, .NET allows programmatic determination of NT group permissions as well, using two .NET (principal objects) classes : WindowsPrincipal (automatically created when using Windows authentication) or GenericPrincipal (customizable event using the WindowsAuthentication_OnAuthenticate event (within the Global.asax file) that occurs during authentication, used with None Authentication mode in web.config):
// With users being members of a Windows account group on a domain: 
if (User.IsInRole ("Domain\\Group")) { 
 ...
// Or via Windows built-in account groups users get added to: 
if (User.IsInRole ("BUILTIN\\Administrators")) { 
 ...
Or through user identity and role, including web.config's case-sensitive credential information, as discussed in the next section containing Forms Authentication, and through Windows Authentication:
using System.Security.Principal;
if ((User.Identity.Name == "authorizedUsername") && (User.Identity.IsAuthenticated == true)) { 
   AllowAccess(); 
}
Finally, you could also implement the WindowsIdentity Class to validate a Windows user account, using the GetCurrent method:
using System.Security.Principal; 

WindowsIdentity User = WindowsIdentity.GetCurrent();

if ((User.Name == "Domain\\User") && (User.IsAuthenticated == true)) { 
   AllowAccess(); 
}
Note: Anonymous Access, as well as impersonation, should be disabled to initiate these kinds of Role-Based Security inquiries, and Authentication Mode should be set to Windows in your web.config file. Otherwise, you'll retrieve .NET's IUSR_machinename user account info - "Domain\ASPNET", or simply nothing at all, due to the enabled impersonation.
Moreover, employing this works as well for applying file authorization through the file's Security Permissions - right-click Properties settings, and URL Authorization.
So now that we've got a good overview on identifying permissions, and since we're on the topic of IIS, we'll briefly discuss some general IIS optimizations that will help our server's overall efficiency and performance.

General IIS Optimizations

  • Make sure Mappings under Home Directory-->Configuration-->Application Configuration has Cache ISAPI Applications enabled, as well as Buffering and other Cache Options under Options
  • Also enable HTTP compression found under the Service tab
  • Adjust the Performance Tuning of your server to an amount of anticipated site activity or hits you may expect to get, and enable Process Throttling in preventing your site from overusing CPU processing, which may indicate an external attack of some kind. Both these adjustments can be made under the Performance tab, within the Web Site's Master Properties
  • Finally, enable performance settings for all your sites under the Default Web Site Properties-->Server Extensions tab, and adjust the performance list box for the appropriate amount of pages that you may have on your site

Web.Config or ASP.NET Security

.NET incorporates this configuration file that is capable of doing a great deal when it comes to security, among other things. For starters, it's the perfect place to store all your appsettings or database connection strings that'll be accessed in your application. Furthermore, everything regarding your application's security can be stored here within its perspective nodes, from allowing specific users and groups access to a particular file location path or multiple location paths to storing account credentials, examine specific HTTP access, and standard Authentication.

A typical web.config configuration file

<configuration> 
      <appSettings> 
        <add key="myDataBase" value="User ID=id; _
             Password=pw; Data Source=myDS; _
      Initial Catalog=myCat; Enlist=false;" /> 
      </appSettings> 
      <location path="securePage.aspx"> 
        <system.web> 
          <authentication mode="None | Windows | Forms | Passport"/> 
          <forms forms="securepage" loginUrl="login.aspx" /> 
          <credentials passwordFormat="Clear | MD5 | SHA1"> 
            <user name="Jimmy" password="hashed Password"/> 
          </credentials> 
          </authentication> 
          <authorization> 
            <!-- General application authorization -->
            <allow verbs="POST" users="Jimmy" roles="Administrators" /> 
            <allow verbs="GET" users="Peter" roles="Debugger Users" /> 
   <!-- URL Authorization --> 
            <allow users="domain1\user, domain2\group" /> 
            <!--Deny anonymous or All Users--> 
            <deny users="? | *" /> 
          </authorization> 
        </system.web> 
      </location> 
      <!--For a second secure page--> 
      <location path="securePage2.aspx"> 
        <!-- Other configuration settings--> 
      </location> 
<configuration> 
As seen, the web.config file is capable of many security measures, from public access to Passport authentication. We'll now discuss them in turn:
<authentication mode="None | Passport | Windows | Forms "/>

Impersonation

.NET allows general, public access through impersonation, where IIS uses its own IUSR_computername account process token to control authentication and authorization. As far as strict security is concerned, this is not really it. That is why it best serves all public sites, and is set in the web.config file like so:
<system.web> 
 <identity impersonate="true" /> 
</system.web>
The identity key element holding the attribute impersonate, set to true is the equivalent of IIS's Anonymous access, but directly affects your .NET application here more, since it helps overcome any " Access Is Denied " problems with .NET on a public site.
And due to this, your SQL connection string would be this below, providing both your application and SQL Server share the same domain or trusted domains. Otherwise you'll receive the Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON' error.
"Data Source=myDS; Initial Catalog=myCat; Enlist=false; Trusted_Connection=yes"
For more info on .NET Impersonation, readImplementing Impersonation in an ASP.NET Application.

Default/None (Custom) Authentication (IIS's Anonymous Authentication)

The default setting of no authentication or None mode , is typically for public sites (impersonation). When included for this purpose in your configuration file, does two things, helps performance since no extra runtime processing is needed and could allow customized authentication to take effect, whether application wide via the global.asax or HTTP modules, working with the AuthenticateRequest event.

Passport Authentication

As noticed in the authentication key element above, one such technique, different among the others as I'm sure most have seen is .NET Passport authentication mode . This service offered by Microsoft, is a somewhat of a one-stop shopping methodology, enabling you a unified access across many applications.

Windows Authentication (IIS's Integrated Windows / Digest Authentication)

Next, as aforementioned in the IIS section above, is Windows Mode , the default security method, as it indeed relies on Windows Authentication in IIS for its authentication, prior to .NET gaining control. Furthermore, because of its nature it is best suited for Intranet sites.
In the web.config file, the authentication node is set for ACL Windows authorization, and is also based on the elements contained within the authorization nodes for which users, roles or groups are allowed or denied access, or URL authorization. Additionally, within these nodes you may even specify what type of HTTP action is allowed, using the verbs key, and setting it for either POST, GET, HEAD, or DEBUG.
Here we've set Role-Based Security within the following web.config's <allow> element key setting, and in this instance it'll allow only Admins and Debuggers access, and deny everyone else:
<configuration> 
<system.web> 
  <authentication mode="Windows" /> 
  <authorization> 
 <allow roles="Administrators, Debugger Users"/> 
 <deny users="*"/> 
  </authorization> 
</system.web> 
</configuration>

Forms Authentication

And finally, there is Forms (Cookie based) Authentication Mode , where your dealing with security based upon criteria captured through a form on your web page. This could be Forms-based (using configuration credentials) or Windows/Role-Based, with account credentials set in place and stored in a database, XML file, text file, or even a remote Web Service, rather than solely in an ACL list. Incidentally, this mode enables good flexibility for creating more personalized sites.
Forms Authentication is your best bet for Internet security-based applications such as an E-Commerce site, since it supports all browsers, is fully customizable, and works well with other security additions such as SSL. Incidentally, cookie size limit with IE5 and higher browsers, has no limit.
Here is an example of Forms-Based Authentication using the FormsAuthentication Class ,
if (FormsAuthentication.Authenticate (userName, userPassword)) { 
   //Redirect user back to original URL once logged in or do whatever 
   FormsAuthentication.RedirectFromLoginPage (userName, boolean); 
} else { 
   Response.Redirect ("login.aspx"); 
} 
with the web.config file holding the user's credentials and authorization:
<authentication mode="Forms"> 
 <forms name=".ASPXAUTH" loginUrl="/login.aspx"> 
   <credentials passwordFormat = "SHA1"> 
  <user name="userName" password="encryptedPassword"/> 
   </credentials> 
 </forms> 
</authentication> 
<authorization> 
 <!--Deny all unauthenticated users-->
 <deny users="?"/> 
</authorization> 
Forms authorization as seen, is controlled via cookies (name attribute) that you name within the forms element key, and the loginUrl or login page to use. This is useful for any pages requiring strict security measures, whereby all unauthorized users get sent to your login page to enter their credentials in a form, and must match the Forms Authentication Credentials before access is granted.
Alternatively, you can utilize the same FormsAuthentication class technique above using a database housing users and roles, as well as utilizing the FormsAuthenticationTicket Class. This method of course has its advantages since a database could store as many credentials as you need. Additionally, if you so choose, you could even store credentials within an XML file, providing you set ACL permissions on its location.
Now, as far as encrypting passwords is concerned, its significance cannot be really stressed enough. It's fortunate however, that it is incredibly easy to do for the above credentials using the (get ready for a long one) FormsAuthentication.HashPasswordForStoringInConfigFile Method. This method uses two parameters, one for the password itself, and the other for the type of hash algorithm you require, SHA1 or MD5:
string encyptedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Text, "encryptionMethod");
Furthermore, within the web.config file you could add any specific authenticated page and allow certain user's access, based on specific HTTP method and deny access to all all or specific non-authenticated user's. Subsequently, by choosing Forms Authentication as a custom security measure, then bear in mind that Anonymous Authentication should be enabled in IIS.

Read Forms Authentication in .NET's documentation for more info.

Miscellaneous

Now aside from these aforementioned security measures, I'll discuss a few miscellaneous encryption-enabling configuration settings helpful in further securing your application.
If you're concerned about the security of any viewstate data being examined or manipulated in any way, then set enableViewStateMac (MAC stands for message authentication code) property to true, as demonstrated below, and individually within each .aspx page's @Page Directive - EnableViewStateMac="true" as well. This property hash encrypts the viewstate and ensures that it has not been altered in any way. It stores this information in the machine.config file's <machineKey> node element. Luckily, this is already the default in .NET, phew!
Further encryption of any viewstate, or forms authentication cookie data could be physically set within the web.config by adding - <machineKey validation="3DES" /> , .NET's Triple-DES (3DES) encryption or any other encryption method you prefer.

As for sites dealing with file uploading, we both know limits have to be set. Users obviously can't upload to their hearts content, and bog down the server. So setting .NET's HTTP runtime maxRequestLength parameter, for 2 megabytes as demonstrated below, ensures that any larger files are declined.
<system.web> 
 <pages buffer ="true" enableViewStateMac ="true" /> 
 <machineKey validationKey="autogenerate | value" decryptionKey="autogenerate | value" 
  validation ="SHA1 | MD5 | 3DES" /> 
 <httpRuntime maxRequestLength ="2048" /> 
</system.web>

ADO.NET Security

In all database applications you're dealing with a form of some kind that will be accessed by a user who will enter information to be verified against a database or other means, before any access or data is allowed. Protecting information or access in this situation isn't too difficult a concept, it's simply a matter of storing (preferably encrypted info) into a database, then validating this on login. For more info check out - Using MD5 to Encrypt Passwords in a Database. Almost certainly your site will also include a searchable form for users to enter search criteria in obtaining data.
Aside from these somewhat innocent scenarios, and by virtue of creating forms that enables users the ability to submit information to your application, you're left vulnerable to a malevolent technique exploited by hackers known as SQL Injection . This is a security threat whereby SQL statements or other malicious code, through form fields, are injected in addition to your code and executed, and can cause great damage to your data, whether they retrieve, delete confidential information or simply cause havoc among it!
To prevent SQL Injections you should:
  1. Validate all entered criteria via regular expressions for any mischievous keywords or text. Furthermore, apply HTML Encoding to any inputted text. Fortunately, version 1.1 of the .NET framework, offers superior validation, whereby if any user enters certain un-encoded HTML syntax or characters, .NET will raise an error. Read Request Validation - Preventing Script Attacks for more info.
  2. Use parameterized Stored Procedures (Sprocs) for your queries
  3. Create custom error pages so no one could inadvertently retrieve any data or server information from non-custom error pages.
Some added security measures when working with ADO.NET are:
  1. If caching data, store critical info server-side via session state or with the Cache object. I discussed this in light of a real-world example in my article Drilldown Datagrid Searching with ASP.NET, and read more about the various method for storing state.
  2. SQL server access should be performed through Windows Integrated Security, not "mixed mode," and never leave the default "sa" username and blank password as is, set a good username and password instead, which lead us to number 3.
  3. If possible create a trusted connection to SQL with the Integrated Security set to SSPI (Security Support Provider Interface), as this eliminates the need in storing any user id's and passwords.
    "Data Source=myDS; Integrated Security=SSPI; Enlist=false; Initial Catalog=myCtlg" 
    However, you may encounter - Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON,' where remedying this is to have your Windows Account be a Domain Account and share the same or trusted domains with IIS and SQL Server.
  4. Encrypt your connection strings in your web.config file, or other critical data elsewhere, and then later decrypt them, via the CryptoStream class in your page code, when you need to use them. Also dependent on the size of your data, whether large or small will determine Symmetric or Asymmetric Encryption.
  5. Finally, never hard-code anything, like any of the aforementioned, in your client-side pages.

Code Security

This security measure deals with setting permissions on projects, files, code and resources from anything unauthenticated. The CodeAccessPermission class comes in to play in allowing administrators the power to delegate certain types of access to various resources, thus ensuring authorized access only. It works alongside the trust element key within the web.config file in setting code access security (Full is the default):
<trust level="Full | High | Medium | Low | Minimal" originUrl="url"/>
Read Secure Coding Guidelines for the .NET Framework and Secure Coding Guidelines for more info .
Furthermore, as this also applies to all aspects in this article, if you really, really need the utmost security for very critical data, you do have a couple of nice options:
  1. Retrieve sensitive information and settings using serialization , that converts data to bytes for any type of transmission, that later gets deserialized on the other end.
  2. Directly reference an object's (dll/assembly) metadata (binary info about your dll) and other information or invoke methods contained within it during run time via Reflection using the System.Reflection namespace
Look at ASP.NET Security for more info and if you got the time read Microsoft's 608 page - Building Secure ASP .NET Applications treatise. Furthermore, peruse ASP.NET Architecture as well.
Now that we've dealt with security as an good overview, we now can move on to the actual components involved in making our application's architecture robust and high-performance. We'll first begin looking at some best practices that will determine general application health, before we discuss the best methods with each Tier.


General .NET Best Practices

The best practices to be discussed stem from real-world .NET application use, and have proven themselves very viable and sound in producing very scalable, high-performance applications. Since I'll be dealing with .NET programming a la hand coding and web forms, those interested in strict Visual Basic.NET optimizations should see - Performance Optimization in Visual Basic .NET for more info. Nevertheless, the best practices discussed here, generally apply to VB.NET as well. Moreover, all the tips offered here are general application page optimizations.
  • Right at the top of your .aspx page's @ Page directive, it is always important to disable any features you won't need or use. For example, set EnableSessionState to false on any pages not requiring use of session state, as well as Debug, Trace and EnableViewState. Alternatively, if you need to access Session State values but won't be modifying any, then use EnableSessionState="ReadOnly". For strongly-typed error-free compilation, Strict and Explicit should be set to true to enforce early binding and variable declaration. Moreover, always set the page's Buffer to true for nice performance boosts, and application-wide within the web.config file's system.web nodes as well - <pages buffer="true"/> .
    <%@ Page Language="C#" Debug="false" Strict="true" Explicit="true"
        Trace="false" EnableSessionState="false" EnableViewState="false"
        Buffer="true" %>
    Note, all of the above attributes are applicable to an .ascx user-control's @ Control directive, except for Buffer, Trace and EnableSessionState.
  • Another important attribute of the @Page directive is AutoEventWireup. When this attribute is set to true, the .NET runtime doesn't require events on the page like Page_Load or Page_Init to indicate any implicit event handlers like the Handles clause, rather it takes care of them for you. You probably may have changed this attribute in the past only to discover that your controls wouldn't fire, well this is why. Therefore, to improve performance and handle events manually, set AutoEventWireup="False" and override the method. So, our commonplace Page_Load as shown,
    void Page_Load (Object sender, EventArgs e) {
    
    //Your code here
    
    }
    
    is transformed to the overridden method event shown below. In order to override the default event handler, you'll need to rename Page_Load to OnLoad and change its access modifier keyword to Protected. Also notice how its Sender parameter is omitted, so that it matches the event handler.
    [C#]
    
    protected override void OnLoad (EventArgs e) {
    
    //Your code here
    
    }
    
    [VB]
    
    Protected Overrides Sub OnLoad (e As EventArgs)
    
    'Your code here
    
    End Sub
     
         
  • On Page_Load set Response.BufferOutput to true and Response.Flush at some later key point in your code.
    void Page_Load(Object sender, EventArgs e) { 
    
        Response.BufferOutput = true; 
    
        //...... 
    
        Response.Flush(); 
    
    } 
  • Utilize Page caching by incorporating the @ OutputCache directive to speed up page display by storing this page is memory. You start by specifying, in seconds, the duration for the cache to remain in memory, your particular request scenario and its location. Other customizable request attribute settings include VaryByCustom, VaryByHeader, VaryByParam and VaryByControl , and for the Location attribute (not supported in User Controls or .ascx files) you're allowed - "Any | Client | Downstream | Server | None."
    <%@ OutputCache Duration="7200" VaryByParam="value" Location="Any" Shared="True" %>
    Output caching could also be enabled with the HttpCachePolicy Class. Not only that, but the OutputCache directive include-syntax also fully applies to Fragment Caching or caching server user controls (.ascx) files separately as well, and works along the exact same lines. However, one distinct advantage this has over standard Page output caching, is that the user control file supports the Shared attribute setting, whereas the .NET Web Forms page (.aspx) does not. Therefore, by setting your server user control's Shared attribute to true, you in effect allow this control to be shared among all your pages, as opposed to creating separate cached versions of it. As you can probably guess, you'll end up with more available memory and even better performance! Now that's not all, caching your code-behind's source file is very different from what we've seen. To cache your code-behind control you'd declaratively implement the duration attribute in your source file's metadata like so: [C#]
    [PartialCaching(7200)] 
    
    public class myClass : UserControl { 
    
       //Your code here 
    
    }
    [VB]
    <PartialCaching(7200)> _ 
    
    Public myClass : Inherits UserControl 
    
       'Your code here 
    
    End Class
    Reference PartialCachingAttribute Classfor more info.
  • Aside from Page Caching, Data Caching is extremely important, and what you tend to do once you present data to the client. How have you developed the page in question in light of scalabilty and performance? If the user is returned 10,000 records that will assuredly require paging, will your application keep re-hitting the database each time or does it take advantage of .NET's data caching methods? I have written two articles that cover such topics in much greater detail .NET Data Caching and Drilldown Datagrid Searching with ASP.NET. I'll refer you to these two articles for all the information you'll need. As for more generalized data access, we'll be discussing this in greater detail in our Data Layer section, and reveal different scenarios and the best data retrieval method to be used for the best performance.
  • Check for Postback on Page_Load to avoid unnecessarily hitting the server when the page is posting back to itself:
    void Page_Load(Object sender, EventArgs e) { 
    
       if ( !IsPostBack ) { 
    
         //...rest of code here 
    
       } 
    
    }
  • Use Server.Transfer instead of Response.Redirect to redirect any virtual pages on the server, as doing so avoids checking for postback before it executes, thereby conserving server resources. Note, that this method won't work of course when redirecting to an external site. Additionally, if your page contains form variables that also need to get transferred, just set Server.Transfer's preserveForm boolean parameter to "True", like so: Server.Transfer("MyPage.aspx", True)
  • Don't unnecessarily use server-side web server controls for simple tasks where standard HTML controls would suffice.
  • Use .NET's StringBuilder class for all your string writing/creation and manipulation needs.
  • When it comes to your code and values try to minimize boxing and unboxing of values, as this can cause a performance hit.
  • Be very paranoid when it comes to any text you'll be accepting through forms, or query strings. Unless imperative and not practical although easier, utilize POST primarily instead of GET, for any data requests or passing among pages.
  • Use <%@ Page AspCompat="true" %> only if it's an absolute must where you require pre-.NET apartment-threaded VB6 COM object compatibility. It truth, it's best that you simply make the effort to fully migrate your application completely to .NET, as this is better in the long run.
  • Particular settings within your configuration file - web.config , prove useful in improving application performance. First of all, set all pages to buffer, as mentioned. Second, if your site is fully public, setting authentication to None mode further helps, as well as disabling site wide debugging. No public pages or applications should ever have debug mode enabled.
    <system.web>
       <authentication mode="None" /> 
       <pages buffer="true" /> 
       <trace enabled="false" />
       <compilation debug="false" /> 
    </system.web> 
  • Take advantage of .NET's machine.config file processModel attributes. They have much to do with the robustness of the application as it's the server and how well it lives that will further epitomize your application. The old days of rebooting IIS for component registration or recycling and so forth are long gone with all of .NET's way cool abilities contained within this one file. Heck, this file even helps with automatic crash recovery! Read Configuring the ASP.NET Worker Process through the <machine.config> file for some good tips.
  • When compiling any classes in a code behind scheme or as a data object, outside of VS.NET, use /optimize thus assuring smaller, more efficient runtime code:
    (csc/vbc).exe /t:library /out:c:\inetpub\wwwroot\bin\sf.dll sf.(cs/vb) /optimize
Within VS.NET, you could enable optimizations through your Project Solutions properties, under Configuration Properties-->Optimizations.
Furthermore, you could utilize .NET's Native Image Generator (Ngen.exe) to cache your components or applications (.exe) within the GAC (Global Assembly Cache) for faster start-up times:
ngen myComponent.dll/exe
  • When possible and applicable, utilize Asynchronous Calls for added scalability to your application, anywhere from I/O actions to ASP.NET Web Forms.
  • Always, always remember to close, null and dispose of all your objects. Even though .NET's garbage collection scheme is magnificent, do it as well nevertheless.
  • For maximum readability, always properly indent your code.
  • And finally, add comments to your code. .NET comment tags are <%-- Code --%> , whereas dependent on the language you've chosen, VB uses an apostrophe ' to comment out code and C# uses slashes // . This practice ensures not forgetting certain logic or program flow when the time arises. We don't always remember why we coded what we did or how, do we?
Now that we've gotten some great performance enhancing tips for our standard .NET pages, we'll proceed to the N-Tier portions, and individually deal with each one.

Directory Structure

This should be a simple enough concept, separate all key components in their own folders: directories like: includes, images, bin, info, documents, stylesheets, scripts, etc. You get the idea, what a nightmare to have your entire application in one folder, yikes! Furthermore, for stronger security measures, never place your site's folder in your server's root folder.
Also working with a source code control software such a Microsoft's Visual Source Safe goes a long way in protecting code. Many editors available now from HomeSite to Dreamweaver integrate this. Furthermore, Source Safe promotes versioning as well, just like .NET.

Presentation / Business Layer

N-TIER
The presentation layer is just that the presentation of the application via a User Interface (UI). It encapsulates our HTML code and any controls or components that make up our entire application. In classic ASP this page would look like a unpleasant interspersion of code, or spaghetti code as it's known. Simply put, this means server, client and HTML code all mixed together without a care in the world.
This approach is messy, problem prone and doesn't follow .NET's Object-Oriented approach. Our .NET page in comparison will be cleanly laid out, and lightly coded. Why? Well, again because of .NET Platform model. How? With .NET's code-behind . Code-Behind is, as its name implies, the method where all your commonly used code is placed in another file, "behind" the page, in a dll or vb/cs source file, that get's inherited via your page's @ Page Directive. Alongside this benefit, there are performance advantages of writing code this way, since .NET automatically pre-compiles these controls! The page's @ Page Directive inheriting code-behind is thus:
<%@ Page Inherits="myNamespace.ClassName" Src="source file" %>
The source file would look something like this:
          // Page inherits code-behind source file
using System; using System.Diagnostics; //For Tracing, Performance Monitoring using System.Drawing; // For Graphics, Datagrid Colors using System.Web; // For HttpContext using System.Web.UI; //For Literal Controls using System.Web.UI.WebControls; // For Datagrid
namespace myNamespace {
// All your variables, properties here public class ClassName : Page { //Inherits Page Class
// All your code here }
}
Above we imported any necessary libraries we might need. Next we created our namespace, and within this our class, which is what our Page's @ Page Directive will inherit for code-behind to work. However, if you prefer for whatever reason, you could simply create a standalone class in your code-behind file, whereby the Page Directive's Inherits attribute is the class name alone, instead in our example where it is "myNamespace.ClassName."
Also notice that our class inherits the Page class, because this is what constitutes our code-behind interaction with our caller page.
Alternatively, if you compile your source code into a dll,
(csc/vbc).exe /t:library /out:c:\inetpub\wwwroot\bin\sf.dll sf.(cs/vb) /optimize
you would then import it and instantiate it, like so:
          // Import It 
<%@ Import Namespace ="myNamespace" %>
// Then Instantiate It
[C#]
ClassName myObjectName = new ClassName();
[VB]
Dim myObjectName As New ClassName()
or better yet just xcopy it into your server's main bin folder and declare it from within your page's <script> tags, like so:
 // Import it


[C#]

public myNamespace.ClassName myObjectName = new myNamespace.ClassName();


[VB]

Public myObjectName As New myNamespace.ClassName


// Then instantiate it in your code within a Function or Method


[C#]

ObjectType myVariableName = myObjectName.MethodName(Parameters);


[VB]

Dim myVariableName As ObjectType = myObjectName.MethodName(Parameters)
 

        

Another method that implement's code-behind methodology are pagelets or .NET's new improved include files/ Web Server User Controls , or .ascx files . These files expand on the content (UI)/code separation by including commonly used code, in nicely stored controls, again just like include files in classic ASP, but certainly more powerful. How? Well, they for one they can assign and pass parameters, not to mention handle events!
Apart from the more conventional application of user controls, and normal code-behind modules, yet another instance is in creating reusable, Custom Server Controls . Basically these are new, custom built, super-duper controls, acquiring their functionality from inheriting its target control's base class, and expanding on it. Creating one works along the same lines as the above source code, except instead of a Page code-behind file inheriting the Page Class in that example, you'd inherit your particular control's base class. Take for example creating a custom datagrid control:
          namespace myNamespace { 
// All your variables, properties here
public class ClassName : DataGrid { //Inherits DataGrid Class
// All your code here
}
}
After you compile your source file, you'd then register your assembly for use in your page like so:
          <%@ Register TagPrefix="myControl" Namespace="myNamespace" Assembly="myDll Name" %>
Then implementing it is as easy as:
          <myControl:myNamespace ID="MyControl ID" runat="server" property1 = "false" _
property2 = "Red" OnClick="Click Method Name" />
Notice how (providing your source code includes this) your custom control could accept and set, parameters and or properties, and handle events, as we've mentioned. Moreover, this could also apply to standard code-behind user controls as well! Custom control creation tends to get more advanced, and the coding skill requirements jump up a few notches. At any rate, a user controls' more common usage stems, more or less, in reusable site code, i.e. a page's header and footer for instance.
Below demonstrates a typical, albeit, bare bones .aspx page. It has a header and footer, whereby the respective code is separated between two web server controls or modules - header.ascx and footer.ascx. All your page would need to do is register them, and place them in the page accordingly:
          // The control's first get registered at the top of the page 
<%@ Register TagPrefix="Header" TagName="Top" Src="header.ascx" %> <%@ Register TagPrefix="Footer" TagName="Bottom" Src="footer.ascx" %>
// Then get placed in your page where needed
<body>
<Header:Top ID="Header" runat="server" />
<Footer:Bottom ID="Footer" runat="server" />
</body>
Man, .NET rocks! Now that's a nice, clean presentation I think! But wait, that's not all, your web server controls themselves could also further inherit code-behind code! In this case the inherit/src members are implement this time via the control's @ Control directive.
// Header.ascx
          <% @ Control inherits="ClassName" src="source file" %> 
// Your code here
And your control's code-behind file is identical to your page's code behind file, except the control doesn't inherit the Page class, but rather the UserControl class, since it's a control that's calling this file.
          // User Control's code-behind source file 
using System; using System.Web.UI; using System.Web.UI.WebControls; public class ClassName : UserControl {
// Your code here
}
Additionally, if you compile your code-behind file as a dll with a namespace that looks like this:
          namespace ClassName { 
public class myClass : UserControl {
// Your code here
}
}
Then there are now two ways for including it for use in your page. You could inherit it as follows:
          // Inherit it 
<%@ Control inherits="ClassName.myClass" src="source file" %>
Or you could import it and instantiate it as demonstrated earlier. Still yet another cool feature in .NET is the ability for the global.asax file to inherit code-behind as well!
<%@ Application Inherits="MyApp" %> 
For more info on the fine art of OOP Programming, it'll be worth your while in taking the time to thoroughly read - The Quick and Dirty .NET Guide to C#/VB Object-Oriented Programming.
As a final afterthought, since a majority of your initial code within your pages and source file will interact on your code's Page_Load method, you may find that aside from this common function, you may to need to process something even before this or something else as well. Well then have a look at these six additional Page methods, that are contiguous to your Page's event handlers:
  • Page_Init - for pre-Viewstate processing
  • Page_PreRender - for pre-Page rendering tasks.
  • Page_DataBind - for when the Page binds data
  • Page_Dispose - for when the Page get's disposed
  • Page_Error - for any errors on the Page
  • Page_Unload - for code to run on Page unloading or exit
So the above are simply just something to keep in mind. Now you should have a real good idea on creating a clean Presentation Layer with the use of web server controls.
Business Rules/Logic Layer:
This layer is quite simple, as it involves all specific and particular business rules and requirements to any given company. It enforces rules pertaining to data that is retrieved from a database, i.e. mathematical, monetary, formatting, etc.
These rules are stored as methods in this layer separately from other components, thus any changes effective to this app would take place only once. Again, these are functions or methods capable of anything required by your application, to effect all particular business practices. Therefore, in light of efficient N-Tier design, you really do want to avoid placing any business logic in your presentation layer, and here is where it would all go.

Data Access Layer

The data layer, on the other hand, is where we'll be covering various scenarios, and the best methods to use for the best performance and results. This layer encapsulates and compartmentalizes all our data access code within nice, clean components, that interact with our database. Also, keep in mind that the following tips apply to non-Tier standalone applications as well :-)
Incidentally, take the time and prep yourself with An Introduction to ADO.NET for a good look at ADO.NET, with v1.1 features as well, prior to diving into this section. Furthermore, although this section is named Data Access, it could nevertheless include XML as a viable data source. Therefore, read Reading, Storing and Transforming XML Data in .NET for a good look into this methodology.
Now to start, some quick tips off the top dealing with both aspects of data, SQL and ADO.NET. We'll divide these here solely to offer optimal means in each for prime data access. This involves both efficient database design and queries, and employing proficient ADO.NET techniques in retrieving already polished data.
SQL
  • Normalize your data for maximum efficiency and faster performance.
  • When creating new SQL tables always consider the best data types to employ with the type of column data you'll be storing.
  • Index your tables using SQL's Create Index command and or Query Analyzer's Index Tuning Wizard, assuring that your databases are finely tuned.
  • Use SQL Stored Procedures for all your data access, as SQL Server compiles them for all future uses. Furthermore, in SQL, straight and narrow is the best method in writing SQL Stored Procedures. Never overcomplicate your procedures with unnecessary or excessive temporary tables, server-side cursors , as these do create performance bottlenecks. You're better off creating subqueries off a base query and or with a join (avoiding left joins).
  • Never name your stored procedures with a "sp_" prefix, as SQL will interpret this as a system procedure.
  • Create SQL Views for added clarity and security.
  • Include SET NOCOUNT ON in all your stored procedures, as this diminishes network traffic by eliminating the need for SQL in always returning how many rows the procedure affected.
  • Use sp_executesql to execute any standard non-Sproc SQL statements in your code, gaining Stored Procedure-type benefits and reducing overhead:
    sp_executesql N'Select * from table' 
    and even within a Stored Procedure, instead of strictly EXECing any query strings:
    DECLARE @sqlQuery nvarchar (100) 
    SET @sqlQuery = N'SELECT * FROM database.dbo.table'
    EXECUTE sp_executesql @sqlQuery
  • Finally, optimize SQL Server itself to maximize performance. Read SQL Server Settings Optimization Tips for more info. ADO.NET
  • Create all your database access routines as generic, versatile objects, rather than client-side repeated-code methods. All your UI should do is interact with these components, and not have to work out any details. The methods aforementioned in the Presentation Layer apply here as well in creating components and controls.
  • For all intents and purposes, the golden rule for data access is as follows: If you want to page data or provide your application with functionality use a Dataset as the preferred method of disconnected data, otherwise use a Datareader for all your data retrieval. For XML users this would translate to an XmlReader , and StreamReader for text files, both equivalent as that of a DataReader for their respective file types.
  • Use the correct managed data provider for your particular database, ex. System.Data.SqlClient for SQL Server , System.Data.OleDb for Access, System.Data.OracleClient for Oracle , etc.
  • Use Strongly-Typed Datasets over the standard, common un-Typed ones when possible, as this yields better performance. A generated typed Dataset is basically an early bound class inheriting the Dataset base class, as opposed to the typical late bound, non-inheritable Dataset. Thus, it allows for greater flexibility in dealing with your data. In turn, you'll be dealing with easier to read code, whereby you can access fields and tables by customizable names, instead of the conventional collection-based way. There's a good article here on DNJ that you should definitely read called Using Typed DataSets . Here you'll find all you need to know to get you going.
  • As mentioned briefly in the General .NET Best Practices, take full, and I mean full advantage of .NET Caching, as this will significantly boost performance and greatly diminish any persistent database interaction. However, and an important however is, if you decide on caching your data from within your data object, then conventional caching methods won't apply. Within the confines of components, and its interaction with the caller page, these requests happen via an HTTP request. Therefore, caching within your component can only be implemented by using the HttpContext.Cache Class property, part of .NET's Page class.
  • Use parameterized Stored Procedures along side .NET's Command Class Prepare() method, that caches your query for all future SQL Server uses.
    objCommand.Prepare()
  • Make chunky calls to your database rather than smaller, chatty calls. It's better to group all similar, associated calls in one SQL Server access. In other words, use one solid connection to retrieve as much as you can, as opposed to multiple ones.
  • Avoid using Universal Data Link ( UDL ) files for OleDb connections as these can cause potential performance hits. .NET's SQLClient managed data provider does not support this, as prior versions of SQL Server did.
  • Take advantage of connection pooling (whereby all your connection strings are identical) by storing all your connection strings in your web.config file. Furthermore, you'll find that your application will scale alot better with any increased network traffic by doubling, even tripling the default Max Pool Size of 100 to 300, and even bumping up the default Connect Timeout of 10 seconds to 60. Additionally, if your not enlisting any transactional procedures, include enlist=false; to your database's connection string for added performance. Additionally, if your not enlisting any transactional procedures, include enlist=false ; to your database's connection string for added performance. Example:
    <configuration> 
    <appSettings>
    <add key="myDatabase" value="User ID=id; Password=pw; Data Source=datasrc; _ Initial Catalog=dbCat; Enlist=false; Connect Timeout=60; Min Pool Size=10; Max Pool Size=300;"/>
    </appSettings>
    <configuration>
and call it from your page, code-behind source file or component like so:
            [C#] 
ConfigurationSettings.AppSettings["myDatabase"].ToString();
[VB]
ConfigurationSettings.AppSettings("myDatabase").ToString()
  • Finally, remember to close, clear and dispose of all your data objects no matter what. If you would like to further confirm that your database connection is indeed closed, you would write:
    if (dbConnection.State != ConnectionState.Closed) { dbConnection.Close(); }

Common ADO.NET Scenarios

  1. You want to retrieve one single value or item: Use the command class's ExecuteScalar method.
    object dbValue = objCommand.ExecuteScalar();
    Remember, this method always returns an object that you have to convert/cast to your specific type.
  2. You need to retrieve just a single row of data instead: Use the ExecuteReader's SingleRow Command Behavior
  3.               objDataReader = objCommand.ExecuteReader(CommandBehavior.SingleRow);
  4. You want to retrieve multiple database rows and automatically close your connection: Use a data reader.
    objDataReader = objCommand.ExecuteReader(CommandBehavior.CloseConnection);
    Not fast enough, then read all data sequentially as a data stream via ExecuteReader's SequentialAccess Command Behavior. Note that although obvious, I should point out all fields are to be read and access in sequential order starting from 0, dependent on your SQL query columns order.
    objDataReader = objCommand.ExecuteReader(CommandBehavior.SequentialAccess); 
    Still not enough? Read all fields by index position or strongly-typed accessor method, instead of field name
    string value = objDataReader(0); 
    or try
    string value = objDataReader.GetString(0);
    *The GetString is one of the Datareaders type specific members to read columns thus reducing any runtime conversions. And get your results with your preferred method of datareader field reads:
    while (objDataReader.Read() == true) 
    { 
    Response.Write (objDataReader(0));
    }
  5. You'd like to open a database connection, read your data, then close it all pretty quickly without too much code This can be accomplished only in C#, with the using statement.
    using (SqlConnection string) {


    // Do database work

    } // Now connection is automatically closed
  6. Best method to retrieve values from a Stored Procedure, instead of looping through rows with a datareader:
    objCommand.ExecuteNonQuery();
  7. Want even better functionality when working with SQL Server? Then use Microsoft's new Data Access Application Block (DAAB) 2.0 . .NET introduces the SqlHelper class that dramatically cut's down development time by allowing you to execute commands against a SQL database, with Sprocs, all within a few lines of code (about 75% less code than you would typically need)! The example below demonstrates how incredibly easy it is to implement a Datareader to connect to and query a SQL database, whether using simple SQL syntax or a SQL Parameter, and all with one line of code!
    <%@ Import Namespace="System.Data" %> 
    <%@ Import Namespace="System.Data.SqlClient" %> 
    <%@ Import Namespace="Microsoft.ApplicationBlocks.Data" %> 
    <script language="C#" runat="server">

    void Page_Load (Object Source, EventArgs E) {
    string strConn = "server=(local);uid=sa;pwd=;database=northwind;"; int categoryID = 1;

    //Using a SQL Query //Connection String, Command Type (Text), and SQL Query
    SqlDataReader objDataReader = SqlHelper.ExecuteReader_ (strConn, CommandType.Text, "SELECT * FROM Products");


    //Using a Sproc //Connection String, Command Type (Stored Procedure), and Parameter SqlDataReader objDataReader = SqlHelper.ExecuteReader_ (strConn, "getProductsByCategory", categoryID);

    while (objDataReader.Read() == true) {
    Response.Write (objDataReader[1].ToString() + "<BR>");
    }

    //Close and clear our object objDataReader.Close(); objDataReader = null;
    }
    </script>
    That's amazing, considering this would usually be accomplished with roughly more or less 10 lines of code! The SQLHelper class used here provides the same amazing robustness for the Dataset. Not only that, but you're allowed transactional and Dataset database updating, as well as exception management! Now to implement this class into your project, you can do this one of two ways. One, as a private assembly by simply xcopying the Microsoft.ApplicationBlocks.Data.dll into your project's bin folder, or two, as a Shared Assembly whereby you install the dll into the Global Assembly Cache using .NET's gacutil.exe command line utility while noting that it has to be assigned a Strong Name. This can be accommodated using the Assembly Linker (Al.exe) utility program. Finally, within Visual Studio.NET, it's all a matter of simply referencing the DLL, by importing into your project. This sure is a cool new class, huh? I think so too. Download the Data Access Application Block for .NET v2 here. Finally, the DataGrid. Now, we've all use the DataGrid and probably more so than any other .NET control, but it can be cumbersome when binding and dealing with large sets of data, so every little bit of optimization helps, and one such technique involves to avoid using the Eval runtime reflection DataBinder.Eval(Container.DataItem ("Item")) expression in the Template Columns when data binding your DataGrid. Instead, convert the DataGrid's DataItem to its specific type which is a DataRowView (if your binding with a DataReader then the item type would be IDataRecord - check out Dynamic DataGrid Paging and Sorting Using A DataReader for DataReader/DataGrid binding), and take away some of the runtime burden, like so:
    <%# CType(Container.DataItem, DataRowView) ("Item") %>
        
    Still not satisfied? Then an even better method of data binding is accomplished by ordinal position, like in the aforementioned DataReader example #3:
    <%# CType(Container.DataItem, DataRowView) (0).ToString() %>
    This last method would be the fastest, but you have to be careful that in modifying your query fields and or order, you will most likely break your app. So you have to be sure what your query contains and is doing.
So there you have most ADO.NET data access scenarios that you'll run into while creating your application. Pretty sure you've got enough now to get you moving. Now that we've gotten our Tiers taken care of, we'll discuss methods that'll ensure us from having our application blow up in our faces - Error Trapping and Handling.

Error Trapping & Handling

Error handling is an essential consideration in all applications. You always want to make sure users aren't scared half to death with some unintelligible error message. Furthermore, you never, ever want any server-side errors ever shown in any way to the client, ever!
Not only that, but you'd want to catch any errors that may in effect break your application. Usually employing typical if-then conditionals are the best way to go in dealing with standard errors and controlling program flow. Still, at times prior to critical code execution, you may need further protection, and better overall error handling. However, overusing try/catch exceptions can decrease system performance!
.NET offers its try/catch error handling , where you'd first issue "try" to run the following code, capture any errors that may occur, and display the exception message, then take any appropriate steps to remedy it. Moreover, this error code block further attaches a finally to itself for any final code to execute:
          try { 
myDataGrid.Databind()
} catch (Exception e) {
Response.Write ("The following error occurred :" + e.Message);
} finally {
myDataGrid.CurrentPageIndex = 0
}
Of course, using finally is not mandatory, and doesn't require it being included. For more info read - Handling and Throwing Exceptions and Best Practices for Handling Exceptions. Further VB error objects available is the On Error Statement, capable of resuming next and going to a preset error handling label via Goto. Alternatively to assigning custom error pages through IIS's Custom Errors tab, another such method involves our famous web.config file. Invoking the try/catch error blocks do well for code errors, but server errors? Nope, not these. Here we'll use web.config's custom errors nodes :
          <system.web> 
<customErrors mode="On" defaultRedirect="errorpage.aspx">
<error statusCode="403" redirect="AccessIsNotAllowed.aspx"/> <error statusCode="404" redirect="PageNotFound.aspx"/> <error statusCode="500" redirect="InternalServerError.aspx"/>
</customErrors>
</system.web>
Now all errors are redirected to more friendly error pages, and if you prefer, why not pull in additional error information, and e-mail yourself or the appropriate party when the error occurs.
Don't forget that you may also implement global error handling not dealt with our try/catch code blocks, via your global.asax file's Application_Error method using .NET's System Error codes:
          protected void Application_Error (Object sender, EventArgs e) { 
Server.Transfer ("error.aspx?error="+Server.GetLastError().Message);
}
Now error handling in .NET, can and should at times be given over to Stored Procedures via the two good methods found in SQL:
  1. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_globals_9ghe.asp:
    DECLARE @RetValue int 
    UPDATE database SET database.dbfield = 'Jimmy' WHERE dbfield = 'Dimitrios'
    IF @@ERROR <> 0
    BEGIN
    PRINT 'An error occurred'
    SET @RetValue = @@ERROR
    END
    ELSE
    BEGIN
    PRINT 'All OK'
    SET @RetValue = '0'
    END
    RETURN @RetValue
    With the @@Error function you declare a variable for the error, return this value at the end and read this client side or print the error through SQL itself. Note an error return of zero (0) indicates no errors at all.
  2. RAISERROR:
    Hard-coded error messages:
    RAISERROR ('Error Occurred in : %s', 16, 1)
    
    Or utilizing the sp_addmessage system stored procedure to produce error messages using the msg_id, as long as the msg_id is over 50000.
    RAISERROR (50001, 16, 1, @columnId)

      Debugging

      Debugging is the art of program flow error handling, where you track down errors that has prevented your code from fully executing. .NET has much improved the classic ASP way of Response.Writing to find and trap program errors and flow. To employ debugging you could do this:
      1. Application wide in your web.config file:
        <system.web> 
        <compilation debug="true"/>
        </system.web>
      2. Or locally and easier is by setting Debug to true in your page's @ Page Directive and also your user-control's @ Control Directive

      Tracing

      Tracing in .NET is another means of debugging, but allows you to insert custom statements added to your page's output, whereby you can "trace" the flow of your code and determine the order of things at run time. Tracing, contrary to debug mode, presents you with a lot of information about your page. It further can help much in ascertaining any performance issues concerning your code. In any event, like debugging, tracing could be implement the same two ways:
      1. Application wide via the web.config file:
        <system.web> 
        <trace enabled="true"/>
        </system.web>
      2. Or locally within each page's @ Page Directive:
        <%@ Page Trace="true" TraceMode="SortByTime | SortByCategory" %> 
        And within your code you would trace by writing:
        Trace.Write("Tracing has started") 
        Trace.Warn("Examine more closely here")
        Trace.Write("Tracing has ended")
        Trace. Write produces standard output, whereas using Trace.Warn would output your text in red for added notice. And bear in mind that the best place to Trace is usually before and after any major code sections, i.e. Datagrid binding, editing, etc.
      With all this error handling now under our belt, what happens now when an error does occur? How do we fix it? Well, next we'll look at some common, though not basic, .NET system errors.

      Common .NET Errors

      • CS0019: Operator '&' cannot be applied to operands of type 'type' and 'type'
        This happens when you are trying to use the wrong operator for the specific operation.
      • CS0029: Cannot implicitly convert type 'object' to 'string'
        This error occurs when you are typically trying to present an object as a string. The solution here is to append ".ToString()," that now represents the object as a string.
      • CS0103: The name 'whatever' does not exist in the class or namespace / CS0246: The type or namespace name 'whatever' could not be found (are you missing a using directive or an assembly reference?)
        This will usually occur when you have not declared a variable globally and or it's missing altogether. CS0246 error also occurs when you are improperly declaring a object or variable, as well as it not existing in your code.
      • CS0117: 'object' does not contain a definition for 'Length'
        Again this has to do with improperly trying to convert a value. To remedy this properly cast or box the value in question.
      • CS0118: 'System.Configuration.ConfigurationSettings.AppSettings' denotes a 'property' where a 'method' was expected
        This error occurs when your AppSettings key is accessed with parentheses in VB, instead of brackets in C# or vice-versa.
      • CS0119: 'whatever' denotes a 'class' which is not valid in the given context
        One cause of this error is to improperly call an object without the "new" keyword in C# and "As New" in VB
      • Object reference not set to an instance of an object
        This runtime error is more often than not caused by your code trying to reference an object that is not available, and for the most part you've probably closed the object or set it to null or nothing somewhere in your code in all valiant attempts to clean up any unnecessary objects from memory. For example, a connection object you've opened and used, but now somewhere you've closed it and set it to nothing, and somewhere after in the code it's being called again and when it does you'll get this error. Find and delete the line and close and null your object later on. Notwithstanding, this runtime error will also occur in the circumstances shown above in CS0119.
      • CS0161: 'method name': not all code paths return a value
        This will happen when you are trying to make a function method behave like a subroutine or vice-versa. Use the proper method keyword to remedy this. Void is for methods that do not return a value, although you could response write the output. Whereas, applying a keyword value like string, int, works alongside a return value at the end of the method.
      • CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
        This error is produced by a number of sources.
        1. If you call an object that requires parentheses () after the command, you'll get this error. Ex. database.Open() not database.Open
        2. Using an assignment operator as opposes to an equality operator
      • CS1001: Identifier expected / BC30183: Keyword is not valid as an identifier.
        Both these similar errors occur when you forget to identify a variable without a keyword, or you were inadvertently forcing a keyword to behave as a variable or type.
      • BC30451: Name 'whatever' is not declared.
        This happens when you are referencing a web form control or variable that either doesn't exist or is named different. Also by chance the control in question may be missing the mandatory runat="server" option.
      • BC30512: Option Strict On disallows implicit conversions from 'type' to 'type'.
        Improper type conversions produce this error. Convert the value or method to the proper cast to fix this.
      • BC30554/BC30560: 'object' is ambiguous.
        This error occurs when you have more than one similarly named dll in your bin folder when compiling or it matched your control inherits call. Furthermore, more than one dll root namespace or class within are named the same. Your page or control are trying to access one source file where a source file and dll may simultaneously exist.
      • BC30574: Option Strict On disallows late binding
        This can happen from any number of reasons in your code. Some common ones are:
        1. Not setting the object's proper data type
        2. An expression that binds data was incorrectly parsed against the object at runtime In Datagrids, for instance, this occurs when omitting the DataBinder.Eval method on a data field
      • BC30684: 'name' is a type and cannot be used as an expression
        This can happen usually when you forget to use the new keyword when instantiating an object or invoking a constructor.



        Performance Testing

        I really won't dwell too much on this topic, as whole books are available on this topics alone! Performance testing takes time and patience to fully realize before and after results. To monitor your site's performance, use the Admin Tools' performance monitors and take the time to add the appropriate counters to the system monitor and take it from there.
        Not only that, but .NET gives you ability to programmatically do the same things, alongside accessing system logs and system processes with its System.Diagnostics namespace. Read Displaying Performance Monitor Information through an ASP.NET Web Page for more info.
        At any rate, read the book in the above link, and check out the article. Subsequently, Microsoft also offers a software tool that pushes your app to its limits, its WAS Tool. This tool and other useful information can be found here - Performance Optimizing Tools.


        Conclusion

        Well you have alot to think about and take into consideration regarding the creation of a finely-tuned application machine, both scalable and secure, not to mention lightning fast. At least it'll give you pride in the finished result, when you present a robust, high-performance application, with little or no down time. In the world of Internet development and deployment, that speaks volumes!
        Until Next Time. Happy .NETing </>
        

    Comments

    Post a Comment

    Popular posts from this blog

    Cloud Computing in simple

    How to Write an Effective Design Document

    Bookmark