Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning ASP.NET 2.0 With CSharp (2006) [eng]

.pdf
Скачиваний:
75
Добавлен:
16.08.2013
Размер:
20.33 Mб
Скачать

Chapter 13

cmd.Parameters.Add(“@Name”, SqlDbType.VarChar, 50); cmd.Parameters.Add(“@Address”, SqlDbType.VarChar, 255); cmd.Parameters.Add(“@County”, SqlDbType.VarChar, 50); cmd.Parameters.Add(“@PostCode”, SqlDbType.VarChar, 15); cmd.Parameters.Add(“@Country”, SqlDbType.VarChar, 50); cmd.Parameters.Add(“@SubTotal”, SqlDbType.Money); cmd.Parameters.Add(“@Discount”, SqlDbType.Money); cmd.Parameters.Add(“@Total”, SqlDbType.Money);

cmd.Parameters[“@MemberName”].Value = User.Identity.Name; cmd.Parameters[“@OrderDate”].Value = DateTime.Now; cmd.Parameters[“@Name”].Value =

((TextBox)Wizard1.FindControl(“txtName”)).Text; cmd.Parameters[“@Address”].Value =

((TextBox)Wizard1.FindControl(“txtAddress”)).Text; cmd.Parameters[“@County”].Value =

((TextBox)Wizard1.FindControl(“txtCounty”)).Text; cmd.Parameters[“@PostCode”].Value = ((TextBox)Wizard1.FindControl(“txtPostCode”)).Text;

cmd.Parameters[“@Country”].Value = ((TextBox)Wizard1.FindControl(“txtCountry”)).Text;

cmd.Parameters[“@SubTotal”].Value = Profile.Cart.SubTotal; cmd.Parameters[“@Discount”].Value = Profile.Cart.MemberDiscount; cmd.Parameters[“@Total”].Value = Profile.Cart.Total;

int OrderID = Convert.ToInt32(cmd.ExecuteScalar());

After you’ve written the data into the Orders table, you need to create an order in which you write into the OrderLines table. This contains an order ID, the product ID, the quantity, and the price. After this, you commit the transaction:

// change the query and parameters for the order lines

cmd.CommandText = “INSERT INTO OrderLines(OrderID, ProductID, Quantity, Price) “ +

“VALUES (@OrderID, @ProductID, @Quantity, @Price)”; cmd.Parameters.Clear(); cmd.Parameters.Add(“@OrderID”, SqlDbType.Int); cmd.Parameters.Add(“@ProductID”, SqlDbType.Int); cmd.Parameters.Add(“@Quantity”, SqlDbType.Int); cmd.Parameters.Add(“@Price”, SqlDbType.Money);

cmd.Parameters[“@OrderID”].Value = OrderID; foreach (CartItem item in Profile.Cart.Items)

{

cmd.Parameters[“@ProductID”].Value = item.ProductID; cmd.Parameters[“@Quantity”].Value = item.Quantity; cmd.Parameters[“@Price”].Value = item.Price;

cmd.ExecuteNonQuery();

}

// commit the transaction

518

E-Commerce

trans.Commit();

}

The next part is the exception handler. If there is any kind of database error, you have to roll back the exception and write it to the error log. Exception handling is explained in more detail in Chapter 15. This code is specifically tailored to handle SQL errors and will cause an error in the application:

catch (SqlException SqlEx)

{

//some form of error - rollback the transaction

//and rethrow the exception

if (trans != null) trans.Rollback();

CreateOrderErrorLabel.Visible = true;

//Log the exception

//Tools.log(“An error occurred while creating the order”, SqlEx)

throw new Exception(“An error occurred while creating the order”, SqlEx);

}

Last, you close the connection and you clear the cart profile of the items if the transaction has been successful:

finally

{

if (conn != null) conn.Close();

}

//we will only reach here if the order has been created sucessfully

//so clear the cart

Profile.Cart.Items.Clear();

There is also a failsafe step that checks to see if you have jumped in the Wizard. Normally this will be under the direction of the program — in other words, you check to see if the user has logged in and jump them forward one step. However, it’s possible that an unscrupulous user might have jumped into this procedure halfway through, or that the procedure has accidentally “forgotten” the login details (normally caused by the session variable being lost — this might happen if ASP.NET restarts halfway through the Wizard). In this case, you check to see if the user has logged in, and if you have no record of them (in other words, they aren’t authenticated), then you jump them back to the login dialog:

protected void Wizard1_ActiveStepChanged( object sender, System.EventArgs e)

{

if (!User.Identity.IsAuthenticated) Wizard1.ActiveStepIndex = 0;

}

}

