cyan yelloW MaGenTa Black panTone 123 c Books for professionals By professionals® The eXperT’s Voice® in sQl serVer Companion eBook Available Pro ASP.NET for SQL Server: High Performance Data Access for Web Developers Pro Pro Dear Reader, A As web developers, we work with ASP.NET and SQL Server by using a rich toolkit of databound controls and database functionality that promises great S performance and productivity gains after we know how to use it properly. ASP.NET Unfortunately, that toolkit is cluttered with a wide spectrum of features—some P ideal for the one-man shop building a tiny application, and others aimed at large for developer teams producing websites used by thousands of people. This book . carves out a path for the senior developer who needs to produce a professional N website that will perform well under high load. I focus on what a senior developer should know and use daily while also E explaining which features to avoid and why. There are several, little-known features in ASP.NET and SQL Server that work very well together without requiring much T SQL Server effort. These features get detailed coverage. Each chapter includes a full sample project, to help you learn by digging into some good code. These projects are f designed to keep you engaged while using them to understand the chapter topics o in greater detail. What you will learn from this book are techniques that allow you to leverage r the many performance and productivity improvements that have been provided S ASP.NET and SQL Server. These improvements will help you produce better websites with the desired results right from the start. This book is meant to be a Q guide as well as a recipe book for you to use as you incorporate the techniques High Performance Data Access I discuss into your projects. The book also focuses on producing websites that will be easier to maintain and to adjust to meet changing needs as traffic to L your websites increases. Having this book will be an asset to you in your daily for Web Developers work, as it guides you toward using ASP.NET and SQL Server more effectively. S Brennan Stehling e Companion eBook r Mastering the bridge between ASP.NET and SQL Server THE APRESS ROADMAP v Beginning ASP.NET 2.0 e E-Commerce in C# 2005 Pro ASP.NET 2.0 Pro ASP.NET See last page for details on $10 eBook version Website Programming for SQL Server r Beginning SQL Server 2005 for Developers S Brennan Stehling SOURCE CODE ONLINE ISBN-13: 978-1-59059-860-3 t e www.apress.com ISBN-10: 1-59059-860-1 h 55999 li n g US $59.99 Shelve in Microsoft/.NET User level: 9 781590 598603 Intermediate–Advanced this print for content only—size & color not accurate spine = 0.82" 432 page count 8601Ch00 8/28/07 4:33 PM Page i Pro ASP.NET for SQL Server High Performance Data Access for Web Developers Brennan Stehling 8601Ch00 8/28/07 4:33 PM Page ii Pro ASP.NET for SQL Server: High Performance Data Access for Web Developers Copyright © 2007 by Brennan Stehling All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. ISBN-13 (pbk): 978-1-59059-860-3 ISBN-10 (pbk): 1-59059-860-1 Printed and bound in the United States of America 9 8 7 6 5 4 3 2 1 Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. Lead Editors: Jonathan Gennick, Jim Huddleston Technical Reviewer: Vidya Vrat Agarwal Editorial Board: Steve Anglin, Ewan Buckingham, Gary Cornell, Jonathan Gennick, Jason Gilmore, Jonathan Hassell, Matthew Moodie, Jeffrey Pepper, Ben Renow-Clarke, Dominic Shakeshaft, Matt Wade, Tom Welsh Project Manager: Kylie Johnston Copy Editors: Sharon Wilkey, Ami Knox Assistant Production Director: Kari Brooks-Copony Production Editor: Ellie Fountain Compositor: Diana Van Winkle, Van Winkle Design Proofreader: April Eddy Indexer: Carol Burbo Cover Designer: Kurt Krames Manufacturing Director: Tom Debolski Distributed to the book trade worldwide by Springer-Verlag New York, Inc., 233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax 201-348-4505, e-mail [email protected], or visit http://www.springeronline.com. For information on translations, please contact Apress directly at 2855 Telegraph Avenue, Suite 600, Berkeley, CA 94705. Phone 510-549-5930, fax 510-549-5939, e-mail [email protected], or visit http://www.apress.com. The information in this book is distributed on an “as is” basis, without warranty. Although every precaution has been taken in the preparation of this work, neither the author(s) nor Apress shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in this work. The source code for this book is available to readers at http://www.apress.comin the Source Code/ Download section. 8601Ch00 8/28/07 4:33 PM Page iii Contents at a Glance About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi About the Technical Reviewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii ■ CHAPTER 1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 ■ CHAPTER 2 Data Model Choices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 ■ CHAPTER 3 Database Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 ■ CHAPTER 4 Databound Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 ■ CHAPTER 5 SQL Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 ■ CHAPTER 6 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 ■ CHAPTER 7 Manual Data Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 ■ CHAPTER 8 Generated Data Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 ■ CHAPTER 9 Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 ■ CHAPTER 10 A Sample Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289 ■ APPENDIX Photo Album . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 ■ INDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 iii 8601Ch00 8/28/07 4:33 PM Page iv 8601Ch00 8/28/07 4:33 PM Page v Contents About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi About the Technical Reviewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii ■ CHAPTER 1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Preparing Your Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Project Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Common Folders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Datasource Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Code and Database Separation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Managing Provider Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Using the Command Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Mixing and Matching Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Configuring Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Membership Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Roles Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Profile Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Creating Users and Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Securing the Admin Section . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Creating the Admin User . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 ■ CHAPTER 2 Data Model Choices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 The Data Access Application Block . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Data Access Code Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Sample Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 Trivial Data Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Nontrivial Data Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Typed DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Nontyped DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 DataReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 v 8601Ch00 8/28/07 4:33 PM Page vi vi ■ CONTENTS DataObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 What’s the Downside? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 ■ CHAPTER 3 Database Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Using Database Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Visual Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 SQL Server Management Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Managing Stored Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Managing Indexes and Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Performance Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Stability Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Unit Tests for Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Continuous Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 ■ CHAPTER 4 Databound Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 DetailsView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 FormView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 GridView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Editing and Validating Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Binding Input Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Binding Input Parameters with a Control . . . . . . . . . . . . . . . . . . . . . . 81 Binding Input Parameters Programmatically . . . . . . . . . . . . . . . . . . . 82 Binding a User Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Embedding Databound Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 ViewState and Databinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Session and ViewState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Paging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Disabling ViewState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 ControlState vs.ViewState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 Creating a Databound Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Getting the Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Getting the Total Rows Count . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Wiring the Pager Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Creating PersonRow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Persisting ViewState Manually . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Working Without ViewState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Walking the Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 8601Ch00 8/28/07 4:33 PM Page vii ■ CONTENTS vii ■ CHAPTER 5 SQL Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 The SqlMembershipProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 Using XML Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Setting the Database Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Creating a Password Policy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 The SqlRoleProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 Controlling Access by Role . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 Controlling Behavior by Role . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 The SqlProfileProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 Why Anonymous Profiles? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Configuring Anonymous Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Managing Anonymous Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Anonymous and Authenticated Profile Differences . . . . . . . . . . . . . 121 Migrating from Anonymous to Authenticated . . . . . . . . . . . . . . . . . 121 Creating a User . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 Dynamic Profiles and Profiles as BLOBs . . . . . . . . . . . . . . . . . . . . . . 124 Using the Provider-Powered ASP.NET Controls . . . . . . . . . . . . . . . . . . . . . 126 Building a SQL Photo Album Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Provider Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Configuration Section Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Provider Collection Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 Abstract Provider Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 The Provider Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 Provider Service Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 The Finished Product . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Building a SQL SiteMap Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 SiteMap Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Implementing SiteMapProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 ■ CHAPTER 6 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 Alternatives to Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Application State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 Session . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 ViewState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Current Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Caching Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Output Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Data Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 8601Ch00 8/28/07 4:33 PM Page viii viii ■ CONTENTS Invalidating Cached Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 Absolute Expiration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 Sliding Expiration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 Cache Dependency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 Manual Removal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 SQL Cache Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 Using the SqlDependency and SqlCacheDependency . . . . . . . . . . 165 Polling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Query Notifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Troubleshooting Query Notifications . . . . . . . . . . . . . . . . . . . . . . . . . 176 Problems with Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Performance Strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Data Warehousing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Lazy Loading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 ■ CHAPTER 7 Manual Data Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Using DataSets,Inline SQL,and Stored Procedures . . . . . . . . . . . . . . . . . 191 DataSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Inline SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Stored Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 Using DataObjects and the ObjectDataSource . . . . . . . . . . . . . . . . . . . . . 199 Design Contract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Data Contract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Testing the Design and Data Contracts . . . . . . . . . . . . . . . . . . . . . . . 203 Building the Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 Creating the Database Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 Consolidating the Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Managing Relationships . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Created and Modified . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 What About Nulls? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Using Database Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 The Data Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 Building the Website . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Connecting the Data Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Creating User Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 Creating the Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Description: