SKIP THE SHIPPING
Use code NOSHIP during checkout to save 40% on eligible eBooks, now through January 5. Shop now.
Register your product to gain access to bonus material or receive a coupon.
This eBook includes the following formats, accessible from your Account page after purchase:
EPUB The open industry format known for its reflowable content and usability on supported mobile devices.
PDF The popular standard, used most often with the free Acrobat® Reader® software.
This eBook requires no passwords or activation to read. We customize your eBook by discreetly watermarking it with your name, making it uniquely yours.
In just 24 sessions of one hour or less, Sams Teach Yourself ASP.NET Core in 24 Hours, will help you build professional-quality, cloud-based, web-connected solutions with ASP.NET Core. This book’s straightforward, step-by-step approach guides you from the basics to advanced techniques, using practical examples to help you make the most of Microsoft’s radically revamped ASP.NET Core framework. ASP.NET Program Manager Jeffrey T. Fritz guides you from jumpstarting development with templates to implementing cutting-edge security and containerization. Every lesson builds on what you’ve already learned, giving you a rock-solid foundation for real-world success.
Step-by-step instructions carefully walk you through the most common ASP.NET Core tasks and techniques
Practical, hands-on examples show you how to apply what you learn
Notes and Tips point out shortcuts, solutions, and problems to avoid
Learn how to…
Introduction 1
HOUR 1: Introducing ASP.NET Core 7
What Is ASP.NET? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Why Should You Program in ASP.NET? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Significant Changes in ASP.NET Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Assumptions and Definitions in This Book . . . . . . . . . . . . . . . . . . . . . . . . . 13
Introducing the Sample Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
HOUR 2: Setting Up Your Work Environment for ASP.NET Core 17
Four Versions of Visual Studio: Where Do I Start? . . . . . . . . . . . . . . . . . . . . 17
Installing Development Tools on Non-Windows Operating Systems . . . . . . . 20
Introducing the dotnet Command-Line Tool . . . . . . . . . . . . . . . . . . . . . . . 24
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
HOUR 3: Exploring the New Project Templates 31
Getting Started with Visual Studio 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
The wwwroot Folder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Getting Started with Visual Studio Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
HOUR 4: Defining ASP.NET Core Configuration 45
Introducing Our Sample Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Getting Started with Server-Side Configuration . . . . . . . . . . . . . . . . . . . . . . 45
Fundamentals of .NET Application Structure . . . . . . . . . . . . . . . . . . . . . . . 46
Managing and Referencing NuGet Packages . . . . . . . . . . . . . . . . . . . . . . . 54
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
HOUR 5: Configuring the Service with the Startup Class 61
Introducing the Startup Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
HOUR 6: Configuring Your Application 75
Writing Configuration Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
HOUR 7: Accessing Your Data with Entity Framework Core 87
Getting Started with Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
HOUR 8: Introducing the MVC Architecture 105
Defining the MVC Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
HOUR 9: Building Your First Controller 113
Reviewing the MVC Folder Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
HOUR 10: Beginning MVC: Writing Your First View 125
Introducing Razor Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
HOUR 11: Scaffolding User Interfaces 135
Introducing Scaffolding in Visual Studio 2017 . . . . . . . . . . . . . . . . . . . . . 135
Investigating the Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
HOUR 12: Writing Data from a Controller 153
Enhancing Scaffolded Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
Validating Data in Controller Methods . . . . . . . . . . . . . . . . . . . . . . . . . . 154
Investigating Controller Interaction Strategies . . . . . . . . . . . . . . . . . . . . . 157
Preventing Unwanted Cross-site Posting . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Speeding Up Data Access with Asynchronous Techniques . . . . . . . . . . . . . 159
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
HOUR 13: Writing Web API Methods 165
In the Old Days of ASP.NET[…] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
Creating a New Trip with Web API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Updating a Trip with Web API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
Deleting a Trip with Web API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Negotiating and Formatting Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Trimming the Fat: An API-Only Application . . . . . . . . . . . . . . . . . . . . . . . 177
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
HOUR 14: Introducing Reusable User Interface Components 183
Introducing Tag Helpers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
Tag Helpers That Ship with ASP.NET Core . . . . . . . . . . . . . . . . . . . . . . . . 186
Building Other Reusable Components . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Writing Your Own Tag Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
Writing a Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
Counting the Number of Trips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Formatting Your Trips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Using Your New Tag Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
HOUR 15: npm and bower: Client-Side Package Managers 199
What Are npm and bower? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Getting Started with npm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Getting Started with bower . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Automating npm and bower . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
Installing Newer Versions of npm and bower . . . . . . . . . . . . . . . . . . . . . . . 208
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
HOUR 16: Introducing Angular 213
Getting Started with Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Exploring the Default Template Angular Website . . . . . . . . . . . . . . . . . . . 216
Delivering Content to the Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Adding Some Real Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
HOUR 17: Connecting Angular to ASP.NET Core 229
Connecting the Pieces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Connecting to a Running ASP.NET Core Service . . . . . . . . . . . . . . . . . . . . 235
Combining the Projects: Merging Angular and ASP.NET Core . . . . . . . . . . 240
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
HOUR 18: Routing Angular Requests Around ASP.NET Core 247
What Is Routing? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Generating URLs from ASP.NET Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Routing in Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Merging Projects: The Final Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
Debugging and Building Your App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
HOUR 19: Running Angular on the Server 265
Introducing Angular Universal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
Migrating Your Application to SpaServices . . . . . . . . . . . . . . . . . . . . . . . . 271
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
HOUR 20: Authenticating Your Users 275
Starting a New Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
Configuring Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
Adding the Registration Feature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Adding the Login and Logout Features. . . . . . . . . . . . . . . . . . . . . . . . . . . 285
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
HOUR 21: Granting Access to Users 291
Defining Roles, Claims, and Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Adding Claims to Your Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Adding a Sample Administrator User . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
Configuring Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Defining Security Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
Enforcing Authorization Policies in Content . . . . . . . . . . . . . . . . . . . . . . . 298
Creating More Complex Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
HOUR 22: Deploying to Production 303
What Is This “Production” You Speak Of? . . . . . . . . . . . . . . . . . . . . . . . . 303
Hosting ASP.NET Core Behind Another Server . . . . . . . . . . . . . . . . . . . . . . 304
Preparing Production Web Space . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
Deploying Your Application to a Windows Server . . . . . . . . . . . . . . . . . . . 305
Shared and Self-Contained Deployments . . . . . . . . . . . . . . . . . . . . . . . . . 308
Deploying Your Application to a Linux Server . . . . . . . . . . . . . . . . . . . . . 311
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
HOUR 23: Working with Docker Containers 317
What Is a Container? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Why Use Containers? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
Installing Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
Packaging Your Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Deploying to Azure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
HOUR 24: Looking to the Future and .NET Standard 331
What Is .NET Standard? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
Building and Using a .NET Standard Library . . . . . . . . . . . . . . . . . . . . . . 334
Future Updates for ASP.NET Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Staying Connected with the ASP.NET Core Community . . . . . . . . . . . . . . . 338
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
Q&A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
9780672337666, TOC, 11/27/2017