Home > Articles > Programming > ASP .NET

State Management in ASP.NET 2.0

With the reintroduction of cross-page posting and the introduction of Profile and the Wizard, View, and MultiView controls to the ASP.NET developer's toolbox, ASP.NET 2.0 should make the discussion of where to store client state in Web applications even more interesting. Fritz Onion and Keith Brown discuss these features and how to use them.
This chapter is from the book

This chapter is from the book

WHERE DO YOU STORE per-client state in a Web application? This question is at the root of many heated debates over how to best design Web applications. The disconnected nature of HTTP means that there is no "natural" way to keep state on behalf of individual clients, but that certainly hasn't stopped developers from finding ways of doing it. Today there are many choices for keeping client-specific state in an ASP.NET Web application, including Session state, View state, cookies, the HttpContext.Items collection, and any number of custom solutions. The best choice depends on many things, including the scope (Do you need the state to last for an entire user session or just between two pages?), the size (Are you worried about passing too much data in the response and would prefer to keep it on the server?), and the deployment environment (Is this application deployed on a Web farm so that server state must be somehow shared?), just to name a few.

ASP.NET 2.0 does not offer a penultimate solution for storing client state, but it does introduce three new features that should be considered any time you are looking for a place to store state on behalf of individual users. The first feature, cross-page posting, is actually the resurrection of a common technique used in classic ASP and other Web development environments for propagating state between two pages. This technique was not available in ASP.NET 1.1 because of the way POST requests were parsed and processed by individual pages, but has now been reincorporated into ASP.NET in such a way that it works in conjunction with server-side controls and other ASP.NET features. The second feature is a trio of new server-side controls that implement the common technique of showing and hiding portions of a page as the user interacts with it. The Wizard control gives developers a simple way to construct a multistep user interface on a single page, and the MultiView and View controls provide a slightly lower-level (and more flexible) way of hiding and displaying panes.

The last feature, Profile, is by far the most intriguing. Profile provides a prebuilt implementation that will store per-client state across requests and even sessions of your application in a persistent back-end data store. It ties into the Membership provider of ASP.NET 2.0 for identifying authenticated clients, and generates its own identifier for working with anonymous users as well, storing each client's data in a preconfigured database table. This feature provides a flexible and extensible way of storing client data and should prove quite useful in almost any ASP.NET application.

Cross-Page Posting

This version of ASP.NET reintroduces the ability to perform cross-page posts. Once a common practice in classic ASP applications, ASP.NET 1.x made it nearly impossible to use this technique for state propagation because of server-side forms and view state. This section covers the fundamentals of cross-page posting in general, and then looks at the support added in ASP.NET 2.0.

Fundamentals

One common mechanism for sending state from one page to another in Web applications is to use a form with input elements whose action attribute is set to the URL or the target page. The values of the source page's input elements are passed as name-value pairs to the target page in the body of the POST request (or in the query string if the form's method attribute is set to GET), at which point the target page has access to the values. Listings 4-1 and 4-2 show a pair of sample pages that request a user's name, age, and marital status, and display a customized message on the target page.

Listing 4-1. sourceform.aspx—sample form using a cross-page post

<!-- sourceform.aspx -->
<%@ Page language="C#" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Source Form</title>
</head>
<body>
    <form action="target.aspx" method="post">
        Enter your name:
        <input name="_nameTextBox" type="text" id="_nameTextBox" />
        <br />
        Enter your age:
        <input name="_ageTextBox" type="text" id="_ageTextBox" /><br />
        <input id="_marriedCheckBox" type="checkbox"
               name="_marriedCheckBox" />
        <label for="_marriedCheckBox">Married?</label><br />
        <input type="submit" name="_nextPageButton" value="Next page" />
    </form>
</body>
</html>

Listing 4-2. target.aspx—sample target page for a cross-page post

<!-- target.aspx -->
<%@ Page language="C#" %>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Target Page</title>
</head>
<body>
  <h3>
  Hello there
  <%= Request.Form["_nameTextBox"] %>, you are
  <%= Request.Form["_ageTextBox"] %> years old and are
  <%= (Request.Form["_marriedCheckBox"] == "on") ? "" : "not " %>
  married!
  </h3>
