ASP.Net notes ASP.Net Notes

ASP.Net Web Forms

ASP.NET Web Forms pages are text files with an .aspx file name extension.  An ASP.NET page can be created simply by taking an existing HTML file and changing its file name extension to .aspx; no code modification is required.  The .aspx file is compiled only the first time it is accessed; the compiled type instance is then reused across multiple requests.

Unlike with ASP, the code used within the above <% %> render blocks is actually compiled - not interpreted, using a script engine, resulting in improved runtime execution performance.  In addition to (or instead of) using <% %> code render blocks to program dynamic content, ASP.NET server controls can be used to program Web pages.  Server controls are declared within an .aspx file, using custom tags or intrinsic HTML tags that contain a runat="server" attribute value.

ASP.NET supports page code, declared within the originating .aspx file, or the code-behind method, enabling the page code to be separated from the HTML content into a separate file.

3 tier web architecture overview

Data access layer

Stored procedures provide a clean separation between the database and the middle-tier data access layer.  Maintenance is easier, since database schema changes will be invisible to the data access components.

Business Object Layer

Map classes to the database tables.  Group classes into a NameSpace to organize components.  A middle-tier component requires a database connection string for every query.  To prevent needing to recompile a component for connection string changes, place the connection string in the AppSettings section of the web applications configuration file, web.config (located in the root of the web applications virtual directory).

Presentation Layer

The presentation layer is built using ASP.NET Web Forms.

Implementing 3 tier web architecture

ASP.NET Server Controls

Declare a Server Control as follows:
  <asp:Label id="MyLabel"  class="MyLabelText" runat="server" />
HTML Controls should be used for existing forms, using existing HTML elements; Forms and List Controls should be used for new web forms.

ASP.NET User Controls

User Controls can contain properties and encapsulate logic that can be exposed to the Web Forms that include them.
A User Control is added to a Web Form via a Register directive, containing three attributes: Example Register directive to add the Menu User Control:
	<%@ Register TagPrefix="IBuySpy"  TagName="Menu" Src="_Menu.ascx" %>
Once you have added the Register directive to your page, specify the User Control location by adding the User Control tag within the Web Forms UI, as follows:
  <IBuySpy:Menu id="Menu1"  runat="server" />

Assemblies (a.k.a.  Components)

Assembly/Components are now self-describing, eliminating the need for Registration.  Metadata assembly information is now sufficient to locate and use a component placed in the \bin directory.

Authentication

Integrated Windows authentication is only suitable within a LAN i.e. not over the Internet.  ASP.Net XML web services can authenticate via URL or file authorization.
A sub-directory web.config setting supercedes a parent-directory web.config.
Unlike the default placement, place <deny> elements before <allow> elements.
ASP.Net uses the DOMAIN\IWAM_server account.  It’s necessary to add IWAM_server account to the SQL Server Users list and specifically enable SELECT permission.  Public access is sufficient.
The following example of FormsAuthentication is implemented in the Login.aspx.  Specify a login page in the web.config (a non-compiled XML file, replacing the old global.asa file) authentication section; web.config is useful when you can’t access the IIS server at your ISP.  The path specifies the file for which to require authentication.

void LoginBtn_Click(Object sender, ImageClickEventArgs e) 
{
	// Only attempt a login if all form fields on the page are valid
	if (Page.IsValid == true) 
    {
		// Save old ShoppingCartID
		IBuySpy.ShoppingCartDB shoppingCart = new IBuySpy.ShoppingCartDB();
		String tempCartID = shoppingCart.GetShoppingCartId();

		// Attempt to Validate User Credentials using CustomersDB
		IBuySpy.CustomersDB accountSystem = new IBuySpy.CustomersDB();
		String customerId = accountSystem.Login(email.Text, password.Text);

	if (customerId != null) 
	{
		// Migrate any existing shopping cart items into the permanent shopping cart
		shoppingCart.MigrateCart(tempCartID, customerId);

		// Lookup the customer's full account details
		IBuySpy.CustomerDetails customerDetails = accountSystem.GetCustomerDetails(customerId);

		// Store the user's fullname in a cookie for personalization purposes
		Response.Cookies["IBuySpy_FullName"].Value = customerDetails.FullName;

		// Make the cookie persistent only if the user selects "persistent" login checkbox
		if (RememberLogin.Checked == true) 
        {
			Response.Cookies["IBuySpy_FullName"].Expires = DateTime.Now.AddMonths(1);
		}

		// Redirect browser back to originating page
		FormsAuthentication.RedirectFromLoginPage(customerId, RememberLogin.Checked);
	}
	else 
    {
		Message.Text = "Login Failed!";
	}
}
}
The RedirectFromLoginPage method issues an authentication ticket to the client and redirects the user back to the page they were attempting to access. When you log into the store, ASP.NET authentication creates a browser cookie that is set and read by the server.  Once logged in, if you browse to a page that requires authentication, the server looks for that cookie; if it finds it, you’re allowed to view that page. Depending on the session affinity settings, one might have to log in every time you hit a different web server.  To prevent this, modify the Store to share the cookie across all servers (or turn on session affinity).

By default the authentication cookies decryption key is set to auto-generate, resulting in a random decryption key, dependent to that server; consequently, the servers will not be able to use each others’ cookie.  This can be solved by manually setting the decryption key to be the same key on all servers in the web farm with the following element in the web.config:
  <machineKey decryptionKey="213454ABE333321D"  />
The decryption key was selected at random; it just needs to be the same in each servers web.config file.  The default protection level for the Forms based authentication setting was also changed from All to None.

Cross-browser support