The checkout process is a lengthy one, but it is the most essential part. If you get this wrong, you will never get any orders!

519

Chapter 13

Secure Transactions

You might be forgiven for thinking that you’re missing one vital part of the process. How do you ensure that your transaction isn’t compromised and that credit card details aren’t left wide open to the ether? Of course the nature of the HTTP protocol is exactly that, you send text across to the web server and you receive text back again. There’s nothing to stop anybody out there from listening and recording your details.

Fortunately, there is a two-pronged attack with which you can ensure transactions are secure and that the credit card details and other confidential information are not compromised:

Encryption: You must encode, or scramble, the information that is sent to the web server and received back from the web server. The web server has a public key, and users will have a private key that enables them to decode the information. Only having the public key and the private key together will allow you to encrypt the message. The web server will have a public key and its own private key at the other end. To encrypt messages, you use a secure communications protocol. Either Secure Sockets Layer (SSL) or Secure HTTP (S-HTTP) would provide this functionality. You can specify encryption methods and whether to use SSL on a connection in the Web.config file.

Certificates: To guarantee that the site you are dealing with at the other end is reputable, it can be certified by a Certificate Authority. Verisign (www.verisign.com) is perhaps the most common Certificate Authority. The authority is paid a yearly fee by the e-commerce vendor and in return, the authority performs checks on the business to prove that it is legitimate. These checks are then recorded in the form of a certificate. You can browse particular sites’ certificates during the checkout process. To make your site trustworthy, you should go about obtaining a certificate from a Certificate Authority.

You’re not going to implement any of these features on the Wrox United site for reasons of practicality, but if you want to implement an e-commerce solution, you must make use of encryption and certificates.

What Else Can You Do?

Having gone this far in the chapter, you probably deserve a cup of tea and a sit down. However, while you’re enjoying your well-earned brew, this would be a good time to get your thinking cap on and have a think about what else you could add to the shop. An e-commerce site is like a community and can continually evolve as your site evolves — you shouldn’t think that you’ve done everything possible with it. The following list outlines some things to consider as your e-commerce site evolves:

Improving the product catalog: You can show how many products are currently in stock, how long they will take to deliver, and the release date of a product. You can add customer reviews or testimonies to how wonderful or appalling the product is, and add a cross-linking reference that mentions which other items a customer bought when they purchased a particular item.

Improving membership tracking and personalization: Add a member discount, record credit card details, and mail out special offers related to past purchases (so if a customer bought a replica kit in 2004, when the 2005 version comes out, it might be good to e-mail them).

520

E-Commerce

Improving the shopping cart: Make the shopping cart visible at all times.

Improving the checkout process: Make the checkout process simpler so that it can be achieved in as few clicks as possible.

Summar y

Hopefully this rather intense chapter hasn’t scared you away. E-commerce is a lengthy and complex process — however, the new features of ASP.NET 2.0 make it approachable and possible to program for the first time without weeks of heartache and stress. Although e-commerce isn’t something to be taken lightly, it is something that can be added to an application with a little bit of thought and careful work.

This chapter started by describing the e-commerce pipeline, which is outlined as follows:

Select an item from the catalog

Put the item in the shopping cart

Check out with the item or items

Supply address details

Pay for the item

Confirm the transaction

You started by creating a design for your product catalog and then you built a Catalog page. From the catalog you allowed the user to hone in on particular items, and you did this via a Product Item page. Neither of these items specifically required the use of the shopping cart, so you held off creating one.

They just queried the database and displayed the relevant details. However, without these pages, you would not be able to shop effectively.