</body>
</html>

This example works fine in both ASP.NET 1.1 and 2.0, and with a few simple modifications would even work in classic ASP. This technique is rarely used in ASP.NET, however, because the form on the source page cannot be marked with runat="server"; thus, many of the advantages of ASP.NET, including server-side controls, cannot be used. ASP.NET builds much of its server-side control infrastructure on the assumption that pages with forms will generate POST requests back to the same page. In fact, if you try and change the action attribute of a form that is also marked with runat="server", it will have no effect, as ASP.NET will replace the attribute when it renders the page with the page's URL itself. As a result, most ASP.NET sites resort to alternative techniques for propagating state between pages (like Session state or using Server.Transfer while caching data in the Context.Items collection).

In the 2.0 release of ASP.NET, cross-page posting is now supported again, even if you are using server-side controls and all of the other ASP.NET features. The usage model is a bit different from the one shown in Listings 4-1 and 4-2, but in the end it achieves the desired goal of issuing a POST request from one page to another, and allowing the secondary page to harvest the contents from the POST body and process them as it desires. To initiate a cross-page post, you use the new PostBackUrl attribute defined by the IButtonControl interface, which is implemented by the Button, LinkButton, and ImageButton controls. When the PostBackUrl property is set to a different page, the OnClick handler of the button is set to call a JavaScript function that changes the default action of the form to the target page's URL. Listing 4-3 shows a sample form that uses cross-page posting to pass name, age, and marital status data entered by the user to a target page.

Listing 4-3. SourcePage1.aspx—using cross-page posting support in ASP.NET 2.0

<!-- SourcePage1.aspx -->
<%@ Page Language="C#" CodeFile="SourcePage1.aspx.cs"
         Inherits="SourcePage1" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Source page 1</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            Enter your name:
            <asp:TextBox ID="_nameTextBox" runat="server" /><br />
            Enter your age:
            <asp:TextBox ID="_ageTextBox" runat="server" /><br />
            <asp:CheckBox ID="_marriedCheckBox" runat="server"
                          Text="Married?" /><br />
            <asp:Button ID="_nextPageButton" runat="server"
                     Text="Next page" PostBackUrl="~/TargetPage.aspx" />
        </div>
    </form>
</body>
</html>

Once you have set up the source page to post to the target page, the next step is to build the target page to use the values passed by the source page. Because ASP.NET uses POST data to manage the state of its server-side controls, it would not have been sufficient to expect the target page to pull name/value pairs from the POST body, since many of those values (like __VIEWSTATE) need to be parsed by the server-side controls that wrote the values there in the first place. Therefore, ASP.NET will actually create a fresh instance of the source page class and ask it to parse the POST body on behalf of the target page. This page instance is then made available to the target page via the PreviousPage property, which is now defined in the Page class. Listings 4-4 and 4-5 show one example of how you could use this property in a target page to retrieve the values of the controls from the previous page: by calling FindControl on the Form control, you can retrieve individual controls whose state has been initialized with values from the post's body.

Listing 4-4. TargetPage.aspx—target page of a cross-page post

<!-- TargetPage.aspx -->
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="TargetPage.aspx.cs"
         Inherits="TargetPage" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Target Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Label runat="server" ID="_messageLabel" />
        </div>
    </form>
</body>
</html>

Listing 4-5. TargetPage.aspx.cs—target page of a cross-page post codebehind

// TargetPage.aspx.cs
public partial class TargetPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (PreviousPage != null)
        {
            TextBox nameTextBox =
                 (TextBox)PreviousPage.Form.FindControl("_nameTextBox");
            TextBox ageTextBox =
                 (TextBox)PreviousPage.Form.FindControl("_ageTextBox");
            CheckBox marriedCheckBox =
            (CheckBox)PreviousPage.Form.FindControl("_marriedCheckBox");

            _messageLabel.Text = string.Format(
    "<h3>Hello there {0}, you are {1} years old and {2} married!</h3>",
             nameTextBox.Text, ageTextBox.Text,
             marriedCheckBox.Checked ? "" : "not");
         }
     }
}

The technique shown in Listing 4-5 for retrieving values from the previous page is somewhat fragile, as it relies on the identifiers of controls on the previous page as well as their hierarchical placement, which could easily be changed. A better approach is to expose any data from the previous page to the target page by writing public property accessors in the codebehind, as shown in Listing 4-6.

Listing 4-6. SourcePage1.aspx.cs—exposing public properties to the target page

// File: SourcePage1.aspx.cs
public partial class SourcePage1 : Page
{

    public string Name
    {
      get { return _nameTextBox.Text; }
    }

    public int Age
    {
      get { return int.Parse(_ageTextBox.Text); }
    }
    public bool Married
    {
      get { return _marriedCheckBox.Checked; }
    }
}

Once the public properties are defined, the target page can cast the PreviousPage property to the specific type of the previous page and retrieve the values using the exposed properties, as shown in Listing 4-7.

Listing 4-7. TargetPage.aspx.cs—target page using properties to retrieve source page values

// TargetPage.aspx.cs
public partial class TargetPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        SourcePage1 sp = PreviousPage as SourcePage1;
        if (sp != null)
        {
            _messageLabel.Text = string.Format(
    "<h3>Hello there {0}, you are {1} years old and {2} married!</h3>",
            sp.Name, sp.Age, sp.Married ? "" : "not");
        }
    }
}

Because this last scenario is likely to be the most common use of cross-page posting—that is, a specific source page exposes properties to be consumed by a specific target page—there is a directive called PreviousPageType that will automatically cast the previous page to the correct type for you. When you specify a page in the VirtualPath property of this directive, the PreviousPage property that is generated for that page will be strongly typed to the previous page type, meaning that you no longer have to perform the cast yourself, as shown in Listings 4-8 and 4-9.

Listing 4-8. TargetPage.aspx with strongly typed previous page

<!-- TargetPage.aspx -->
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="TargetPage.aspx.cs"
         Inherits="TargetPage" %>
<%@ PreviousPageType VirtualPath="~/SourcePage1.aspx" %>
...

Listing 4-9. TargetPage.aspx.cs—using strongly typed PreviousPage accessor

// TargetPage.aspx.cs
public partial class TargetPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (PreviousPage != null)
        {
            _messageLabel.Text = string.Format(
    "<h3>Hello there {0}, you are {1} years old and {2} married!</h3>",
            PreviousPage.Name, PreviousPage.Age,
            PreviousPage.Married ? "" : "not");
        }
    }
}

Implementation

When you set the PostBackUrl property of a button to a different page, it does two things. First, it sets the client-side OnClick handler for that button to point to a JavaScript method called WebForm_DoPostBackWithOptions, which will programmatically set the form's action to the target page. Second, it causes the page to render an additional hidden field, __PREVIOUSPAGE, which contains the path of the source page in an encrypted string along with an accompanying message authentication code for validating the string. Setting the action dynamically like this enables you to have multiple buttons on a page that all potentially post to different pages and keeps the architecture flexible. Storing the path of the previous page in a hidden field means that no matter where you send the POST request, the target page will be able to determine where the request came from, and will know which class to instantiate to parse the body of the message.

Once the POST request is issued to the target page, the path of the previous page is read and decrypted from the __PREVIOUSPAGE hidden field and cached. As you have seen, the PreviousPage property on the target page gives access to the previous page and its data, but for efficiency, this property allocates the previous page class on demand. If you never actually access the PreviousPage property, it will never create the class and ask it to parse the body of the request.

The first time you do access the PreviousPage property in the target page, ASP.NET allocates a new instance of the previous page type, as determined by the cached path to the previous page extracted from the __PREVIOUSPAGE hidden field. Once it is created, it then executes the page much like it would if the request had been issued to it. The page is not executed in its entirety, however, since it only needs to restore the state from the POST body, so it runs through its life cycle up to and including the LoadComplete event. The Response and Trace objects of the previous page instance are also set to null during this execution since there should be no output associated with the process.

It is important to keep in mind that the preceding page will be created and asked to run through LoadComplete. If you have any code that generates side effects, you should make an effort to exclude that code from running when the page is executed during a cross-page postback. You can check to see whether you are being executed for real or for the purpose of evaluating the POST body of a cross-page post by checking the IsCrossPagePostBack property. For example, suppose that the source page wrote to a database in its Load event handler for logging purposes. You would not want this code to execute during a cross-page postback evaluation since the request was not really made to that page. Listing 4-10 shows how you might exclude your logging code from being evaluated during a cross-page postback.

Listing 4-10. Checking for IsCrossPagePostBack before running code with side effects

public partial class SourcePage1 : Page
{

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsCrossPagePostBack)
        {
            WriteDataToLogFile();
        }
    }
}

Caveats

While this new support for cross-page posting is a welcome addition to ASP.NET, it does have some potential drawbacks you should be aware of before you elect to use it. The first thing to keep in mind is that the entire contents of the source page is going to be posted to the target page. This includes the entire view state field and all input elements on the page. If you are using cross-page posting to send the value of a pair of TextBox controls to a target page, but you have a GridView with view state enabled on the source page, you're going to incur the cost of posting the entire contents of the GridView in addition to the TextBox controls just to send over a pair of strings. If you can't reduce the size of the request on the source page to an acceptable amount, you may want to consider using an alternative technique (like query strings) to propagate the values.

Validation is another potential trouble area with cross-page posting. If you are using validation controls in the client page to validate user input prior to the cross-page post, you should be aware that server-side validation will not take place until you access the PreviousPage property on the target page. Client-side validation will still happen as usual before the page issues the POST, but if you are relying on server-side validation at all, you must take care to check the IsValid property of the previous page before accessing the data exposed by the PreviousPage property.

A common scenario where this may occur is with custom validation controls. If you have set up a custom validation control with a server-side handler for the ServerValidate event, that method will not be called until you access the PreviousPage after the cross-page posting has occurred. Then there is the question of what to do if the previous page contains invalid data, since you can no longer just let the page render back to the client with error messages in place (because the client has already navigated away from the source page). The best option is probably just to place an indicator message that the data is invalid and provide a link back to the previous page to enter the data again. Listings 4-11 and 4-12 show a sample of a source page with a custom validation control and a button set up to use cross-page posting, along with a target page. Note that the code in the target page explicitly checks the validity of the previous page's data before using it and the error handling added if something is wrong.

Listing 4-11. Source page with custom validator

<!-- SourcePageWithValidation.aspx -->
<%@ Page Language="C#" %>