Use CSS
CSS class name is case sensitive in Netscape Navigator 6
For inputs, Netscape Navigator requires the Size attribute

Caching for performance

Output Caching (pages in rendered HTTP format)
Fragment Caching (of page fragments)
Data Caching (via API access)
Cache time period is specified via Duration = “3,600” (e.g. 3,600 seconds).

Data-access components – implementation pattern

This implementation pattern can be seen in the GetProducts() method in the ProductsDB class:

public SqlDataReader GetProducts(int categoryID) 
{
    // Create Instance of Connection and Command Object
    SqlConnection myConnection = new SqlConnection(ConfigurationSettings.AppSettings["ConnectionString"]);
    SqlCommand myCommand = new SqlCommand("ProductsByCategory", myConnection);
    
    // Mark the Command as a SPROC
    myCommand.CommandType = CommandType.StoredProcedure;
    
    // Add Parameters to SPROC
    SqlParameter parameterCategoryID = new SqlParameter("@CategoryID", SqlDbType.Int, 4);
    parameterCategoryID.Value = categoryID;
    myCommand.Parameters.Add(parameterCategoryID);
    
    // Execute the command
    myConnection.Open();
    SqlDataReader result = myCommand.ExecuteReader(CommandBehavior.CloseConnection);
    
    // Return the datareader result
    return result;
}

Note the CloseConnection argument in the Command.ExecuteReader method. In general, stateless objects (which do not keep state in instance data-members) produce highly scalable solutions.  The client passes all data, required for a particular action, to a method and the method passes all resulting data back to the client. This approach simplifies resource management by freeing every data access object, following any method call.  The client can use any data access object to make a method call because all required inputs are passed to the object with the call.

To release database resources as soon as possible, classes (e.g. books, categories, customers, orders) encapsulate SQL stored procedure calls.  A class instance is a holder for a SqlCommand object, which makes stored procedure calls.  The DataAccess classes implement Finalize/Dispose semantics to close the active database connection.  The DataAccess objects are allocated and released at the point of a stored procedure call in the scope of either a using or try finally block.  For more information, see Optimizing Application Performance Using Efficient Data Retrieval.

Resultset terminology

DataReader
a forward-only, no-cursor resultset.
DataSet
a scrollable resultset.
DataReader
uses a forward-only, read-only "firehose" cursor; it's much lighter-weight and faster than a dataset.  It's very important to note that you must explicitly close the data connection, used by a DataReader, after DataBind() is called.  Middle-tier methods should return a SQLDataReaderResults class that includes not only a DataReader, but also a Close() method.  After databinding, the page closes the data reader and its connection using the result.Close() method.
A DataSet, a resident of the System.Data namespace, is most precisely defined as a provider-neutral, in-memory and disconnected relational data structure.

Translating between the provider-neutral DataSet and provider-specific Connection and Command objects is done by the DataAdapter.
The DataAdapter class is abstract; you can’t create it, directly; you’ll need to use one of the provider-specific derived child classes e.g. the System.Data.SqlClient.SqlDataAdapter class.  The following C# code assumes that the SqlConnectioncn has already been created:
  
  SqlCommand cmd = new  SqlCommand
  	("
    	SELECT * 
        FROM Customers;
     ", 
     	conn
     );

SqlDataAdapter sda = new SqlDataAdapter();
 sda.SelectCommand = cmd;
		DataSet ds = new DataSet();
sda.Fill(ds);
At this point, the connection, conn, no longer needs to be open and can be closed without compromising the data usefulness.  You could also have passed the SELECT statement into SqlDataAdapter’s constructor method. 

Each table in a DataSet is represented by a DataTable object, exposed through the DataSet’s Tables collection.  Each DataTable, in turn, contains a DataRows collection, which you can index, which can be used to retrieve row data.

To fill a DataSet with multiple DataTables, use multiple DataAdapters:
  SqlDataAdapter sda = new  SqlDataAdapter("SELECT * FROM Customers;",conn);
 SqlDataAdapter sda2 = new SqlDataAdapter(“SELECT * FROM Products;”,conn);
		  DataSet ds = new DataSet();
  sda.Fill(ds);
  sda2.Fill(ds);
You can override default (table) names by specifying a name when calling the Fill method:
  
  sda.Fill(ds,”Customers”);
  sda2.Fill(ds,”Products”);
Though using multiple DataAdapters works, it’s inefficient because each Fill method requires a trip to the database.  If your database Provider supports it, you can use a single adapter and a batch query to load the data:
  SqlDataAdapter sda = new  SqlDataAdapter
  (“
		SELECT  * 
		FROM Customers;
  ” +
  “
  		SELECT * 
        FROM Products;
   ”);
  sda.Fill(ds);
However, this approach results in the default table-naming.  To specify table names, define TableMapping Properties for the Adapter:
  sda.TableMappings.add(“Table”,”Customers”);
  sda.TableMappings.add(“Table1”,”Products”);

Session State

Session state is controlled within the Web.Config file.  Three modes are offered:
  1. InProc - in process i.e. in web server memory (best on a single server)
  2. StateServer – in a separate server in memory
  3. SQL Server – sessions stored in a SQL Server database

Errors

“Visual Studio .Net has detected that the web server is not running ASP.Net version 1.1.  You will be unable to run ASP.Net web applications  or services.”
ASPnet11
Apparently this error is thrown when the version of ASP.Net is not registered, even though it was installed.  The following ends the error:
  C:\WINDOWS\Microsoft.NET\Framework\v1.0.4322\aspnet_regiis.exe -i 
Note that the 4322 version may/should be replaced with the latest framework version, you have.  The –i switch installs the given version.

Back Home