With a catalog working, you could add the cart. The cart consisted of two objects: the CartItem object (one for each item selected by the user from the catalog and the ShoppingCart object, (which contained a bundle of CartItem objects). To enable the shopping cart, you added Insert, Update, and Delete methods, which allowed you to put things into, change the amount of, and remove items from your shopping cart. Last, you connected the shopping cart to your catalog by creating an Add an Item button to your Product Item page.

Next you created a checkout process that handled the login, the confirmation of the delivery address, and the credit card details, and finished the procedure. Although you couldn’t handle the card details with the application, you learned about the various options offered. Finally you learned how to make the transactions secure and some ways to extend and improve the e-commerce procedure in Wrox United.

Exercises

An e-commerce site could potentially offer many extra features. In these exercises, you’re going to focus on just one. Some fan sites offer the capability for their members to purchase items at a reduced price, a membership discount. How would you go about implementing it? Each question is about a stage of the implementation and together they will give you this functionality.

521

Chapter 13

1.The member discount is something that is applied to the shopping cart as you add items to the cart. What do you need to add to the ShoppingCart object to make sure it can store a discount of 10% for fan club members? You can assume that you can detect a fan club member with the property HttpContext.Current.User.IsInRole(“FanClubMember”).

Hint: You will need to create a subtotal as well.

2.How can you display the member discount details on the Shopping Cart page so that only a fan club member will see them?

522

14

Performance

Throughout the book you’ve learned a range of techniques to help you build web sites, and really concentrated on the possibilities regarding what controls and code you can use to produce great functionality for your site. One thing you haven’t looked at, though, is how to make your site perform as well as it possibly can. After all, it doesn’t matter how great your site looks — if it performs badly, it fails. Internet users are an impatient lot and expect sites to be fast.

Although performance should be addressed throughout the design and building of a site, this isn’t always practical, especially for the beginner. So this chapter revisits some of the earlier pages to see how they can be improved, and discusses the techniques that can be used to create the best performing sites.

In particular, this chapter looks at the following:

How to design and test for performance

The techniques to use in ASP.NET pages and data handling to ensure the fastest possible pages

What caching is and how it can be used

Let’s start with the things you can do to either existing code or new code that you write.

Simple Techniques

Several simple things are easy to do and provide good performance, as well as being good design techniques and aiding future development work and maintenance. After all, writing applications isn’t just about getting the best from them now, but also getting the best from them in the future.

Chapter 14

Being able to fix and update web applications easily is just as much a part of development as producing the application in the first place. This section on simple techniques looks at the following:

How to dispose of resources after they are no longer required

How to ensure connecting to a database is done in the best possible way

How using stored procedures can improve data access performance

How to use generics to improve performance of collections

How session state can be minimized to allow less processing to be done by ASP.NET

How view state can be tuned to reduce the amount of data sent to and from the web server

This section starts with object disposal.

Object Disposal

In performance terms, certain things are expensive; that is, they can lead to performance problems. The reason for this is that objects need resources to manage them, resources such as CPU and memory. The fewer of these resources used, the less work the server is doing, which in turn leads to more pages for more users. If the use of these resources can be minimized, the site will perform better, and part of that minimization is to make sure you only use the resource for as little time as possible.

In general, objects that use expensive resources like the file system, graphics, or databases should be disposed of as soon as they are no longer needed. The only exception is database connections in ASP.NET applications, as discussed in the “Database Connections” section later in the chapter. Disposal of objects frees up resources, such as files and memory, allowing the web server to perform more efficiently. By default, resources are disposed of automatically by the Garbage Collector, but it is possible to improve performance by taking control of object disposal yourself, and you can do this in two ways. You can either use a standard pattern for creating the resource, using it, and then disposing of it, or you can use the using statement. This section looks at both methods, because you’ll see both in documentation.

In the Wrox United site, one area where this is used is for images. Certain users have permission to upload images. Administrators can upload new images for the shop, the owner and coach can upload player pictures, and reporters and fan club members can upload match pictures. Part of this upload process involves creating a thumbnail image, which uses the Image object, something that should be disposed of as soon as it’s no longer required. Disposal is necessary for two reasons. The first is because the image is a file-based resource, and the file may be required by other pages, so making sure you don’t have any connection to it means it’s available for others — the sooner you remove it, the sooner someone else can access it. The second reason for disposal is because images take memory, so disposing of the image means the memory is freed and available for other processes.

The routine for creating thumbnails is in the ImageHandling.cs file, in the App_Code directory, and is a simple class with a single shared method (actually, there are two methods, but one is only required as part of the image handling and isn’t actually used).

The general structure of this code is to create a new Image object from an existing image stored on disc. Then a new Image object is created using the GetThumbnailImage method, which specifies the new width and height. It’s pretty simple, but it involves two Image objects, so it requires two lots of disposal. The next section looks at how this routine works using the two ways of resource disposal.

524

Performance

Disposal with try/catch

To dispose using the try/catch methods you follow this simple pattern:

try

{

// create resource

}

catch()

{

// handle exception

}

finally

{

// dispose of resource

}

Your image code using this pattern is as follows:

public static void GenerateThumbnail(string SourceImagePath, string TargetImagePath)

{

short newHeight; short newWidth;

Image sourceImage = null; Image targetImage = null;

try

{

sourceImage = Image.FromFile(SourceImagePath);

newHeight = short(sourceImage.Height * 0.25); newWidth = short(sourceImage.Width * 0.25);

Image.GetThumbnailImageAbort abort1 = new Image.GetThumbnailImageAbort(ImageHandling.ThumbnailCallback);

try

{

targetImage = sourceImage.GetThumbnailImage(newWidth, newHeight, cb, IntPtr.Zero);

targetImage.Save(TargetImagePath, Imaging.ImageFormat.Gif);

}

catch (Exception ex)

{

// log exception

}

finally

{

if (targetImage != null) targetImage.Dispose();

}

}

catch (Exception ex)

{

525

Chapter 14

// log exception

}

finally

{

if (sourceImage != null) sourceImage.Dispose();

}

}

You can immediately see this is a little hard to read. There are two try/catch blocks, one within the other. The outer one is for sourceImage — the original image. This is loaded from a file using Image. FromFile, and then the new width and height are calculated using the Height and Width properties of the source image — the new height and width are 25% of the original. After the new size is defined, a callback variable (cb) is created in case there is an error during the creation of the thumbnail — the GetThumbnailImage method will call the callback if an error occurs. We’re not actually handling any errors because we decided that it isn’t critical if thumbnails aren’t generated. If you have an application where it is critical to know about these errors, you could log the error in the callback routine.

The inner try/catch block then surrounds targetImage, which is generated from sourceImage using GetThumbnailImage with the new width and height. This creates a new image based on the new size. After it is generated, targetImage is then saved to a file as a GIF image.

The finally blocks of each try/catch check that the Image object exists before disposing of the object, by calling the Dispose method.

Disposal with Using

The using statement makes the preceding code much simpler, as shown here:

public static void GenerateThumbnail(string SourceImagePath, string TargetImagePath)

{

using (Image sourceImage = Image.FromFile(SourceImagePath))

{

short newHeight = (short) Math.Round((double)(sourceImage.Height * 0.25)); short newWidth = (short) Math.Round((double)(sourceImage.Width * 0.25)); Image.GetThumbnailImageAbort abort1 = new

Image.GetThumbnailImageAbort(ImageHandling.ThumbnailCallback);

using (Image targetImage = sourceiMage.GetThumbnailImage(newWidth, newHeight, abort1, IntPtr.Zero))

{

targetImage.Save(TargetImagePath, ImageFormat.Gif);

}

}

}

You can immediately see how much easier this is to read, as well as how much more sense it makes. The using statement created the resource, which is then automatically disposed of when the trailing brace (}) of the code block is reached. The syntax for the using statement is as follows:

using (resource)

{

// code that uses the resource

}

526

Performance

What happens is that when the trailing brace (}) is reached, the resource is disposed of immediately — there’s no waiting for the Garbage Collector to dispose of it. In the code you have the following:

using (Image sourceImage = Image.FromFile(SourceImagePath))

This is similar to declaring variables, in that it declares a variable, sourceImage, and assigns it a value. Unlike variable declaration though, the variable is disposed of as soon as the trailing brace is reached. So as soon as the trailing brace is done, the sourceImage variable is gone. In fact, because its scope is defined as part of the using statement, sourceImage isn’t accessible outside of the using code block.

You only need to explicitly dispose of resources if they are deemed expensive resources, such as files (images, text files, and so forth) or graphics resources that use lots of memory. Normal variables and objects, even those you might think take a lot of memory such as a DataSet, should not be disposed of explicitly.

Database Connections

Fetching data from a database can be done automatically by way of the data source controls, or manually though the objects in the System.Data namespaces. One of these objects applies to connecting to a database — for SQL Server or SQL Server Express, that object is the SqlConnection. In general, databases are limited to the number of connections they can have. Each connection takes resources and may stop another application from connecting, so these should be used as sparingly as possible, and

only kept open for as short a time as possible. The general rule is to open the connection as late as possible, fetch the data, and close the connection as soon as possible, disposing of the connection once done. The Using statement is excellent for this:

using (SqlConnection conn = new SqlConnection(“. . .”))

{

// code that uses the connection

}

Here the using statement keeps track of the connection object, conn, which is closed and disposed of when the trailing brace is reached.

In general, if you are manually creating connections, you should dispose of them as soon as you are finished. If you are using the data source controls, object disposal is handled automatically for you.

Stored Procedures

In the examples you’ve seen so far in this book, the data has been fetched either using a data source control such as the SqlDataSource or using code, where a SqlCommand or SqlDataAdapter were used. In all cases, the command that fetched the data, the SQL, was entered directly, such as:

SELECT [ProductID], [Name], [Description], [Price], [PictureURL] FROM [Products]

Now there’s nothing intrinsically wrong with this — it’s a standard SQL statement that works fine. However, it’s not the fastest way to fetch data, because the database has to work out exactly how it is going to fetch the data when the command is executed. This involves creating what’s known as an execution plan — a plan of how the SQL is going to be executed, how tables are to be joined if there are multiple

527