<script runat="server">
    public int Prime
    {
        get { return int.Parse(_primeNumberTextBox.Text); }
    }
    private bool IsPrime(int num)
    {
        // implementation omitted
    }
    protected void _primeValidator_ServerValidate(object source,
                           ServerValidateEventArgs args)
    {
        args.IsValid = IsPrime(Prime);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Source page with validation</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            Enter your favorite prime number:
            <asp:TextBox ID="_primeNumberTextBox" runat="server" />
            <asp:CustomValidator ID="_primeValidator" runat="server"
                 ErrorMessage="Please enter a prime number"
               OnServerValidate="_primeValidator_ServerValidate">
                                         **</asp:CustomValidator><br />
            <asp:Button ID="_nextPageButton" runat="server"
                        Text="Next page"
                        PostBackUrl="~/TargetPageWithValidation.aspx"
                         /><br />
            <br />
            <asp:ValidationSummary ID="_validationSummary"
                                   runat="server" />
        </div>
    </form>
</body>
</html>

Listing 4-12. Target page checking for validation

<!-- TargetPageWithValidation.aspx -->
<%@ Page Language="C#" %>
<%@ PreviousPageType VirtualPath="~/SourcePageWithValidation.aspx" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (PreviousPage != null && PreviousPage.IsValid)
        {
          _messageLabel.Text = "Thanks for choosing the prime number " +
                        PreviousPage.Prime.ToString();
        }
        else
        {
            _messageLabel.Text = "Error in entering data";
            _messageLabel.ForeColor = Color.Red;
            _previousPageLink.Visible = true;
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Target Page With validation</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Label runat="server" ID="_messageLabel" /><br />
            <asp:HyperLink runat="server" ID="_previousPageLink"
                           NavigateUrl="~/SourcePageWithValidation.aspx"
                           visible="false">
                  Return to data entry page...</asp:HyperLink>
        </div>
    </form>
</body>
</html>

Finally, it is important to be aware that the entire cross-page posting mechanism relies on JavaScript to work properly, so if the client either doesn't support or has disabled JavaScript, your source pages will simply post back to themselves as the action on the form will not be changed on the client in response to the button press.

Multi-Source Cross-Page Posting

Cross-page posting can also be used to create a single target page that can be posted to by multiple source pages. Such a scenario may be useful if you have a site that provides several different ways of collecting information from the user but one centralized page for processing it.

If we try and extend our earlier example by introducing a second source page, also with the ability to collect the name, age, and marital status of the client, we run into a problem because each page is a distinct type with its own VirtualPath, and the target page will somehow have to distinguish between a post from source page 1 and one from source page 2. One way to solve this problem is to implement a common interface in each source page's base class; this way, the target page assumes only that the posting page implements a particular interface and is not necessarily of one specific type or another. For example, we could write the IPersonInfo interface to model our cross-page POST data, as shown in Listing 4-13.

Listing 4-13. IPersonInfo interface definition

public interface IPersonInfo
{
  string Name { get; }
  int Age { get; }
  bool Married { get; }
}

In each of the source pages, we then implement the IPersonInfo on the codebehind base class, and our target page can now safely cast the PreviousPage to the IPersonInfo type and extract the data regardless of which page was the source page, as shown in Listing 4-14.

Listing 4-14. Generic target page using interface for previous page

IPersonInfo pi = PreviousPage as IPersonInfo;
if (pi != null)
{
  _messageLabel.Text = string.Format("<h3>Hello there {0}, you are {1}
years old and {2} married!</h3>",
                    pi.Name, pi.Age, pi.Married ? "" : "not");
}

It would be even better if we could use the PreviousPageType directive to strongly type the PreviousPage property to the IPersonInfo interface. In fact, there is a way to associate a type with a previous page instead of using the virtual path, which is to specify the TypeName attribute instead of the VirtualPath attribute in the PreviousPageType directive. Unfortunately, the TypeName attribute of the PreviousPageType directive requires that the specified type inherit from System.Web.UI.Page. You can introduce a workaround to get the strong typing by defining an abstract base class that implements the interface (or just defines abstract methods directly) and inherits from Page, as shown in Listing 4-15.

Listing 4-15. Abstract base class inheriting from Page for strong typing with PreviousPageType

public abstract class PersonInfoPage : Page, IPersonInfo
{
  public abstract string Name { get; }
  public abstract int Age { get; }
  public abstract bool Married { get; }
}

This technique then requires that each of the source pages you author change their base class from Page to this new PersonInfoPage base, and then implement the abstract properties to return the appropriate data. Listing 4-16 shows an example of a codebehind class for a source page using this new base class.

Listing 4-16. Codebehind class for a sample source page inheriting from PersonInfoPage

public partial class SourcePage1 : PersonInfoPage
{
  public override string Name
  {
    get { return _nameTextBox.Text; }
  }
  public override int Age
  {
    get { return int.Parse(_ageTextBox.Text); }
  }
  public override bool Married
  {
    get { return _marriedCheckBox.Checked; }
  }
}

Once all source pages are derived from our PersonInfoPage and the three abstract properties are implemented, our target page can be rewritten with a strongly typed PreviousPageType directive, which saves the trouble of casting, as shown in Listing 4-17.

Listing 4-17. Strongly typed target page using TypeName

<%@ PreviousPageType TypeName="PersonInfoPage" %>

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
  if (PreviousPage != null)
  {
    _messageLabel.Text = string.Format(
"<h3>Hello there {0}, you are {1} years old and {2} married!</h3>",
               PreviousPage.Name, PreviousPage.Age,
               PreviousPage.Married ? "" : "not");
  }
}
</script>
<!-- ... -->

The effort required to get the strong typing to work for multiple source pages hardly seems worth it in the end. You already have to check to see whether the PreviousPage property is null or not, and casting it to the interface using the as operator in C# is about the same amount of work as checking for null. However, both ways are valid approaches, and it is up to you to decide how much effort you want to put into making your previous pages strongly typed.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020