MCPD 70-518 Exam Ref: Designing and Developing Windows Applications Using Microsoft .NET Framework 4 ®
Tony Northrup Matthew A. Stoecker
®
Published with the authorization of Microsoft Corporation by: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, California 95472 Copyright © 2011 by Tony Northrup and Matthew Stoecker All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means without the written permission of the publisher. ISBN: 978-0-7356-5723-6 1 2 3 4 5 6 7 8 9 QG 6 5 4 3 2 1 Printed and bound in the United States of America. Microsoft Press books are available through booksellers and distributors worldwide. If you need support related to this book, email Microsoft Press Book Support at
[email protected]. Please tell us what you think of this book at http://www.microsoft.com/learning/booksurvey. Microsoft and the trademarks listed at http://www.microsoft.com/about/legal/ en/us/IntellectualProperty/Trademarks/EN-US.aspx are trademarks of the Microsoft group of companies. All other marks are property of their respective owners. The example companies, organizations, products, domain names, email addresses, logos, people, places, and events depicted herein are fictitious. No association with any real company, organization, product, domain name, email address, logo, person, place, or event is intended or should be inferred. This book expresses the author’s views and opinions. The information contained in this book is provided without any express, statutory, or implied warranties. Neither the authors, O’Reilly Media, Inc., Microsoft Corporation, nor its resellers, or distributors will be held liable for any damages caused or alleged to be caused either directly or indirectly by this book. Acquisitions and Developmental Editor: Ken Jones Production Editor: Holly Bauer Editorial Production: S4Carlisle Publishing Services Technical Reviewer: Bill Chapman Copyeditor: Susan McClung Indexer: Potomac Indexing, LLC Cover Composition: Karen Montgomery Illustrator: S4Carlisle Publishing Services
Contents at a Glance Introduction xv Preparing for the Exam
xviii
Chapter 1
Designing the Layers of a Solution
1
Chapter 2
Designing the Presentation Layer
89
Chapter 3
Designing the Data Access Layer
173
Chapter 4
Planning a Solution Deployment
225
Chapter 5
Designing for Stability and Maintenance
265
Index 303
Contents Introduction xv Microsoft Certified Professional Program
xv
Acknowledgments xvi Support and Feedback
xvi
Preparing for the Exam
xviii
Chapter 1 Designing the Layers of a Solution
1
Objective 1.1: Design a Loosely Coupled Layered Architecture. . . . . . . . . . 2 Designing Service-Oriented Architectures
2
Providing Separation of Concern
4
Designing a System Topology
4
Choosing Between Presentation and Business Logic
6
Using WCF Routing
8
Understanding BizTalk Server
10
Objective Summary
11
Objective Review
11
Objective 1.2: Design Service Interaction. . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Designing Service and Method Granularity
14
Choosing Protocols and Binding Types
16
Using REST
18
Using Message and Data Contracts
19
Using Custom SOAP Headers
22
Managing Data Integrity
24
Choosing Synchronous vs. Asynchronous
24
What do you think of this book? We want to hear from you! Microsoft is interested in hearing your feedback so we can continually improve our books and learning resources for you. To participate in a brief online survey, please visit:
www.microsoft.com/learning/booksurvey/ v
Choosing a Message Exchange Pattern
25
Versioning 25 Hosting WCF Services
27
Objective Summary
28
Objective Review
28
Objective 1.3: Design the Security Implementation. . . . . . . . . . . . . . . . . . . 30 Planning for User Account Control
31
Designing for Least Privilege
31
Understanding Process Identity
35
Understanding Impersonation and Delegation
36
Implementing Authorization
41
Planning Role Management
44
Using Cryptography
45
Objective Summary
49
Objective Review
50
Objective 1.4: Design for Interoperability with External Systems. . . . . . . . 52 Accessing Assemblies from Unmanaged Code
52
Accessing COM Objects
53
Objective Summary
54
Objective Review
54
Objective 1.5: Design for Optimal Processing. . . . . . . . . . . . . . . . . . . . . . . . 56 Planning for Long-Running Processes
56
Scaling Applications
60
Moving to the Cloud
63
Using Queuing
63
Minimizing Latency
64
Using a Service Bus
65
Objective Summary
66
Objective Review
66
Objective 1.6: Design for Globalization and Localization. . . . . . . . . . . . . . 69
vi
Contents
Choosing Between CurrentCulture and CurrentUICulture
70
Format Text for Differing Cultures
71
Translating Applications
72
Working with Time
72
Comparing Data
73
Designing Databases for Globalization
74
Objective Summary
75
Objective Review
75
Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Answers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 Objective 1.1: Review
80
Objective 1.1: Thought Experiment
81
Objective 1.2: Review
81
Objective 1.2: Thought Experiment
82
Objective 1.3: Review
82
Objective 1.3: Thought Experiment
83
Objective 1.4: Review
84
Objective 1.4: Thought Experiment
84
Objective 1.5: Review
85
Objective 1.5: Thought Experiment
86
Objective 1.6: Review
86
Objective 1.6: Thought Experiment
87
Chapter 2 Designing the Presentation Layer
89
Objective 2.1: Choose the Appropriate Windows Technology. . . . . . . . . . 90 Windows Forms
90
WPF 90 Choosing Between Windows Forms and WPF
92
Interoperating Between Windows Forms and WPF
92
Choosing a Presentation Pattern
97
Objective Summary
99
Objective Review
99
Objective 2.2: Design the UI Layout and Structure. . . . . . . . . . . . . . . . . . . 100 Evaluate the Conceptual Design
100
Designing for Inheritance and the Reuse of Visual Elements
101
Creating a Resource Dictionary
108
Designing for Accessibility
109
Deciding When Custom Controls Are Needed
111 Contents
vii
Objective Summary
112
Objective Review
112
Objective 2.3: Design Application Workflow. . . . . . . . . . . . . . . . . . . . . . . . 113 Implementing User Navigation
114
Navigation Applications in WPF
117
Using PageFunction Objects
124
Simple Navigation and Structured Navigation
125
Designing for Different Input Types
126
Objective Summary
127
Objective Review
127
Objective 2.4: Design Data Presentation and Input. . . . . . . . . . . . . . . . . . 129 Designing Data Validation
129
Design a Data Binding Strategy
134
Managing Data Shared Between Forms
139
Managing Media
140
Objective Summary
140
Objective Review
141
Objective 2.5: Design Presentation Behavior. . . . . . . . . . . . . . . . . . . . . . . . 143 Determine Which Behaviors Will Be Implemented and How
143
Creating Attached Behaviors
147
Implementing Drag-and-Drop Functionality
148
Objective Summary
154
Objective Review
154
Objective 2.6: Design for UI Responsiveness. . . . . . . . . . . . . . . . . . . . . . . . 155 Offloading Operations from the UI Thread and Reporting Progress
156
Using Dispatcher to Access Controls Safely on Another Thread in WPF
161
Avoiding Unnecessary Screen Refreshes
162
Determining Whether to Sort and Filter Data on the Client or Server
163
Addressing UI Memory Issues
164
Objective Summary
165
Objective Review
165
Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 viii
Contents
Answers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Objective 2.1: Review
168
Objective 2.1: Thought Experiment
168
Objective 2.2: Review
168
Objective 2.2: Thought Experiment
169
Objective 2.3: Review
169
Objective 2.3: Thought Experiment
170
Objective 2.4: Review
170
Objective 2.4: Thought Experiment
171
Objective 2.5: Review
171
Objective 2.5: Thought Experiment
171
Objective 2.6: Review
172
Objective 2.6: Thought Experiment
172
Chapter 3 Designing the Data Access Layer
173
Objective 3.1: Choose the Appropriate Data Access Strategy . . . . . . . . . 174 Understanding .NET Data Access Technologies
174
Supporting Different Data Sources
177
Choosing a Data Access Strategy
178
Objective Summary
179
Objective Review
179
Objective 3.2: Design the Data Object Model. . . . . . . . . . . . . . . . . . . . . . . 181 Mapping to Persistent Storage
182
Designing a Schema Change Management Strategy
184
Abstracting from the Service Layer
185
Objective Summary
187
Objective Review
187
Objective 3.3: Design Data Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Understanding Caching
189
Using MemoryCache
190
Caching Web Services
190
Objective Summary
191
Objective Review
192
Contents
ix
Objective 3.4: Design Offline Storage and Data Synchronization. . . . . . 194 Determining the Need for Offline Data Storage
194
Using Sync Framework
195
Designing Synchronization
198
Objective Summary
201
Objective Review
201
Objective 3.5: Design for a Concurrent Multiuser Environment. . . . . . . . 203 Planning for Multiuser Conflicts
203
Understanding Deadlock Conflicts
205
Designing Concurrency for Web Services
206
Using Cross-Tier Distributed Transactions
207
Objective Summary
208
Objective Review
208
Objective 3.6: Analyze Data Services for Optimization. . . . . . . . . . . . . . . 210 Understanding ORM Performance
211
Understanding Lazy and Eager Loading
211
Optimizing Round-Trips
213
Objective Summary
214
Objective Review
214
Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 Answers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
x
Contents
Objective 3.1: Review
217
Objective 3.1: Thought Experiment
218
Objective 3.2: Review
218
Objective 3.2: Thought Experiment
219
Objective 3.3: Review
219
Objective 3.3: Thought Experiment
220
Objective 3.4: Review
220
Objective 3.4: Thought Experiment
221
Objective 3.5: Review
221
Objective 3.5: Thought Experiment
222
Objective 3.6: Review
222
Objective 3.6: Thought Experiment
223
Chapter 4 Planning a Solution Deployment
225
Objective 4.1: Define a Client Deployment Strategy . . . . . . . . . . . . . . . . . 226 Understanding Installation Methods
226
Choosing an Installation Method
231
Deploying the .NET Framework
232
Deploying COM Objects
234
Objective Summary
235
Objective Review
235
Objective 4.2: Plan a Database Deployment. . . . . . . . . . . . . . . . . . . . . . . . 237 Understanding Database Deployment Files
237
Using SQL Scripts
237
Using the Vsdbcmd.exe Tool
238
Using Data-Tier Projects
239
Using SQL Server Database Projects
239
Publishing Databases from Server Explorer
240
Publishing Databases with a WCF Web Service
241
Understanding Deployment Conflicts
242
Deploying an Embedded Database Privately
242
Objective Summary
244
Objective Review
244
Objective 4.3: Design a Solution Update Strategy . . . . . . . . . . . . . . . . . . . 246 Updating ClickOnce Applications
247
Updating with Windows Installer
248
Packaging Shared Components
248
Checking for Windows Installer Updates
249
Updating Shared Components
249
Designing Web Services for Updates
249
Objective Summary
251
Objective Review
251
Objective 4.4: Plan for N-Tier Deployment . . . . . . . . . . . . . . . . . . . . . . . . . 253 Designing a Physical Topology
254
Determining Component Installation Order
256
Objective Summary
256
Objective Review
257 Contents
xi
Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Answers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 Objective 4.1: Review
260
Objective 4.1: Thought Experiment
260
Objective 4.2: Review
261
Objective 4.2: Thought Experiment
262
Objective 4.3: Review
262
Objective 4.3: Thought Experiment
263
Objective 4.4: Review
263
Objective 4.4: Thought Experiment
264
Chapter 5 Designing for Stability and Maintenance
265
Objective 5.1: Design for Error Handling. . . . . . . . . . . . . . . . . . . . . . . . . . . 265 Designing an Exception Handling Strategy
266
Handling Exceptions Across Tiers
267
Collecting User Feedback
270
Creating Custom Exception Classes
272
Processing Unhandled Exceptions
272
Objective Summary
273
Objective Review
274
Objective 5.2: Evaluate and Recommend a Test Strategy. . . . . . . . . . . . . 275
xii
Contents
Understanding Black Box and White Box Testing
276
Understanding Functional Tests
277
Understanding UI Tests
279
Understanding Performance Tests
281
Understanding Code Coverage
282
Objective Summary
283
Objective Review
283
Objective 5.3: Design a Diagnostics and Monitoring Strategy. . . . . . . . . 285 Providing Monitoring Information
285
Providing Usage Reporting
291
Choosing Distributed or Centralized Logging
292
Designing a Diagnostics and Monitoring Strategy
293
Profiling .NET Applications
294
Objective Summary
295
Objective Review
295
Chapter Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 Answers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 Objective 5.1: Review
299
Objective 5.1: Thought Experiment
299
Objective 5.2: Review
300
Objective 5.2: Thought Experiment
300
Objective 5.3: Review
301
Objective 5.3: Thought Experiment
302
Index 303
What do you think of this book? We want to hear from you! Microsoft is interested in hearing your feedback so we can continually improve our books and learning resources for you. To participate in a brief online survey, please visit:
www.microsoft.com/learning/booksurvey/ Contents
xiii
Introduction
M
ost development books take a very low-level approach, teaching you how to use individual classes and accomplish fine-grained tasks. Like the Microsoft 70-518 certification exam, this book takes a high-level approach, building on your knowledge of lower-level Microsoft Windows application development and extending it into application design. Both the exam and the book are so high-level that there is very little coding involved. In fact, most of the code samples this book provides simply illustrate higher-level concepts. The 70-518 certification exam tests your knowledge of designing and developing Windows applications. By passing this exam, you will prove that you have the knowledge and experience to design complex, multitier Windows applications using Microsoft technologies. This book will review every concept described in the exam objective domains, such as the following: ■■
Designing the layers of a solution
■■
Designing the Presentation layer
■■
Designing the Data access layer
■■
Planning a solution deployment
■■
Designing for stability and maintenance
This book covers every exam objective, but it does not necessarily cover every exam question. Microsoft regularly adds new questions to the exam, making it impossible for this (or any) book to provide every answer. Instead, this book is designed to supplement your relevant independent study and real-world experience. If you encounter a topic in this book that you do not feel completely comfortable with, you should visit any links described in the text and spend several hours researching the topic further using MSDN, blogs, and support forums. Ideally, you should also create a practical application with the technology to gain hands-on experience.
Microsoft Certified Professional Program Microsoft certifications provide the best method for proving your command of current Microsoft products and technologies. The exams and corresponding certifications are developed to validate your mastery of critical competencies as you design and develop, or implement and support, solutions with Microsoft products and technologies. Computer professionals who become Microsoft-certified are recognized as experts and are sought after industrywide. Certification brings a variety of benefits to the individual and to employers and organizations.
xv
More Info Other Microsoft Certifications
For a full list of Microsoft certifications, go to www.microsoft.com/learning/mcp/ default.asp.
Acknowledgments First and foremost, I’d like to thank Ken Jones at O’Reilly for his work to design the Exam Ref book series, for choosing me (once again) as an author, and for his work as an editor. It’s been great to work with you, as always, Ken! I’d also like to thank Bill Chapman, the technical reviewer, Holly Bauer, the production editor, Dan Fauxsmith, the production manager, and Susan McClung, the copyeditor. Finally, I must thank my friends and family for their support, especially Chelsea and Madelyn Knowles (for their support, patience, and companionship) and John and Linda Antonino (for always being gracious hosts).
Support and Feedback The following sections provide information on errata, book support, feedback, and contact information.
Errata We’ve made every effort to ensure the accuracy of this book and its companion content. Any errors that have been reported since this book was published are listed on our Microsoft Press site at oreilly.com: http://go.microsoft.com/FWLink/?Linkid=234917 If you find an error that is not already listed, you can report it to us through the same page. If you need additional support, email Microsoft Press Book Support at
[email protected]. Please note that product support for Microsoft software is not offered through the addresses above.
xvi Introduction
We Want to Hear from You At Microsoft Press, your satisfaction is our top priority, and your feedback our most valuable asset. Please tell us what you think of this book at: http://www.microsoft.com/learning/booksurvey The survey is short, and we read every one of your comments and ideas. Thanks in advance for your input!
Stay in Touch Let’s keep the conversation going! We’re on Twitter: http://twitter.com/MicrosoftPress
Introduction xvii
Preparing for the Exam
M
icrosoft certification exams are a great way to build your resume and let the world know about your level of expertise. Certification exams validate your on-the-job experience and product knowledge. Although there is no substitute for on-the-job experience, preparation through study and hands-on practice can help you prepare for the exam. We recommend that you augment your exam preparation plan by using a combination of available study materials and courses. For example, you might use the Exam Ref and another study guide for your “at home” preparation, and take a Microsoft Official Curriculum course for the classroom experience. Choose the combination that you think works best for you.
xviii Preparing for the Exam
C h apter 1
Designing the Layers of a Solution T
he highest-level part of the design process is also the most exciting: designing the application architecture. In this stage, the application begins to come to life, without bogging you down in technical details. You create a logical design for your application and then map the logical layers to physical servers. Once you determine the physical layout, you can choose inter-application communication mechanisms to expose your business logic to different front-end user interfaces (UIs) and determine your authentication, authorization, and cryptographic requirements.
important
Have you read page xviii? It contains valuable information regarding the skills you need to pass the exam.
Further into the design process, you choose whether you need to support Component Object Model (COM) interoperability, and what changes you might need to make to provide that as efficiently as possible. You also will need to plan for scalability by estimating the minimum and maximum processing requirements of your application, how those requirements might change over its lifespan, and how you can scale the application accommodate it. Finally, you will need to decide how to use globalization and localization to adapt your application for different locations and languages. You must format data correctly for different regions and provide UI elements in the user’s preferred language.
Objectives in this chapter: ■■
Objective 1.1: Design a loosely coupled layered architecture
■■
Objective 1.2: Design service interaction
■■
Objective 1.3: Design the security implementation
■■
Objective 1.4: Design for interoperability with external systems
■■
Objective 1.5: Design for optimal processing
■■
Objective 1.6: Design for globalization and localization
1
Objective 1.1: Design a Loosely Coupled Layered Architecture In computer science, the term loosely coupled refers to different systems that can interoperate with minimal dependencies on each other. For example, if a client application communicates to the server application using only a well-defined web service, the two layers can be said to be loosely coupled. If a client and server application share common classes and assemblies and require those objects to communicate, the two layers are tightly coupled. Creating a loosely coupled layered architecture provides greater flexibility in the future. For example, if the client and server components are tightly coupled, upgrading one would require upgrading both. If you decided to transition a tightly coupled application to a different development environment, you would need to upgrade both simultaneously. With a loosely coupled architecture, you could upgrade one layer of an application without affecting the others.
This objective covers how to: ■■
Design service-oriented architectures.
■■
Provide separation of concern (SoC).
■■
Design a system topology.
■■
Choose between presentation and business logic.
■■
Use Windows Communication Foundation (WCF) routing.
■■
Describe common uses of Microsoft BizTalk Server.
Designing Service-Oriented Architectures A service-oriented architecture (SOA) loosely couples different application components. Each component communicates using standards-based web services, which are defined using schemas and contracts. There are four commonly understood tenets of SOA: 1. Boundaries are explicit. Communicating between services can be costly and
nreliable. To communicate with a remote service, an application must convert u parameters into XML or another standard format, establish a network connection, and then transmit the data—perhaps thousands of miles away. The Microsoft .NET Framework is capable of completely hiding the fact that method calls are being accessed across the network, a feature that could lead developers to make inefficient design choices. For better performance and reliability, make boundaries explicit so that developers are aware of the costs of a remote service call.
2
Chapter 1
Designing the Layers of a Solution
2. Services are autonomous. Services do not make assumptions about other services;
they assume other services will change or not exist at all. A service that today runs on the same computer and responds instantly might be moved later to a different part of the world, with several seconds of latency. Services never trust other services like they might methods within a single application; they assume all input is malicious and validate all data. Because Microsoft Visual Studio can build classes that transparently access web services, it is easy for developers to forget that they must carefully check the integrity of data provided across the network. 3. Services share schema and contract, not class. Rather than two services sharing
a single class and using that for communications, services advertise contracts that describe the format of messages they send and receive. These contracts must remain stable. When you must change the schema or contract, create a new version of your service. Many developers create a web services client and server within the same Visual Studio project; doing this would make it easy to accidentally violate this tenet. 4. Service compatibility is based upon policy. Policies separate low-level structural
compatibility (such as defining the format of messages) from higher-level semantic compatibility (such as defining capabilities and requirements). More Info The Four Tenets of SOA
You can read the original article that defined the four tenets, “A Guide to Developing and Running Connected Systems with Indigo,” at http://msdn.microsoft.com/magazine/ cc164026.aspx. For more information about policies, read “Understanding Web Services Policy” at http://msdn.microsoft.com/en-us/library/ms996497.aspx.
In practice, creating an SOA application means the following: ■■ ■■
■■
■■
■■
Each layer of the application is a separate solution in Visual Studio. Layers communicate using standards-based web services, such as those you can build with WCF. Layers do not depend upon a specific implementation of other layers, allowing each layer to be updated or replaced without affecting the others. Layers do not require other layers to use the same classes, development platform, or operating system. Although different layers should not share classes, they often will. It’s important, however, that they have well-defined boundaries and the communications between them are standardized.
Objective 1.1: Design a Loosely Coupled Layered Architecture
Chapter 1
3
Providing Separation of Concern Separation of concern (SoC) is a software architecture concept for dividing code used for different purposes. For example, if you were designing an application with SoC in mind, you might create different application layers for the UI, the business logic, the data access, and the database itself. SoC provides several benefits. Support for test-driven development allows quality ssurance (QA) to query the business logic directly to verify that it provides an expected a output when given a specific input. Developers can modify client applications to update the UI without any potential impact on the business logic or data access layers. Implementing SoC can increase development time for smaller applications, albeit by a small margin. However, SoC can dramatically reduce debugging, QA, and maintenance time. SoC also simplifies dividing development tasks between multiple developers. Therefore, the larger the development effort, the more important SoC becomes.
Designing a System Topology Although other system topologies exist, such as Model-View-ViewModel (MVVM) and the ASP.NET Model-View-Controller (MVC), you should be familiar with the traditional three-layer architecture for the 70-518 exam. The three-layer architecture consists of the following: ■■
■■
■■
Presentation The UI, this layer is responsible for layout and formatting. For Microsoft Windows applications, you will typically implement the Presentation layer using a W indows Presentation Foundation (WPF), Silverlight, or Windows Forms application. Business Logic Also known as Application Logic, this layer is responsible for making decisions based on business rules. If this layer has multiple sublayers, you can refer to the architecture as an n-tier architecture. Typically, you will implement the business logic layer using a WCF web service. Data Typically implemented by a database, this layer is responsible for storing and retrieving information. NOTE Layer vs. Tier
Although the two terms are often used interchangeably, technically, layers are logical divisions in an application, whereas tiers are physical divisions that are deployed to separate computers.
Figure 1-1 illustrates the three-layer architecture and provides examples of the components that might be in each layer.
4
Chapter 1
Designing the Layers of a Solution
Web client
Business logic
Presentation
Windows client
Decision making
Approval
Other database server
Data
Microsoft SQL Server
Workflow
Figure 1-1 The three-layer architecture
For a typical WPF application, the three-tier architecture might be implemented as: ■■
■■
Presentation A WPF application. The WPF application communicates with Business Logic web services (typically using WCF) to submit information provided by the user and retrieve information to be displayed to the user. This layer should not process the data in any way; it should only validate user input and format data for display. You should write the least amount of code possible in this layer. Business Logic A .NET Framework assembly that exposes services via WCF. The Business Logic layer receives requests from the Presentation layer such as, “How long will it take to ship this item?” or “Should I offer this customer a coupon?” The Business Logic layer retrieves all data required to answer queries from the Data layer.
Objective 1.1: Design a Loosely Coupled Layered Architecture
Chapter 1
5
■■
Data A database server, such as a server running Microsoft SQL Server. The data layer stores raw data, such as a table containing every item for sale and the number in inventory or a table with every customer and an index of their orders. Often, the Data layer will include a separate assembly that performs data access, abstracting the Business Logic from the database implementation and allowing you to change databases without modifying the Business Logic layer.
The three-layer architecture offers these benefits: ■■
■■
■■
■■
■■
Easier to divide among different developers Lets the database developers design the database and the design specialists create the UI. Easier to replace a single component For example, you could initially launch the application with a WPF interface. Later, you could create an ASP.NET Presentation layer to provide mobile access. By separating the Presentation layer, you would not have to rewrite the entire application—just the Presentation layer. Easier to scale You can add more database or application servers without making any changes to the presentation layer. More flexibility Many Windows applications include Presentation and Business Logic in a single assembly. By separating the two, you simplify replacing the UI later. It also allows you to support multiple, different UIs, such as web, Windows, and mobile interfaces. Easier to test The only way to create a reliable application is to create a testable application. Providing separation of concern allows you to more easily test individual components.
If these three tiers aren’t familiar to you as a Windows developer, it is because many indows applications use a two-tier logical architecture that combines presentation and logic W into a single application. More Info Creating Layered Applications
For Visual Studio templates that generate a structure for layered applications, visit http://layerguidance.codeplex.com/. For samples of a layered .NET Framework application that provides both Windows and web clients, visit http://layersample.codeplex.com/, http://www.codeproject.com/KB/cs/Three_Layer_Architecture.aspx, and http://cloudsample.codeplex.com/.
Choosing Between Presentation and Business Logic Many tasks could be performed at either the Presentation layer (the client) or the Business Logic layer (the server). For example, if you ask users to enter their address in a form, you could provide a DropDownList named CountryDropDownList with every country in the world. Once they select a country, you could populate the StateDropDownList with a list of states/ provinces in their country.
6
Chapter 1
Designing the Layers of a Solution
You could do this on either the server or the client, as follows: ■■
■■
Client Store a list of countries in a collection and use that collection to populate a drop-down list. Server Expose a web service method that, when queried, returns a list of countries. Then, use the results of the web service method to populate the drop-down list. To reduce unnecessary queries, you could cache the list of countries on the client.
Neither approach is clearly superior, but each has advantages. By providing the list of countries on the server, you keep more code in the Business Logic layer, which allows you to update it without distributing client updates. By storing the list on the client side, you improve performance for both the user and the server by reducing the number of queries to the Business Logic layer. This eliminates a delay in data entry. In addition, by reducing the number of requests sent to the Business Logic layer, it improves scalability. EXAM TIP
The 70-518 exam does not require you to know how to write code at all. You do need to know the capabilities and limitations, however, and have a higher-level understanding of the impact of writing different types of code.
Table 1-1 lists tasks that can be done on the client and situations that would require you to perform the task on the server instead. Table 1-1 Client-side and Server-side Tasks
Client-side Tasks
Server-side Tasks
For convenience, notify users if they enter data in an invalid format (for example, if they enter too few numbers for a credit card).
For security and data integrity, verify that user data falls within specified bounds.
Add items to a menu dynamically based on the user’s access level.
Authorize the user’s access to different methods.
Perform tasks that require access to the client, such as saving files or performing processing with the graphics processing unit (GPU).
Perform tasks that require access to resources on the internal network that the server can access but that are not exposed to the client.
Perform tasks that would consume a great deal of bandwidth communicating between the client and server.
Perform tasks that cannot be performed on the client.
Perform user-interface interactions, such as expanding menus and displaying slideshows.
Perform security-oriented tasks, such as processing credit cards and authenticating users.
If a task can be performed on either the client or the server, you should perform the task on the server because it centralizes business logic. In addition, you can never trust data submitted by a client because an attacker might create an application that impersonates an approved client, or data from the client might be intercepted and modified in transit.
Objective 1.1: Design a Loosely Coupled Layered Architecture
Chapter 1
7
Using WCF Routing You can use WCF routing to create a layer of abstraction between service layers. With WCF routing, you can create a single interface that sends requests to one of several different web services. You also can use routing to convert between different communication standards and data formats and to provide an Internet interface for intranet services. Figure 1-2 illustrates WCF routing.
ASM
X
NorthWebService
NetTcpBinding
REST Client
EastWebService MQ
MS
Routing Service
SouthWebService Figure 1-2 Routing provides a single interface for multiple web services
Just like every website is identified by a unique URL, all web services use context-based routing. When you use context-based routing, you identify a service endpoint using a unique host name/IP address, Transfer Control Protocol (TCP) or User Datagram Protocol (UDP) port number, and service name. For example, identifying a service with the URL http://contoso .com/myservice.asmx is context-based routing, and Microsoft Internet Information Services (IIS) would use the URL to distinguish requests from web services located at http://contoso .com/myotherservice.asmx or http://contoso.com:8080/myservice.asmx. Content-based routing identifies a service by looking within a message. When you use content-based routing, you identify a service endpoint using specific method and parameter values. For example, imagine a web service that processes transactions for three different credit card companies. The content-based router could examine the message to determine which credit card company should process the transaction, and then forward the message to the appropriate service. Responses would be routed through the routing service as well. Content-based routing is typically transparent to the client.
8
Chapter 1
Designing the Layers of a Solution
You can create a content-based routing service entirely by adding settings to your Web .config file. Because the routing service acts as a client to the back-end services, you must configure the back-end web service endpoints using the configuration section:
With the service configured, configure the section to examine the message and pass it on to one of the endpoints. In this example, the router filters use XPath to examine the Direction field in the message:
Finally, you can define the routing service with its endpoint. This configuration resembles any other WCF service except that it is derived from System.ServiceModel.Routing.RoutingService. You must set routing.routeOnHeadersOnly to false to allow WCF to examine the body of the message:
Understanding BizTalk Server While WCF routing is easy to configure and relies entirely on the .NET Framework, you also can use Microsoft BizTalk Server 2010 to route web service requests. BizTalk is a tool that allows you to connect different applications and then control how messages passed between the applications are processed. Because BizTalk can communicate with a wide variety of applications, you can use it to bridge different data formats (such as comma-delimited text and XML), types of communications (such as proprietary protocols and web services), and authentication protocols (such as Windows and non-Windows), in addition to simple web service routing. Figure 1-3 shows the BizTalk architecture. Business Activity Monitoring
BizTalk Server Orchestration
Business Rules Engine Health and Activity Tracking
Messaging
Enterprise Single Sign-on
Figure 1-3 The BizTalk architecture
You could, for example, use BizTalk to connect a mainframe order processing application that uses proprietary communications to a .NET Framework inventory management application that uses web services. Using BizTalk would relieve you of the time-consuming task of writing code to communicate with the mainframe application. In addition, if you later replaced either the mainframe application or the .NET Framework application, you would only need to connect the new application to BizTalk—you would not need to modify the other application.
10
Chapter 1
Designing the Layers of a Solution
BizTalk is also capable of: ■■
Converting data in different formats into XML documents
■■
Converting XML documents between different schema
■■
Prioritizing messaging
■■
Routing incoming messages to different applications
You will not need to know how to use BizTalk server for the exam, but you should be familiar with its capabilities. The exam objectives do specifically list content and context-based filtered routing, however. MORE INFO biztalk
For more information about BizTalk, visit http://www.microsoft.com/biztalk/.
Objective Summary ■■
■■
■■
■■
■■
The four tenets of SOA are that boundaries are explicit; services are autonomous; services share schema and contract, not class; and service compatibility is based upon policy. Providing separation of concern (SoC) makes it easier to replace layers of your application, simplifies dividing development tasks between multiple developers, and improves testing. The most common system topology is an architecture consisting of three layers: Presentation, Business Logic, and Data. When designing an application, aim to place as much code as possible into the Business Logic layer. The Presentation layer should be responsible only for formatting and displaying data. You can use WCF routing or BizTalk Server to forward requests to multiple web s ervices.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are planning the deployment of a new application using the traditional three-tier
architecture. Currently, your solution consists of a database server, a web service, a WPF client, and an ASP.NET client. The WPF client and the ASP.NET client communicate with the web service, which in turn communicates with the database server. While creating an architecture diagram, which component would you place within the Business Logic layer? A. The database server B. The web service
Objective 1.1: Design a Loosely Coupled Layered Architecture
Chapter 1
11
C. The WPF client D. The ASP.NET client 2. You are migrating a web service to a new architecture. Currently, the web service
is contained entirely within a single assembly, and it provides a single interface for all functions. In the next-generation version of the application, the web service will provide SoC by dividing different functions into three different web services. For backward compatibility, you would like to create a routing service that accepts requests from clients designed for the current version of the application and routes the requests to the appropriate next-generation web service. You would like to minimize software licensing costs. Which approach do you recommend? A. WCF context-based routing B. WCF content-based routing C. BizTalk Server D. SQL Server 3. You are designing a loosely coupled application. Which of the following is consistent
with the design of a service-oriented architecture? A. Use WebHttpBinding for communications between layers. B. Use NetNamedPipesBinding for communications between layers. C. Two services are built using a single Visual Studio solution, with shared classes
deployed to the Global Assembly Cache (GAC). D. A class in the data model makes calls to a standards-based web service to populate
property values.
Thought Experiment Designing a WCF Web Service
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Humongous Insurance. Humongous Insurance is designing an application that agents around the world will use to get quotes for new clients,
query and update existing customer records, and check their quarterly sales performance.
12
Chapter 1
Designing the Layers of a Solution
The developers have designed the architecture shown in Figure 1-4.
ASP.NET front-end
WPF front-end
WCF web service
SQL Server database Figure 1-4 The Humongous Insurance sample architecture
Answer the following questions about the future performance of the application:
1. What is the architecture of the application? 2. Does the application follow the four tenets of SOA? Why or why not? 3. If you later decide to separate the web service into three web services, how could you provide backward compatibility with existing clients?
Objective 1.2: Design Service Interaction Services, by their nature, must be designed for use by other developers. Because other developers will be working with your web service, you must pay particular attention to its design, especially the service granularity, cohesion, protocols, contracts, and binding types. Because web services are as vulnerable to attack as a website, you must use data validation to manage your data integrity carefully. This objective provides detailed information about designing web services.
This objective covers how to:
■■
Design service and method granularity.
■■
Choose protocols and binding types.
■■
Use Representational State Transfer (REST).
Objective 1.2: Design Service Interaction
Chapter 1
13
■■
Use message and data contracts.
■■
Use custom Simple Object Access Protocol (SOAP) headers.
■■
Manage data integrity.
■■
Choose between synchronous and asynchronous web services.
■■
Choose a message exchange pattern.
■■
Decide how to version an application.
■■
Choose between different ways to host a WCF web service.
Designing Service and Method Granularity Service and interface granularity defines the capabilities that you build into individual services. Within a service, you also can choose how granular individual methods are. The sections that follow describe these two concepts in more detail.
Service Granularity Typically, you should strive to design coarse-grained rather than finely grained services. For example, if you were creating a Business Logic layer for a supply chain application, you might need to expose methods that check the current inventory, ship parts between warehouses, and order additional inventory. If you chose to create finely grained service, you might create a separate service for each method. However, a better choice would be to create a single coarse-grained inventory management service that included all three methods. You would not, however, want to add methods related to other business processes to the same service; personnel management tasks, for example, belong in a different service. Coarse-grained services are easier for developers to create clients for because they need to create fewer service references. Coarse-grained services also are easier for business managers to understand, explain, and diagram when describing your application’s architecture. As shown in Figure 1-5, you can use other services or WCF routing to create a coarse-grained service that forwards requests to a more finely grained service, abstracting your finely grained implementation.
Cohesion The term cohesion means being grouped together. When designing services, cohesion defines how methods are grouped into different services. The methods within a service are cohesive if they perform similar activities with closely related data. In this way, choosing which methods are part of a service is exactly like deciding which functionality you should add to a class.
14
Chapter 1
Designing the Layers of a Solution
Client
Order Service (RoutingService)
Catalog Service
Product Description Service
Payment Service
Inventory Service
Shipping Service
Pricing Service
Figure 1-5 Using services to abstract finely grained services
Computer science defines several different types of useful cohesion. Of these, computer scientists consider functional cohesion to be the best. The types of cohesion are as follows: ■■
■■
■■
Functional cohesion The methods in a service are grouped together because they perform different aspects of a single task. Sequential cohesion The methods in a service are grouped together because they are called during the same phase in a process. Communicational cohesion The methods in a service are grouped together because they accept the same data as parameters.
Sometimes, creating loosely coupled architecture seems to be at odds with cohesion. If you were to create a highly cohesive design that was tightly coupled, your application would have a logical layout, but the interdependencies would make it difficult to update. If you were to create a loosely coupled application design that lacked cohesion, you would have few interdependencies between services, but service development would be more difficult,
Objective 1.2: Design Service Interaction
Chapter 1
15
evelopers consuming your services would have a difficult time understanding which services d and methods to use for any task, and changing one aspect of your business logic would require updating many different services.
Method Granularity You also can choose to have different levels of granularity for methods within a service. For example, you could create a single method that performs multiple actions as part of a transaction, or you could create multiple methods that perform each individual step within that transaction. As a general rule, you should create methods that perform an entire transaction so that you can avoid maintaining state between individual service calls. Not only does this simplify programming, but it simplifies the scalability of your application by allowing you to deploy multiple application servers that do not need to communicate the transaction state between each other. You should, however, create more finely grained service methods when the message size or the transaction time would be excessive. As a general rule, if you cannot respond to a request within five seconds, you should divide the request into multiple methods. For example, when creating a method to query the inventory for an item, you could accept a collection of multiple items as a parameter and return the inventory for all the items as the response; this would simplify programming and improve performance. However, if querying the inventory of each individual item took three seconds, the time to process a request for 10 items would be excessive, and it would be better to accept a request for only a single item at a time.
Choosing Protocols and Binding Types WCF supports many different types of network protocols, implemented as bindings. If you need to be able to communicate across the Internet, choose one of these Hypertext Transfer Protocol (HTTP) bindings because HTTP communications are almost always allowed through firewalls: ■■
■■
■■
16
wsHttpBinding A standards, SOAP-based web service, wsHttpBinding is perfect when you will be communicating with .NET Framework–based hosts, or if you need to communicate across the Internet, where firewalls might block non-HTTP traffic. wsHttpBinding provides powerful security features, making it the binding type of choice for Internet communications. wsHttpBinding does not support streaming or duplex communications. WSDualHttpBinding Like wsHttpBinding, except it provides duplex communications when the service needs to initiate communications to a client. Duplex communications will not work if the client is behind a firewall or Network Address Translation (NAT) device. basicHttpBinding Like wsHttpBinding, basicHttpBinding is SOAP-based. However, it is based on earlier SOAP 1.1 standards and does not include the full set of wsHttpBinding features such as encryption. basicHttpBinding is primarily useful for communicating with WS-Basic Profile conformant web services, such as ASMX-based web services.
Chapter 1
Designing the Layers of a Solution
■■
webHttpBinding A REST-style binding, which functions differently than SOAP. REST, uses a wider variety of HTTP commands than SOAP, such as GET, PUT, and DELETE. REST is described in more detail in the next section, “Using REST.”
If you don’t need to communicate across firewalls or the Internet, and all hosts are .NET Framework–based, you can choose from these more powerful bindings: ■■
■■
■■
■■
netNamedPipeBinding The preferred binding type for communications between processes on a single computer. netTcpBinding The most powerful binding type when all hosts are based on the .NET Framework. NetMsmqBinding Useful when you need to queue messages for later processing. For example, the client might need to submit a task to a server that will not be able to process the message in a timely manner, or is completely offline. NetPeerTcpBinding Provides peer-to-peer communications, when more than two hosts are involved.
Use the flowchart in Figure 1-6 to choose a binding type for your scenario. Although it does not include all binding types, it does cover the most common uses. Firewalls or non-.NET?
Yes
No
Queuing?
Yes
NetMsmqBinding
WebHttpBinding
ASMX?
Yes
BasicHttpBinding
Yes
WSDualHttpBinding
No
Yes
NetPeerTcpBinding
No
Local?
REST
SOAP
No
Peer-to-Peer?
REST or SOAP?
Duplexing?
No Yes
NetNamedPipes Binding
WSHttpBinding
No NetTcpBinding
Figure 1-6 WCF binding decision flowchart
Objective 1.2: Design Service Interaction
Chapter 1
17
Any service you expose can use multiple bindings. Therefore, you could provide a binding based on netTcpBinding for .NET Framework–based hosts, and a second wsHttpBinding for hosts that use open standards. While choosing the binding type is an important decision, it’s relatively easy to change fter the fact. Choose to define both client and server bindings using configuration files, a rather than hard-coding them into your application. Rely on discover protocols, such as Web Service Definition Language (WSDL), to save yourself from reconfiguring clients if the server settings change.
Using REST EXAM TIP
Read this section twice—REST is very important for the exam.
RES is an architecture that uses HTTP verbs (such as GET, POST, PUT, and DELETE) to retrieve and update resources on a server. Because it uses HTTP, RESTful web services closely resemble traditional web browser–to–web server interactions. For example, to retrieve information about a specific customer, a RESTful web services client might submit the following HTTP request: GET http://contoso.com/customer/92483
The RESTful web service might then respond with an XML document representing that c ustomer. To update a customer, the web services client might submit the following HTTP request: UPDATE http://contoso.com/customer/92483?name=Fabrikam
Because RESTful web services can use the HTTP verbs (GET, POST, PUT, and DELETE), ESTful web services should use subjects rather than actions. This can be counterintuitive, R however, b ecause most web developers tend to structure requests around actions rather than subjects. For example, if tasked with creating an ASP.NET webpage to update a customer, most developers would create a page for updating customers and retrieve the customer ID from the query string or form data. In such a web application, the previous example might instead be: GET http://contoso.com/updateCustomer?id=92483&name=Fabrikam
RESTful web services inherit these characteristics from HTTP: ■■
Requests can pass easily through most firewalls.
■■
All requests are stateless.
■■
Resources must be identified with a URL.
■■
18
Resource types are identifying using Multipurpose Internet Mail Extensions (MIME) types.
Chapter 1
Designing the Layers of a Solution
Unlike SOAP and most other web services implementations, RESTful web services do not require the use of XML. Therefore, you can use REST even if there is a technological barrier to XML. WCF added support for REST-style web services with the release of the .NET Framework 3.5. In particular, WCF Data Services can use REST to provide read and write access to an underlying database, with very little coding required by the developer. More Info WCF and REST
For more information, read “Overview of REST in WCF” at http://msdn.microsoft.com/netframework/dd547388.
Using Message and Data Contracts When you create a WCF web service, you can choose between creating a message contract (using the MessageContract attribute) and a data contract (using the DataContract attribute). ■■
■■
Message contact Define message headers and other elements. Message contracts are required for the SOAP format. Data contract Define the data types passed to and from the service without defining the format of the message.
Whenever possible, use a data contract because it is simpler to define and provides more exibility by allowing you to use different messaging techniques. Choose a message contract fl only when you need to conform to an existing standard, such as when you need to replace an existing SOAP web service, or when you need to pass information in headers separately from the data itself.
Creating a Message Contract To define a message contract, create a class and decorate it with the MessageContract attribute. Decorate individual properties with either MessageBody or MessageHeader. The following code sample demonstrates how to define a simple message contract: Sample of Visual Basic.NET Code _ Public Class Person Public operation As Operation Public transactionDate As DateTime Public Id As Integer Public Name As String Public Age As Integer End Class Sample of C# Code [MessageContract] public class Person
Objective 1.2: Design Service Interaction
Chapter 1
19
{ [MessageHeader] public Operation operation; [MessageHeader] public DateTime transactionDate; [MessageBodyMember] public int Id; [MessageBodyMember] public string Name; [MessageBodyMember] public int Age; }
Creating a Data Contract To create a data contract, specify a class that the web service will return to the client. Decorate the class with the DataContract attribute. Decorate each public attribute that you want the web service to send to the client with the DataMember attribute: Sample of Visual Basic.NET Code _ Public Class Person _ Public Property Id() As Integer End Property _ Public Property Name() As String End Property _ Public Property Age() As Integer End Property Public Sub New(_id As Integer, _name As String, _age As Integer) Me.Id = _id Me.Name = _name Me.Age = _age End Sub End Class Sample of C# Code [DataContract] public class Person { [DataMember] public int Id { get; set; } [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } public Person(int _id, string _name, int _age) { this.Id = _id; this.Name = _name;
20
Chapter 1
Designing the Layers of a Solution
this.Age = _age; } }
Next, add a WCF web service to your project. This creates a class with the ServiceContract ttribute, creates a .svc file that refers to the class, and adds the necessary service a configuration information to the Web.config file: Sample of Visual Basic.NET Code _ Public Class PersonService End Class Sample of C# Code [ServiceContract(Namespace = "")] public class PersonService { }
Finally, within your ServiceContract class, create a member method that exposes the data the client control needs, and add the OperationContract attribute to the method. The client will query this method: Sample of Visual Basic.NET Code _ Public Function GetPeople() As Person() Dim people As Person() = New Person(3) {} ' Populate the people array with fake data ' This would be replaced with a database query people(0) people(1) people(2) people(3)
= = = =
New New New New
Person(1, Person(2, Person(3, Person(4,
"Tony", 37) "Chelsea", 26) "Madelyn", 7) "Sandi", 7)
Return people End Function Sample of C# Code [OperationContract] public Person[] GetPeople() { Person[] people = new Person[4]; // Populate the people array with fake data // This would be replaced with a database query people[0] people[1] people[2] people[3]
= = = =
new new new new
Person(1, Person(2, Person(3, Person(4,
"Tony", 37); "Chelsea", 26); "Madelyn", 7); "Sandi", 7);
return people; }
Objective 1.2: Design Service Interaction
Chapter 1
21
More Info Message and Data Contracts
For more information about message contracts, visit http://msdn.microsoft.com/library/ ms730255.aspx. For more information about data contracts, visit http://msdn.microsoft .com/library/ms733127.aspx.
Using Custom SOAP Headers When designing web services, you might need to communicate system-related information from the client to the server. This information might include: ■■
Custom login credentials
■■
User tokens
■■
Preferences
■■
Application or operating system identification and version
■■
Time stamps
You should avoid putting system-related information into your messages. Instead, communicate these details using custom SOAP headers. SOAP standards and the .NET Framework implementation of SOAP allow you to add custom headers to communicate this type of information, separating system data from business logic data. The following shows a sample SOAP message with a custom header in bold: SubmitOrder lit3py5rt21z5v5fvlm25s55 …
In the .NET Framework, implement a custom SOAP header by adding the custom data to the outgoing message header and extracting the data from the incoming message header. The sections that follow describe three ways to do this, in order from most basic to most flexible.
How to Add Simple Custom SOAP Headers The simplest way to add custom SOAP headers is to create a new instance of MessageHeader by calling MessageHeader.CreateHeader on the client. Then, add it to the Headers collection. On the server, read the header from the incoming message by calling Headers.FindHeader
22
Chapter 1
Designing the Layers of a Solution
and then Header.GetReaderAtHeader. GetReaderAtHeader creates an instance of XmlReader, which you can process like an XML document. More Info Creating Custom SOAP Headers Using MessageHeader
For detailed instructions describing how to create custom SOAP headers, read “Custom Message Headers in WCF” at http://developers.de/blogs/rolf_nebhuth/archive/2006/07/27/864.aspx.
How to Create Custom SOAP Headers Using Generic Classes The simplest way to add strongly typed custom SOAP headers is to create an instance of the generic System.ServiceModel.Channels.MessageHeader class on the client using the data you need to pass in the header, and then adding it to the OperationContext.Current.Outgoing MessageHeaders collection on the client. On the server, read the header by calling the generic OperationContext.Current.IncomingMessageHeaders.GetHeader method. More Info Creating Custom SOAP Headers Using the Generic MessageHeader
For detailed instructions describing how to create custom SOAP headers using the generic MessageHeader class, read “WCF Custom Message Headers” at http://blogs.microsoft.co.il/ blogs/bursteg/archive/2006/04/23/141.aspx.
How to Create Custom SOAP Headers Using Custom Classes For increased flexibility, you also can create custom SOAP headers by creating custom classes. First, derive a new class from the System.ServiceModel.Channels.MessageHeader class. Override the OnWriteHeaderContents and ReadHeader methods to serialize and deserialize the header. You will also need to create a custom MessageInspector. On the client, implement IClientMessageInspector. On the server, implement IDispatchMessageInspector. To add the custom header, override BeforeSendRequest and call Message.Headers.Add. To read the custom header, override AfterReceiveRequest, find the header by calling Message.Headers .FindHeader, and retrieve the header by calling Message.Headers.GetReaderAtHeader. GetReaderAtHeader creates an instance of XmlReader, which you can process like an XML document. Finally, you will need to create a custom behavior to add the custom MessageInspector to the WCF pipeline. To do this, derive custom classes from IEndPointBehavior and Behavior ExtensionElement and then configure the custom behavior in the Web.config file.
Objective 1.2: Design Service Interaction
Chapter 1
23
More Info Creating Custom SOAP Headers Using Custom Classes
For detailed instructions describing how to create custom SOAP headers, read “Handling custom SOAP headers via WCF Behaviors” at http://weblogs.asp.net/paolopia/archive/2008/02/25/ handling-custom-soap-headers-via-wcf-behaviors.aspx. Refer to MSDN for details about the individual classes.
Managing Data Integrity Most developers understand the importance of validating all user input. Web services require a simple extension of that philosophy: validate all data that crosses a trust boundary. Naturally, a trust boundary exists between an application and a user. Trust boundaries also exist between a client and a web service or when any external application might provide input to an internal application. You do not necessarily need to validate data when it is sent between two internal applications, assuming that you trust that the application has already validated the data. For example, if you have a trusted business logic web service that validates data from clients and then stores it using a data access web service, you do not necessarily need to revalidate the data at the data access web service. However, doing so would improve security and data integrity.
Choosing Synchronous vs. Asynchronous Services can be synchronous or asynchronous: ■■
■■
Asynchronous A one-way operation that does not return a response immediately but can return a response using a callback. The client submits a request and then immediately continues processing, without waiting for a response. If the service provides an asynchronous callback, the callback method runs when the asynchronous operation is complete. Clients of asynchronous web services are not immediately aware of errors that might occur. Synchronous A two-way, request-response operation. The client submits a request and waits until the service returns a response. Synchronous services can return fault messages, which behave like exceptions.
Because they can return fault messages, most web services should be synchronous. Even if the web service cannot immediately complete the processing of the request, choosing a synchronous web service allows you to return a token to the client that they can use to retrieve their results, or an error message to the client if the web service can determine immediately that the request cannot be processed. If you must use an asynchronous service, you can return a response using polling. With polling, the client sends additional asynchronous messages to the server to determine if the response is ready. Typically, the client needs to identify the original request with a unique identifier. 24
Chapter 1
Designing the Layers of a Solution
Choosing a Message Exchange Pattern Service exchanges can be stateful or stateless: ■■
■■
Stateful Also known as a conversational exchange, stateful exchanges require the service to store data for a client and retrieve it to process subsequent requests. In a stateful exchange, the client might provide data in one request that is used to address future requests. For example, an order management application might first call CreateOrder(), make multiple calls to AddItemToOrder(), and finally call SubmitOrder(). Stateless Stateless exchanges do not require the service to track a single client between requests. In a stateless exchange, the client provides all the information required to complete each request. For example, a stateless order management application might make a single call to SubmitOrder() that includes the entire details of the order, including each individual item.
Use stateless exchanges whenever possible. Stateless services are easier to reuse and r eplace. In addition, you can scale out stateless web services and provide redundancy by simply adding servers and configuring round-robin Domain Name System (DNS) with the IP address of each server.
Versioning In the real world, applications regularly change to fix bugs and add features. Most of the time, developers strive to provide backward compatibility. Backward compatibility means that an update allows an application to continue to work with services, applications, and other components that were designed to work with an earlier version of an application. However, not all updates provide perfect backward compatibility, and sometimes ackward compatibility is impossible to provide. You can use versioning to limit the impact of b imperfect backward compatibility. The .NET Framework itself provides an ideal example of how to use versioning to limit the impact of backward compatibility. Between releasing versions 1.0 and 4.0 of the .NET Framework, Microsoft added a massive amount of new classes and functionality. Therefore, an application written for the .NET Framework 4.0 probably cannot run using the .NET Framework 1.0 because most developers would have used one of the new classes or features. While updates to the .NET Framework typically have minimal changes to existing classes, most releases have several important changes that would prevent many applications written for an earlier version of the .NET Framework from running correctly with the newer release (without some updates). To work around this, .NET Framework applications always prefer to run using the version of the .NET Framework they were created with. Therefore, if you run an application written with .NET Framework 3.5 on a computer that has every version of the .NET Framework installed on it, it will always run with .NET Framework 3.5. If the computer does not have the exact version of the .NET Framework installed, the application might run using a newer version of the .NET Framework, depending on how you have configured the application.
Objective 1.2: Design Service Interaction
Chapter 1
25
Using versioning, Microsoft has almost eliminated the problem of backward compatibility. .NET Framework 4.0 never needs to be able to run applications written using earlier versions because those applications will simply run with their native version of the .NET Framework. While Microsoft does release updates to the .NET Framework without incrementing the major version number, those updates never seriously change the functionality of any class within that version of the .NET Framework, and they provide both forward and backward compatibility. For example, if Microsoft releases a security update to fix a vulnerability in .NET Framework 4.0, they don’t increase the version number to 4.1. They also test the update to verify that applications written before the update was released will work after the update is installed, providing backward compatibility. They also provide forward compatibility by verifying that applications compiled using the update will run correctly on a computer that does not have the update installed. Following this versioning technique sounds straightforward: increment the version number only when an update might cause another application to fail, and have other applications always prefer the version they were compiled and tested against. However, it requires discipline. If you add a new feature to your assembly, no matter how minor, you need to increment the version number. Any other applications that use your assembly will need to be tested and recompiled against the newer version of your assembly before they can use the new version. If you release an update and do not increment the version number, clients can immediately take advantage of the improvements. However, you must test the assembly thoroughly to ensure that it provides perfect forward and backward compatibility. The .NET Framework provides two different types of version numbers: ■■
■■
File version A version number only for your reference. The .NET Framework never examines the file version. Assembly version The version number that the .NET Framework uses to determine which assembly to bind to at run time.
Unless you need to maintain two version numbers, you should disregard the file version and use only the assembly version. When there is more than one version of an assembly installed, such as in the Global Assembly Cache (GAC), the .NET Framework will always prefer to bind with the version used to compile the application, even if there is a newer version available. Therefore, you should increment the assembly version every time you want applications to use the assembly only after they have been recompiled with it. If you release a bug fix or security update that does not affect functionality, you should not increment the assembly version. In fact, you might need to apply the update to multiple, older versions of your assembly, each with their older version numbers. The same concepts apply when building web services. To provide backward compatibility, simply continue running a web service with the same endpoint. If you release a new update that changes functionality, deploy it to a new endpoint, and inform clients of the newly available version so that they can update and deploy their clients at their leisure, without interrupting current versions of their clients.
26
Chapter 1
Designing the Layers of a Solution
.NET Framework versions include four numbers, in the format .. .. Typically, you manually increment the major and minor versions for important releases. In Visual Studio, you can set the build number to an * to have it automatically increment with each build. You should leave the revision number blank or set to 0. More Info Versioning for Services
For more information, read “Versioning in SOA” at http://msdn.microsoft.com/library/ bb491124.aspx.
Hosting WCF Services You can host a WCF service in four different ways: ■■
■■
■■
■■
Self-hosting You create an application that listens for incoming connections. To run the web service, you need to launch the application, which makes it difficult to self-host on a server. Self-hosting typically works best when the web service needs to run only when an application is running, such as a web service that is part of a WPF application. Hosting within IIS You run the service within IIS, which launches it the first time that it receives a request. You can use IIS to configure application pools that isolate the web service and even limit the processor time and bandwidth it uses. Systems administrators are typically familiar with configuring and managing IIS, simplifying deployment. Hosting within the Windows Process Activation Service (WAS) WAS functions similarly to IIS, but supports non-HTTP transport protocols such as TCP, named pipes, and message queuing. Hosting within a Windows service You create a Windows service that can start automatically when the computer starts. You can configure the process identity of a web service to specify which user account the service uses.
More Info Hosting WCF Services
For more information about self-hosting a WCF service, read “How to: Host a WCF Service in a Managed Application” at http://msdn.microsoft.com/library/ms731758.aspx. For more information about hosting within IIS, read “How to: Host a WCF Service in IIS” at http://msdn.microsoft.com/library/ms733766.aspx. For more information about WAS, read “How to: Host a WCF Service in WAS” at http://msdn.microsoft.com/library/ms733109.aspx. For more information about hosting a WCF service in a Windows service, read “How to: Host a WCF Service in a Managed Windows Service” at http://msdn.microsoft.com/library/ ms733069.aspx.
Objective 1.2: Design Service Interaction
Chapter 1
27
Objective Summary ■■
■■
■■
■■
■■
■■ ■■
■■
■■
■■
When designing services, consider cohesion, service granularity, and method granularity. Cohesion is how methods are grouped into services. Service granularity is how many different methods are grouped into a service. Method granularity is how many different capabilities an individual method has. The .NET Framework provides many different protocol and binding types for web services. For Internet web services, choose a protocol based on HTTP, such as wsHttpBinding or WSDualHttpBinding. If all clients use the .NET Framework and are located on the intranet, you can choose netTcpBinding or NetMsmqBinding. REST is a web services architecture that uses HTTP verbs (such as GET, POST, PUT, and DELETE) to retrieve and update resources on a server. REST can pass easily through most firewalls. Message contracts define message headers and other elements and are required for the SOAP format. Data contracts define the data types passed to and from the service without defining the format of the message. You can create custom SOAP headers when you need to communicate system-related information from the client to the server and you do not want to make the information part of the data. Always validate data that crosses a trust boundary, such as any data sent to a web service. Services can be synchronous or asynchronous. Asynchronous services provide a one-way operation that does not return a response. Synchronous services provide a two-way, request-response operation. Service exchanges can be stateful or stateless. Stateful exchanges require the service to store data for a client and retrieve it to process subsequent requests. Stateless exchanges do not require the service to track a single client between requests. In a stateless exchange, the client provides all the information required to complete each request. You should update the version number any time you change functionality in your application, but not if you release a security update or fix a bug. The .NET Framework version numbers include four components, in the format .. .. You can host WCF services in four different ways: self-hosting, hosting within IIS, hosting within WAS, and hosting within a Windows service.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are planning the deployment of a new WCF web service. The web service must
meet the following requirements: ■■
28
Chapter 1
Support open standards Designing the Layers of a Solution
■■
Function through firewalls and proxy servers
■■
Not require the use of XML
Which binding type should you choose? A. NetMsmqBinding B. NetTcpBinding C. WsHttpBinding D. WebHttpBinding 2. You are planning the deployment of a new WCF web service using REST. You plan
to host the web service on a server running Windows Server 2008 R2 located in a datacenter. You need the service to be available constantly. You want to provide administrators the greatest flexibility for configuring and managing the web service. How should you host the web service? A. Self-hosting within a WPF assembly B. Hosting within IIS C. Hosting within WAS D. Hosting within a Windows service 3. You are planning the deployment of a new WCF SOAP web service. The web service
must comply with an existing data format. In addition to the data being transferred, you also must transfer a unique ID with the message. Which solutions could meet this requirement? (Choose all that apply; each answer forms part of the complete solution.) A. Create a custom message contract and specify the MessageContract attribute. B. Add a custom MessageHeader instance to the Headers collection. C. Create a custom REST verb and define the value as a property. D. Add a custom DataMember to the DataContract.
Thought Experiment Designing a Public Web Service
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You can find
answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Southridge Video. Southridge Video provides DVD rentals and streaming video services across the Internet. They are planning to offer public, standards-based web services that their customers can use to browse, rent, and stream videos.
Objective 1.2: Design Service Interaction
Chapter 1
29
The developers have designed a web service with these attributes: ■■
A single web service with different methods for retrieving information about videos, managing account details (such as change of address), and streaming videos online.
■■
A SOAP-based web service using WsHttpBinding.
■■
The web service acts as the Presentation layer, forwarding requests to a separate application on the internal network that performs business logic tasks. In turn, the Business Logic layer retrieves data from and sends updates to the database.
Answer the following questions about the future performance of the application:
1. Will customers be able to create non-Windows clients to connect to our web service? Specifically, will they be able to use JavaScript?
2. Will customers be able to connect through firewalls and proxy servers? 3. What positive or negative effects do you expect from deploying all capabilities in a web service?
Objective 1.3: Design the Security Implementation Application security involves many different elements: ■■
■■ ■■
User authentication Validating the user’s identity using a user name and password, a certificate, or other credentials User authorization Determining whether a user is authorized to access a resource Application process identity The user account the application uses to access resources
■■
Application privileges The resources the application is allowed to access
■■
Cryptography Encrypting, validating, and signing data
This objective covers how to implement these security elements. More Info Auditing
This objective discusses auditing only briefly. For detailed information, refer to Objective 5.3 in Chapter 5, “Designing for Stability and Maintenance.”
30
Chapter 1
Designing the Layers of a Solution
This objective covers how to: ■■
Plan for User Account Control (UAC).
■■
Design for least privilege.
■■
Understand process identity.
■■
Understand impersonation and delegation.
■■
Implement authorization.
■■
Plan role management.
■■
Use cryptography.
Planning for User Account Control User Account Control (UAC), a feature of Windows Vista, Windows 7, and Windows 8, improves desktop security by having users log on without administrative privileges by default. When an application requires administrative privileges, UAC prompts the user to allow the action and then elevates the user’s privileges. While many applications in the past required administrative privileges, very few do today. For example, although it used to be common to store settings in the registry (which might require elevated privileges), today, settings are typically stored in .config files within the user’s profile. Similarly, applications in the past used to write files to the computer’s Program Files folder, which requires administrative access to update. Today, those types of files are typically stored within the user’s profile. If your application does require administrative privileges, you will need to configure it to use UAC to elevate privileges. There are two ways to do this: ■■
■■
Update the manifest When an application usually needs administrative privileges, use this technique to generate a UAC prompt every time the user runs the application. The default Properties\App.manifest file generated by Visual Studio 2010 includes commented-out instructions and configuration settings that you can use to elevate privileges automatically with UAC. Launch a new process at run time When users won’t always need administrative privileges, create a separate assembly that performs administrative tasks and configure that assembly’s manifest to generate a UAC prompt. Launch this assembly when the user accesses an administrative feature. The assembly could have its own UI, or it could simply perform a task specified by the calling assembly and then close.
Note that you cannot elevate the privileges of a running process.
Designing for Least Privilege Least privilege is the security concept of minimizing the risk of a security compromise by limiting permissions. For user security, least privilege requires administrators to ensure that users have only the minimum access they need. For developers, code access security (CAS)
Objective 1.3: Design the Security Implementation
Chapter 1
31
provides least privilege by limiting your application’s access to resources, regardless of which user is logged on. For example, an application might be allowed to read and write text files to a specific folder, or it might be allowed to communicate only on the internal network. Today, CAS provides an extra layer of protection from malware and application v ulnerabilities. If you were creating a new inventory management application, you could use CAS to grant the application only the minimum privileges it needed to connect to other internal web services and databases, display text and images, and export files to the user’s Documents folder. Even if your new application had a vulnerability that allowed it to run malicious code, CAS would prevent the application itself from installing software or modifying system files. Note CasPol.exe
By default, .NET Framework 4 no longer uses security settings configured with the CasPol.exe tool. In addition, machine-level security configuration is no longer supported. For more information, read “Security Changes in the .NET Framework 4” at http://msdn.microsoft. com/library/dd233103.aspx.
Using Partial Trust Partial trust is a technique for creating a least-privilege application environment by limiting the application’s privileges. Similar to the way browsers restrict the access that a JavaScript application has to your computer’s resources, the Common Language Runtime (CLR) restricts a WPF browser-hosted application’s access. WPF browser-hosted applications are also known as XAML Browser Applications (XBAPs). By default, the CLR assigns XBAPs the limited Internet permissions, regardless of whether the application is launched from the Internet or the local computer. By default, the Internet zone has a limited but powerful set of permissions, including: ■■
Displaying two-dimensional and three-dimensional visuals and media
■■
Playing sound
■■
Storing 512 kilobytes (KB) of data in isolated storage
■■
Using the Open File dialog box and Browser Download dialog box
■■
Using email links
■■
Showing browser pages from the same site
■■
Using .NET controls
The CLR will not allow applications running in the Internet zone to do many important things, however: ■■
■■
32
Using the Save File dialog box (you have to use the Browser Download dialog box instead) Full drag-and-drop capability (though you can simulate this function with mouse capture and move events)
Chapter 1
Designing the Layers of a Solution
■■
File system access (instead, you should use isolated storage)
■■
Accessing the registry
In Visual Studio 2010, you can configure an application’s security settings from the Project properties window. Because Visual Studio will restrict the permissions to the zone you specify, you will be able to identify problems with restricted permissions while debugging. More Info Partial Trust
For more information about partial trust, read “WPF Partial Trust Security” at http://msdn. microsoft.com/library/aa970910.aspx. For more information about configuring security settings while debugging, read “How to: Debug a ClickOnce Application with Restricted Permissions” at http://msdn.microsoft.com/library/593zkfdf.aspx and “How to: Set Custom Permissions for a ClickOnce Application” at http://msdn.microsoft.com/library/hafybdaa.aspx.
Using CAS Imperatively You can use CAS imperatively to verify that the code has sufficient privileges to carry out an action. This allows you to detect insufficient privileges, catch the exception if the privileges are not available, and log the error or provide helpful information to the user. The following code sample creates an instance of FileIOPermission, which represents rivileges to read or write files. FileIOPermission can also be used without specifying a file p name to determine whether the application has any file access privileges. Sample of Visual Basic.NET Code ' Create a permission to read input.txt Dim fp As New FileIOPermission(FileIOPermissionAccess.Read, "input.txt") ' Add a permission to read and write output.txt fp.AddPathList(FileIOPermissionAccess.Write Or FileIOPermissionAccess.Read, _ "output.txt") Try ' Verify that those permissions are available. ' Demand throws an exception if they are not available. fp.Demand() Catch s As SecurityException ' TODO: Handle the missing file access permissions End Try Sample of C# Code // Create a permission to read input.txt FileIOPermission fp = new FileIOPermission(FileIOPermissionAccess.Read, "input.txt"); // Add a permission to read and write output.txt fp.AddPathList(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, "output.txt");
Objective 1.3: Design the Security Implementation
Chapter 1
33
try { // Verify that those permissions are available. // Demand throws an exception if they are not fp.Demand(); } catch (SecurityException s) { // TODO: Handle the missing file access permissions }
Imperative security can affect performance. For each demand you make, the .NET Framework has to walk the stack to verify that every caller has sufficient privileges. Therefore, make demands prior to entering loops. After you call a permission’s Demand method, you can call its Assert method to improve performance for future demands of the same permission. After calling Assert, any future Demand requests do not have to walk the entire stack; the stack walk stops at the method that called Assert. Therefore, if you call methods that might make demands, you can improve the performance of those methods by creating an instance of the required permission, calling Demand, and then calling Assert. After you no longer require the permission, call the permission’s RevertAssert method. More Info Optimizing CAS
For more information about improving the performance of CAS, read “Security Optimizations” at http://msdn.microsoft.com/library/ett3th5b.aspx.
Using Application Domains The CLR runs .NET Framework applications within application domains. Application domains are virtual environments (commonly called sandboxes) with separate memory spaces that can be configured with restricted privileges. When the CLR runs a new application, it either creates a new application domain or adds the application to an existing application domain. As a developer, you can create application domains to run untrusted components with fewer privileges than the current application domain. At a high level, the process requires creating and configuring an instance of AppDomainSetup, specifying evidence using an instance of an Evidence object, and then using those two objects to create a new AppDomain instance for the component. More Info Configuring Application Domains
For details about how to configure an application domain with limited privileges using a configuration file and evidence, read “AppDomainSetup.ConfigurationFile Property” at http://msdn.microsoft.com/library/system.appdomainsetup.configurationfile.aspx.
34
Chapter 1
Designing the Layers of a Solution
Understanding Process Identity Besides CAS, application privileges are restricted by their process identity’s role-based security. For example, a web service hosted within IIS might run using the Network Service account, which becomes the web service’s process identity. If you were to host the web service in a Windows service, the process identity would be the user account the Windows service is configured to log on as. If a user were to run a WPF application on his local computer, the process identity would be his local account, with the privileges varying depending on whether the user is logged on as an Administrator and whether the application used UAC to elevate privileges. Note CAS and Role-Based Security
A hosted web application can never have more privileges than the process identity, but the .NET Framework can restrict privileges further using CAS.
By default, IIS 7.5 running on Windows Server 2008 R2 creates virtual accounts with similar privileges to the Network Service account. These virtual accounts have better security than using the shared Network Service account because the virtual accounts prevent applications running in different application pools from interfering with each other. To minimize the risks of your application being exploited to take unexpected actions on the host computer (such as transmitting the contents of private files across the network), configure a process identity with minimal privileges. To do this, create a new user account, assign it only the minimal set of operating system privileges that it requires to run, and then assign that user account as your application pool’s identity (if hosting within IIS) or the Windows service’s log on account (if hosting within a Windows service). More Info Process Identity
For more information about process identity, read “Application Pool Identities” at http://learn.iis.net/page.aspx/624/application-pool-identities/ and “Configuring ASP. NET Process Identity” at http://msdn.microsoft.com/library/dwc1xthy.aspx. For detailed information about the permissions you must configure for a custom process identity, read “ASP.NET Required Access Control Lists (ACLs)” at http://msdn.microsoft.com/library/kwzs111e.aspx.
Once you understand your process identity, you can use resource-level security to limit access to resources from your application. Typically, the default permissions assigned to the Network Service account are sufficient for web services; however, you can assign additional permissions if your application requires access to nonstandard resources, or you can remove default permissions to limit further the risks of a successful breach of security.
Objective 1.3: Design the Security Implementation
Chapter 1
35
If you are deploying your application to a single server, you can modify resource-level security settings manually. For example, you can use Windows Explorer to modify NTFS file permissions and grant your application’s process identity to save files to a folder. Similarly, you can use the Print Management console to allow your application’s process identity to print, or use the Registry Editor to grant your application’s process identity the privileges that it needs to update a registry setting. If you manually configure security settings, document those settings thoroughly so that systems administrators understand the security implications and so that they can restore the application to a different server. Any privilege changes you make will affect all applications using the same process identity. For example, if you grant the Network Service account privileges to create files in a directory, all applications using the Network Service account will have those privileges. This can increase security risks (if you grant additional privileges) or cause unexpected security problems (if you remove privileges). Therefore, you should use this technique cautiously and communicate your changes to systems administrators and other developers. Rather than configuring resource-level security settings manually, you should configure resource-level security settings using a setup project. This requires the user installing the application to have sufficient privileges to modify the security settings. The Network Service account has limited privileges on the local server and no privileges to remote computers. In the event of a breach of security, this helps protect other computers on your network from being accessed by an attacker. If your application needs to access local resources to which Network Service does not have access, or remote resources that require authentication, you have several choices: ■■
■■
■■
Create a privileged service account Create a domain user account with sufficient privileges on both the server and any remote servers, and then change the process identity of the application pool or service account to the newly created domain user account. To minimize risks in the event of a breach of security, assign the account the minimal privileges your application requires to run. Use impersonation to access resources on the server When users authenticate with Windows credentials, you can temporarily change your application’s process identity to match the browser’s. Any resources the application accesses will be accessed with the user’s credentials. Use delegation to access resources on remote computers Like impersonation, delegation changes the process identity of the application to match the client’s. Delegation flows the credentials to remote computers, however.
Understanding Impersonation and Delegation By default, applications use their process identity to access local and remote resources. Impersonation and delegation allow your application to access resources with the user’s credentials instead, synchronizing the security context of the client and server applications. To access resources on the server as the user, use impersonation. To access resources on remote computers as the user, use delegation. 36
Chapter 1
Designing the Layers of a Solution
You might use impersonation and delegation in any of these scenarios: ■■
Connecting to a database
■■
Connecting to a web service
■■
Accessing files
■■
Performing administrative tasks
If you use Windows authentication, your application has direct access to the user’s c redentials. If you use client certificate authentication, you need to map the client certificate to a Windows account when processing requests. If you use a custom authentication method, you need to obtain a token programmatically for the user’s Windows credentials.
Impersonating Users Impersonation allows an application to access local resources with the user’s credentials. Impersonation is costly and increases security risks, so you should use it minimally. To impersonate a user within a method imperatively, use System.Security.Principal.Windows ImpersonationContext or ServiceSecurityContext.Current.WindowsIdentity.Impersonate, as the following example shows: Sample of Visual Basic.NET Code Using ServiceSecurityContext.Current.WindowsIdentity.Impersonate() ' Access resources with the user's credentials Return String.Format("Hello, {0}", WindowsIdentity.GetCurrent().Name) End Using Sample of C# Code using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate()) { // Access resources with the user's credentials return string.Format("Hello, {0}", WindowsIdentity.GetCurrent().Name); }
To impersonate a user for an entire service method declaratively, decorate the method with OperationBehaviorAttribute and set Impersonation to ImpersonationOption.Required, as the following example shows: Sample of Visual Basic.NET Code _ Public Sub MyMethod(input As String) ' TODO: Do work as the user End Sub Sample of C# Code [OperationBehavior(Impersonation = ImpersonationOption.Required)] public void MyMethod(string input) { // TODO: Do work as the user }
Objective 1.3: Design the Security Implementation
Chapter 1
37
To impersonate a user for part of a service method imperatively, decorate the method with OperationBehaviorAttribute. Within the method, create an instance of WindowsIdentity and then call the instance’s Impersonate method, as the following example shows: Sample of Visual Basic.Net Code _ Public Sub DoWorkAsUser(input As String) Dim userContext As WindowsIdentity = ServiceSecurityContext.Current.WindowsIdentity If userContext Is Nothing Then Throw New InvalidOperationException ("The caller cannot be mapped to a WindowsIdentity") End If Using userContext.Impersonate() ' TODO: Do work as the user End Using End Sub Sample of C# Code [OperationBehavior] public void DoWorkAsUser(string input) { WindowsIdentity userContext = ServiceSecurityContext.Current.WindowsIdentity; if (userContext == null) { throw new InvalidOperationException ("The caller cannot be mapped to a WindowsIdentity"); } using (userContext.Impersonate()) { // TODO: Do work as the user } }
More Info Impersonation and Delegation
For more information, read “Impersonation/Delegation” at http://msdn.microsoft.com/ library/cc949004.aspx and “Delegation and Impersonation with WCF” at http://msdn. microsoft.com/library/ms730088.aspx.
Delegating Credentials Delegation allows an application to access network resources with the user’s credentials. Because delegation increases the rights your application has to network resources, delegation increases security risks. For example, imagine that you created an intranet site that used delegation to access a database using the user’s credentials, allowing the user to edit his own profiles within the company’s directory. An attacker with access to the application code on the server could then modify the application to access confidential files with a user’s privileges. Delegation is even more powerful than impersonation; as a result, it is disabled by default in Active Directory Domain Services (AD DS) environments. If you are running your 38
Chapter 1
Designing the Layers of a Solution
pplication in an Active Directory domain, you must configure the application’s process a identity as a domain account and configure the server’s domain computer account for delegation. To allow unlimited delegation for a server, have an Active Directory domain administrator open Active Directory Users And Computers, edit the computer account’s properties, view the Delegation tab, and then select Trust Computer For Delegation. Constrained delegation reduces the security risks of delegation by restricting which resources an application can access during delegation. Instead of selecting Trust Computer For Delegation, select Trust This Computer For Delegation To Specified Services Only and then specify the services that your application is allowed to access. Figure 1-7 shows a computer that is configured to allow delegation only to access the Spooler service on a specific computer.
Figure 1-7 Use constrained delegation to limit security risks
More Info Constrained Delegation
For detailed instructions on enabling constrained delegation, read “Allow a computer to be trusted for delegation for specific services” at http://technet.microsoft.com/library/ cc739764.aspx.
Designing Trusted Subsystems There are two approaches to authenticating to back-end systems: ■■
Impersonation and delegation Applications present the user’s credentials to back-end systems. This is the best approach when the back-end systems can authorize individual users—for example, when the back-end systems and the users are in the same Active Directory domain.
Objective 1.3: Design the Security Implementation
Chapter 1
39
■■
Trusted subsystems Applications present service credentials to back-end systems. This is the best approach when you cannot assign user privileges directly to back-end systems.
Figure 1-8 compares these two architectures. Active Directory domains are used as an example; in practice, any trust boundary can be used, including ASP.NET membership. Impersonation and Delegation Active Directory domain
User
User credentials
User credentials
Application
Database server
Trusted Subsystem Active Directory domain
User
User credentials
Active Directory domain
Application
Service credentials
Database server
Figure 1-8 Comparing impersonation and trusted subsystems
Impersonation and delegation access back-end resources with the user’s credentials. Although this approach is the best design for many applications, it also has several disadvantages: ■■
■■ ■■
■■
Back-end resources must be able to authenticate the user. Typically, this requires the back-end resources to participate in the same Active Directory domain. Administrators must grant individual users access to internal resources. Impersonation and delegation elevate the server’s privileges, potentially increasing the damage of a successful breach of security. The server must create separate resources, such as database or web service connections, for each user being impersonated.
Trusted subsystems have a significant disadvantage: the application is responsible for controlling access to back-end resources. As a result, the burden of user authentication and resource authorization falls on the application developer, and security vulnerabilities in the application can expose private data to unauthorized users. Trusted subsystems are unable
40
Chapter 1
Designing the Layers of a Solution
to take advantage of the resource authorization and auditing capabilities that might be built into back-end services. For example, consider a payroll application with two different roles: users and managers. The Presentation layer is implemented using a WPF application, the Business Logic layer is implemented using a WCF web service, and the Data layer is implemented using SQL Server database server. Users can view their own payroll information, whereas managers can view or update records. If the developers implement the payroll application with impersonation and delegation, database administrators can grant users only read access to the database while assigning managers privileges to update the database. Database administrators could audit changes to the database, tracking which manager updated each record. However, the application server and the database server need to participate in the same Active Directory domain. If a user discovered a security vulnerability in the web service that allowed him to submit changes to the database, the database’s built-in authorization would reject the change and could record the unauthorized modification attempt. If the developers implement the payroll application using trusted subsystems, the pplication server and the database server do not need to participate in the same Active a Directory domain, and the application can use database connection pooling to improve performance. However, database administrators would need to grant the web service’s process identity privileges to update the database. Database administrators could audit changes, but the auditing would indicate only that the web service’s process identity made a change; it could not track individual users. If a user discovered a security vulnerability in the web service that allowed her to submit changes to the database, the database change would be successful, and the database could not associate the change with the specific user. In this scenario, the web s ervice would be responsible for authorization and auditing. More Info Trusted Subsystems
For more information, read “Trusted Subsystem” at http://msdn.microsoft.com/library/ ff649178.aspx.
Implementing Authorization Typically, Windows client applications are authenticated automatically using the account the user is currently logged in with. As the following sections describe, you can declaratively restrict access to entire methods or imperatively restrict access to sections of code based on the current user’s user name and group membership.
Using Federated Security Federated security is a system that uses security tokens to authenticate users and authorize them to access resources. With federated security, users authenticate themselves to a central identity provider, such as an Active Directory domain controller. The identity provider returns
Objective 1.3: Design the Security Implementation
Chapter 1
41
a cryptographic token to the user proving that the user has been authenticated. The token includes a list of group memberships (also known as roles). The user then sends the token, along with any requests to access a resource. If the resource trusts the identity provider, the resource knows that the user has been authenticated. The resource then can determine if the user is authorized by checking the group memberships included in the token. This process is illustrated in Figure 1-9.
1.User sends credentials 2.Identity provider sends token 3.User sends token to resources
1
2
3
Trust
Identify provider
Resource
Figure 1-9 Federated security, illustrated
If you were to implement federated security using a WPF web service client, a WCF web service, and AD DS as the identity provider, authentication and authorization might use the following process: 1. The user logs on to her computer using her Active Directory domain user account. As
part of the authentication process, the domain controller that authenticates the user provides a token to the user. 2. The user launches a WPF web service client. The web service client’s process identity is
the user’s current account, which is her domain user account. 3. The WPF web service client accesses the WCF web service and provides the token. 4. The WCF web service verifies that the token is valid for its Active Directory domain,
validating that the user is authenticated. The web service then can examine the token to determine if the user is a member of a group that has rights to access the web service. If authorization succeeds, the web service can respond to the request. Whether implemented using AD DS or another identity provider, federated security provides single sign-on, allowing users to authenticate once and then access many d ifferent resources. All the resources have to trust the same user’s identity provider, however. With AD DS, this is typically implemented by adding the server to the user’s Active Directory domain.
42
Chapter 1
Designing the Layers of a Solution
More Info Windows Identity Foundation
You can use Windows Identity Foundation to implement federated security. For more information, visit http://msdn.microsoft.com/security/aa570351.
Using Claims-Based Authentication A claim is the part of the token that identifies the user. Claims-based applications can accept many different types of tokens. For example, they might allow users authenticated to an Active Directory domain, users authenticated to a WCF web service’s custom membership provider, and users authenticated using Windows Live. The three most common types of tokens are as follows: ■■
■■
■■
User name/password token A token that contains two claims: the user name and password. Kerberos ticket The type of token used by AD DS and many other forms of federated security. These tokens include the user name, domain name, and other information. AD DS Kerberos tickets also contain security identifiers (SIDs) that identify the subject and their group memberships. Security Assertion Markup Language (SAML) token SAML tokens use an XMLbased language and can contain any claims that its creator chooses.
More Info .NET Authentication and Authorization
For detailed information about the different forms of authentication and authorization you can use with the .NET Framework, read “Digital Identity for .NET Applications: A Technology Overview” at http://msdn.microsoft.com/library/bb882216.aspx.
Requiring Membership Roles Declaratively You can use the PrincipalPermission attribute to limit entire methods to being run by specific users or roles. The following code sample shows a method that only members of the Administrators role can run. If the current user is not a member of the Administrators role, the CLR throws a security exception: Sample of Visual Basic.NET Code _ Shared Sub Manage() ' TODO: Administrator-only tasks End Sub Sample of C# Code [PrincipalPermission(SecurityAction.Demand, Role = "Administrators")] static void Manage()
Objective 1.3: Design the Security Implementation
Chapter 1
43
{ // TODO: Administrator-only tasks }
Requiring Membership Roles Imperatively You can use PrincipalPermission or User.IsInRole to verify the current user’s group membership within a method. User.IsInRole returns a Boolean value indicating whether the user is a member of the specified role, while PrincipalPermission.Demand throws an exception if the user is not a member of the specified role. To use PrincipalPermission, your code must follow this process: 1. Set the principal policy to WindowsPrincipal. 2. Create an instance of PrincipalPermission with the required privileges. 3. Call the PrincipalPermission.Demand() method. 4. Run the privileged code.
As with PrincipalPermissionAttribute, if the user fails the security check, the CLR throws a security exception. The following code sample demonstrates this process: Sample of Visual Basic.NET Code AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal) Dim principalPerm As New PrincipalPermission(Nothing, "Administrators") principalPerm.Demand() Sample of C# Code AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); PrincipalPermission principalPerm = new PrincipalPermission(null, "Administrators"); principalPerm.Demand();
To verify whether a user other than the current user is a member of a specific role, call Roles.IsUserInRole.
Planning Role Management When planning role management, follow these best practices: ■■
■■
44
Use Windows authentication for intranet applications when users have Active Directory domain accounts. This provides single sign-on for users and centralizes account management. If you use Windows authentication, the .NET Framework uses roles to represent group memberships. If you need to create your own user database separate from AD DS, create a WCF authentication service. This allows you to authenticate from a Windows client application to a web service using ASP.NET membership.
Chapter 1
Designing the Layers of a Solution
More Info WCF authentication service
For more information, read “Windows Communication Foundation Authentication Service Overview” at http://msdn.microsoft.com/library/bb386582.aspx. ■■
■■
■■
■■
If you create a WCF authentication service, work with systems administrators to include the application’s role management in their account management process. For example, when a user leaves the organization, systems administrators will need to remove both the user’s Active Directory domain account and the application account. Never assign privileges to an individual user. Instead, add users to roles and assign privileges to those roles. If an employee leaves the organization, you need only remove the user from the role rather than modifying how privileges are assigned. Create separate roles for different management tasks. For example, instead of creating roles for just users and administrators of a blog application, create separate roles for readers, writers, editors, content approvers, and website managers. Even though it might require you to add users to multiple roles, having more granular roles simplifies delegating tasks if more flexibility is required in the future. Always derive new security classes from existing .NET classes. Microsoft has carefully reviewed and tested the security components of the .NET Framework. This does not mean the .NET Framework does not contain security weaknesses; all code does. However, the .NET Framework’s extensive review and testing helps to make them more secure than classes written by individual developers.
More Info The Risks of Creating Custom Security Classes
For more information, read “How to Customize ASP.NET MVC Authentication” at http://blogs.teamb.com/craigstuntz/2009/09/09/38390/.
Using Cryptography Cryptography protects your data by altering using mathematical algorithms. In application development, the two most useful types of cryptography are encryption and hashing. Encryption is reversible cryptography that allows the intended recipient to decrypt data while making it very difficult for anyone else to access the unencrypted data. In application development, encryption is used to protect communications in transit (for example, while communicating across the network) and in storage (for example, while stored in memory, saved to a file, or written to a database). Hashing is non-reversible cryptography that uniquely identifies data, without allowing anyone to obtain the original data. You can use hashing to verify that a value matches the original data by hashing the value and comparing the results. In application development, hashing is used to sign files digitally and to store and verify passwords.
Objective 1.3: Design the Security Implementation
Chapter 1
45
While you must understand cryptography concepts, you rarely need to implement them yourself. Instead, you should use the cryptographic capabilities built into the operating system, database, and .NET Framework. The sections that follow describe how to use c ryptography to protect network communications and user passwords.
Using IPsec The benefit of communications encryption is that it helps prevent attackers with access to network communications from capturing private information. For example, if you create a WPF client that submits customer information to a web service, an attacker with access to the client, the web service computer, or the network connecting the two might be able to intercept those communications and access the customer information. Using encryption, the data would be unintelligible unless the attacker also had a valid decryption key. For internal applications, the simplest way to encrypt communications is to implement Internet Protocol Security (IPsec). System and network administrators can configure IPsec to encrypt automatically all communications between all computers and applications, specific computers, or specific applications on an intranet. IPsec also provides authentication, which helps to reduce the risk of an attacker impersonating either the client or server. Because the operating system performs IPsec encryption and authentication, you do not need to make any modifications to your application. More Info IPsec
For more information, visit http://technet.microsoft.com/network/bb531150.
Using Secure Sockets Layer (SSL) IPsec works well for internal applications because it requires no application-specific configuration. However, for IPsec to work, computers must share a certificate infrastructure. Therefore, Secure Sockets Layer (SSL) is a better way to encrypt communications between web service clients and servers on the Internet. SSL uses Hypertext Transfer Protocol Secure (HTTPS) to authenticate servers and encrypt both requests and responses. The benefit of server authentication is that it helps reduce the risk of DNS injection attacks that might direct a user to a malicious server when the user attempts to visit a known host name, a technique called a man-in-the-middle attack. If the request uses HTTPS and the server does not have a valid certificate for the requested host name, the user’s browser typically will display an error message and warn the user not to visit the site. .NET web services clients will throw an exception if a server’s SSL certificate is invalid. You should require SSL for web services that accept a user name, password, credit card number, or any other private information. However, using SSL has several drawbacks:
46
Chapter 1
Designing the Layers of a Solution
■■
■■
■■
■■
■■
To allow web services clients on the Internet to authenticate your web service, you must purchase a certificate from a public registrar. For intranet sites in an AD DS environment, you can configure clients to trust your internal certification authority and then issue private certificates without a fee. SSL certificates specify a single host name. Therefore, you need separate certificates for every host name that requires SSL. For example, contoso.com, services.contoso.com, and inventory.contoso.com would each require separate certificates. For security reasons, proxy servers and network caches, such as content delivery networks (CDNs), never cache content retrieved using SSL. Therefore, using SSL might increase server load and bandwidth, reducing scalability. Encrypting responses uses a small additional amount of the server’s processor time. Typically, this processing overhead is insignificant on all but the busiest web services. Using SSL requires a web service to have a unique IP address unless both the client and server support Server Name Indication (SNI).
Exam Tip
For the exam, remember to use communications encryption, implemented with IPsec or HTTPS, to mitigate the risk of man-in-the-middle attacks and capturing network traffic.
Using Hashes to Store Passwords In recent years, a number of organizations have had security breaches that resulted in user passwords being exposed. The cost of this can be devastating to both the organization and the users. The organization receives bad publicity and loses the confidence of its users. The users risk having their privacy violated, and if they use the same password at different sites (as most users do), they could have many different accounts compromised. If you create your own membership provider, store only hashes of user passwords. Hashing is a cryptographic technique for creating a unique set of characters that represents the original password. To improve security further, add a secret key to the password prior to hashing. To authenticate a user with a hashed password, store the hashed version of the password in a database when the user creates her account. When the user authenticates, repeat the hash process on the password the user submits and compare it to the hashed password in the database. If they match, the user provided the correct password. Unlike encryption, you cannot reverse a hash to determine the original password. herefore, if an attacker obtains access to your database, he would be unable to decrypt the T passwords. As an attacker, the best approach to identifying the original passwords would be an inefficient brute force attack: repeat the hashing process on a password dictionary and identify matching hashes. If you add a secret key prior to hashing the passwords, that approach would be successful only if the attacker also had the secret key.
Objective 1.3: Design the Security Implementation
Chapter 1
47
Exam Tip
For the exam, know that you should always hash passwords unless you need to retrieve the original passwords from the database. In those circumstances, choose reversible encryption. In the real world, you should use only hashing.
The .NET Framework provides the following classes for implementing hashing: ■■
HMACSHA1
■■
MACTripleDES
■■
MD5CryptoServiceProvider
■■
RIPEMD160
■■
SHA1Managed
■■
SHA256Managed
■■
SHA384Managed
■■
SHA512Managed
More Info Hashing
For more information, read “Ensuring Data Integrity with Hash Codes” at http://msdn.microsoft.com/library/f9ax34y5.aspx.
Using Digital Signatures To understand digital signatures, you must understand public key cryptography at a high level. With public key cryptography, users or services have two keys: a public key and a private key. Data encrypted with the public key can be decrypted only with the private key, and vice versa. The public key can be shared freely, whereas the private key must be kept secret. If you send a message encrypted with the public key, you can be sure that only the user or service with the private key can decrypt it, providing data confidentiality. If the private key holder encrypts a message with the private key, anyone with the public key can decrypt it, so there’s no data confidentiality, but there is non-repudiation because you know the holder of the private key encrypted it. Although algorithms might vary, the signing application follows these steps to sign data digitally: 1. Generate a hash of the data. 2. Encrypt the hash with the private key to generate the digital signature. The hash is
encrypted instead of encrypting all the data to improve performance. 3. Embed the digital signature and a copy of the public key into the file’s metadata.
The recipient verifies a digital signature by following this process: 1. Retrieve the public key from the metadata and verify that it was issued by a trusted
certification authority (CA). 48
Chapter 1
Designing the Layers of a Solution
2. Use the public key to decrypt the hash, which verifies that it was encrypted by the
holder of the private key that matches the public key. 3. Rehash the data and verify that the hash matches that stored in the digital signature.
Using this process, digital signatures provide data integrity (verifying that data has not been modified) and non-repudiation (verifying the identity of the sender) by combining hashing and public key cryptography. The .NET Framework provides the following classes for implementing digital signatures: ■■
DSACryptoServiceProvider
■■
RSACryptoServiceProvider
■■
ECDsa
■■
ECDsaCng
More Info Digital Signatures
For more information, read “Cryptographic Signatures” at http://msdn.microsoft.com/library/ hk8wx38z.aspx.
Objective Summary ■■
■■
■■
■■
■■
If your application requires more than standard user privileges, you will need to configure it to use UAC to elevate privileges. You can do this by either updating the manifest or by launching a new process at run time. You should design your applications for least privilege. Least privilege is a security concept that limits applications only to the rights they absolutely need to run. To help enforce least privilege, XBAP applications run with partial trust. You need to understand the rights a partial trust application has and those that it lacks. The user account your application runs within is considered its process identity. When your application attempts to use resources outside the .NET Framework, it will authenticate using its process identity credentials. If your application needs to connect to network resources, you will need to grant the process identity sufficient privileges. If the process identity has limited privileges, those limited privileges might restrict what your application can do. You can use impersonation from a web service to access local resources with the user’s credentials, rather than the application’s process identity. If you need to access network resources with the user’s credentials, use delegation. Authorization determines whether a user or application has privileges to access a resource. Federated security systems, such as the AD DS, use tokens to authenticate and authorize users.
Objective 1.3: Design the Security Implementation
Chapter 1
49
■■
■■
When planning role management, use Windows authentication for intranet applications. If you need to create your own user database, create a WCF authentication service. Assign privileges to roles instead of individual users. If the classes built in to the .NET Framework do not suit your needs, create new classes based on the .NET Framework classes. Use cryptography to protect your application’s data. Communications encryption, such as that provided by IPsec or SSL, helps protect your data from attackers who might access it as it crosses the network. You can use hashing to store passwords while limiting the risk that they might be compromised. Use digital signatures to prove that data has not been modified by anyone but the owner.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are designing a WCF web service that accesses a database running on a different
server. Your organization’s security policy states that all database requests must be issued using the users’ credentials; the web service’s process identity cannot be granted any privileges to the database. Both the web service server and the database server participate in the same Active Directory domain. How can you enable database queries while minimizing security risks? (Choose all that apply; each answer forms part of the complete solution.) A. Use delegation to access the database. B. Enable constrained delegation for the web service server’s computer account. C. Add the Network Service account to the Domain Admins group. D. Change the process identity of the application to the System account. 2. You are designing a WCF web service for the government. Government security
r egulations require that any messages sent from the web service to the client be validated to ensure that the data originated from an authorized web service and has not been modified since it was transmitted. Your manager has provided a public/ private key pair. You need to transmit large blocks of data, often more than 1 MB. How can you meet the security requirements while minimizing the performance impact? A. Encrypt the data with your private key. B. Encrypt the data with your public key. C. Hash the data. D. Sign the data digitally.
50
Chapter 1
Designing the Layers of a Solution
3. You are designing a WPF application that calls a third-party assembly to generate XML
files that it stores on the local file system. You need to minimize the security risks of calling the third-party component. Which approach should you recommend? A. Create a local user account with minimal privileges and configure the account as
the third-party assembly’s process identity. B. Create an Active Directory domain user account with minimal privileges and
configure the account as the third-party assembly’s process identity. C. Create an application domain with the least-privileged trust level and launch the
component within the application domain. D. Configure the assembly to impersonate the user’s credentials.
Thought Experiment Designing an Authorization Model
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Northwind Traders. Management is planning a new application that the sales staff will use to track marketing effectiveness and sales efforts. A WPF client application will connect to a WCF web service, which in turn connects to a database. Employees should be able to view and edit their own records, while managers
should be able to view and edit their employees’ records as well. In addition, management will have access to several additional tools. Users will log on using their Active Directory domain credentials. The developers have designed a solution with these features: ■■
Some methods should be used only by members of the Managers role. Within the WPF application, developers will use User.IsInRole to determine whether to display menu items associated with those methods.
■■
Some methods have sections of code that should be run only by members of the Managers role. Developers use User.IsInRole to determine whether the user can run it.
■■
Managers and their employees are associated using a table in the database.
■■
The web service accesses the database as a trusted subsystem.
Objective 1.3: Design the Security Implementation
Chapter 1
51
Management wants your opinion on the application design. Answer the following questions about the future performance of the application:
1. Is hiding the menu items sufficient to protect the manager-only views? If not, what would you do differently?
2. Is checking User.IsInRole sufficient to protect a section of code? If not, what would you do differently?
3. Is using a trusted subsystem the best way to protect records? Why or why not?
Objective 1.4: Design for Interoperability with External Systems You can interoperate with external systems in several different ways: ■■
■■
■■
Web services Communicate with external systems across a network using open standards. File sharing Writing data to a file that can then be transferred to an external system. This method of interoperability is common with mainframe services. Component Object Model (COM) COM is a standard for communicating between processes on a single computer and for accessing external libraries.
Objective 1.2 covered designing web services, and exchanging files is a straightforward matter. Therefore, this objective focuses on interoperability with COM.
This objective covers how to: ■■
Access assemblies from unmanaged code.
■■
Access COM objects.
Accessing Assemblies from Unmanaged Code You can share your class libraries, including the methods, classes, and properties contained within them, so that they can be accessed directly from unmanaged applications using COM. In Visual Studio, this requires two settings: ■■
■■
52
In the Assembly Information dialog box (accessed by clicking Assembly Information on the Application tab of the Project Properties window), select the Make Assembly COM-Visible check box. On the Build tab of the Project Properties window, select the Register For COM Interop check box. This creates a type library and registers the assembly as a COM object in the registry when you build the project.
Chapter 1
Designing the Layers of a Solution
You can also use the Assembly Registration Tool (RegAsm.exe, available from the Visual Studio command prompt) to generate a type library and register an assembly that has already been built for COM. More Info Assembly Registration Tool
For detailed instructions, visit http://msdn.microsoft.com/library/tzat5yw6.aspx.
When creating an assembly that you know will be accessed by COM clients, follow these guidelines to simplify accessing them from unmanaged code: ■■
Avoid using parameterized constructors. Instead, provide constructors with no parameters and allow the object to be configured after construction.
■■
Avoid using static methods.
■■
Define event-source interfaces in managed code.
■■
Include HRESULTs in user-defined exceptions. Interoperability automatically maps HRESULTs and exceptions. For a description of how these map to each other, visit http://msdn.microsoft.com/library/9ztbc5s1.aspx.
■■
Supply globally unique identifiers (GUIDs) for types that require them.
■■
Expect inheritance differences.
Accessing COM Objects To access a COM component from a .NET Framework application, add it as a reference in Visual Studio as follows: 1. From the Project menu, select Add Reference. 2. In the Add Reference dialog box, select the COM tab. 3. Select the COM object you need to refer to, and then click OK.
Now you can access the COM object using its namespace, just as you would any class ative to the .NET Framework. Alternatively, you can use the Type Library Importer tool n ( Tlbimp.exe, available from the Visual Studio command prompt) to create a set of proxy classes manually and add them to an assembly, which you can refer to from other projects. More Info Deploying COM Objects
For more information about deploying COM objects, refer to Objective 4.1, “Define a Client Deployment Strategy,” in Chapter 4, “Planning a Solution Deployment.”
If you must create new COM objects, follow these guidelines to simplify accessing them from managed code:
■■
Minimize the number of calls you make between managed and unmanaged code.
■■
Minimize the number of HRESULTS that you use to improve performance. Objective 1.4: Design for Interoperability with External Systems
Chapter 1
53
■■
■■
■■
■■
■■
Provide a type library with version and locale information, embed it as a resource within the COM object, generate the metadata directly, and sign the metadata with a publisher’s key pair. Providing version and locale information allows assemblies to bind to a particular version or locale of the COM object. Signing the metadata can improve security by preventing binding when the caller has the wrong key. If you do not embed the type library as a resource, be sure to register it so that the .NET Framework can locate it. Type arrays passed as arguments using SAFEARRAY in the unmanaged method signature. This allows the Type Library Importer tool to identify the parameter correctly as an array. If you plan to replace a COM object with a managed assembly at some point in the future and you want to avoid updating your application, create a wrapper assembly. Within the wrapper assembly, create a method for each method within the COM object that you require, and simply pass the parameters to the underlying COM object. Later, you can add complete functionality to the wrapper assembly by adding code to each method, and a pplications referencing the wrapper assembly will not need to be updated. Create a Primary Interop Assembly (PIA) if you want other .NET developers to work with a COM object that you have created. A PIA exposes COM interfaces to .NET. While PIAs are not required (because Visual Studio can build the classes required to refer to COM objects automatically), PIAs are useful when you want to limit the COM functions exposed to .NET.
More Info Building COM Components for Interoperability
For more information, visit http://msdn.microsoft.com/library/64hyx33x.aspx.
Objective Summary ■■
■■
You can expose .NET Framework assemblies so they can be accessed from unmanaged code like COM objects. To simplify access from unmanaged code, avoid parameterized constructors and static methods, define event-source interfaces, include HRESULTS in custom exception classes, and supply GUIDS. You can access COM objects from the .NET Framework. You can simplify this process when creating COM objects by minimizing the number of calls and HRESULTS, p roviding a type library, typing arrays using SAFEARRAY, and creating a wrapper or PIA assembly.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter.
54
Chapter 1
Designing the Layers of a Solution
1. You are designing a new WPF application that will run on every user’s desktop
c omputer. The application uses an internally developed COM object that generates chart images. You plan to migrate the COM object to managed code at some point in the future, and you would like to be able to replace it without recompiling and updating the WPF application. What would you recommend to minimize the development effort? A. Create a type library for the COM object. B. Create a second COM object that is optimized for access from managed code. C. Create a wrapper assembly that interfaces to the COM object. D. Create a WCF web service that interfaces to the COM object. 2. You are designing a new .NET class library that will be used by several different
anaged and unmanaged applications. What can you do to simplify access to the m assembly from unmanaged applications while optimizing performance? A. Use static methods. B. Avoid constructors without parameters. C. Create a type library. D. Throw exceptions for both success and failure conditions.
Thought Experiment Migrating an Unmanaged Application
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Contoso Pharmaceuticals. Recently, Contoso began migrating an unmanaged application to the .NET Framework. The unmanaged application consists of the following: ■■
One Win32 executable, written in C++
■■
Two internally developed COM objects, written in C++
■■
One third-party COM object, written in C++
Unfortunately, the third-party COM object is no longer being supported or updated. In addition, the Contoso Pharmaceutical developers do not know how to write unmanaged code, and therefore they cannot update the existing application or COM objects.
Objective 1.4: Design for Interoperability with External Systems
Chapter 1
55
The developers will follow this plan to migrate the application to the .NET Framework gradually:
1. Replace the first COM object with a .NET Framework class library. Deploy it into the production environment by replacing the COM object.
2. Replace the Win32 executable with a WPF assembly. Deploy it into the production environment.
3. Replace the second COM object with a .NET Framework class library. Deploy it into the production environment by replacing the COM object. Answer the following questions about the future performance of the application:
1. Is the plan technically feasible? Why or why not? 2. Would you change anything about the sequence of the plan? If so, what would you change?
Objective 1.5: Design for Optimal Processing Applications often have intense processing requirements. For large organizations, an application might need to be accessed by tens of thousands of users simultaneously. Processing these requests efficiently requires careful application design. Depending on the processing required for each request, you might need to deploy a single, powerful server, or you might need to deploy hundreds of servers. This objective discusses planning long-running processes, scaling applications up and out, and different forms of messaging that you can use to optimize processing.
This objective covers how to: ■■
Plan for long-running processes.
■■
Scale applications up or out.
■■
Move applications to the cloud.
■■
Use queuing.
■■
Minimize communications latency.
■■
Use a service bus.
Planning for Long-Running Processes Consider a WPF application for travel agents that provides flight information from multiple airlines. If a user requests information about all flights between Boston and Chicago on a specific day, the application might need to send web service requests to a dozen different 56
Chapter 1
Designing the Layers of a Solution
airlines and wait for the responses before displaying the results to the user. One airline might respond in half a second, but another airline might take 10 seconds. If the application queried each airline synchronously (in serial, one after another), the response time would be delayed by the sum total of all the airline web services. It would be more efficient to submit the web service queries asynchronously (in parallel, all at the same time). Then, the response time would be delayed only by the time required by the slowest web service. When you create a method, such as a Button.Click event handler, the code in the method runs synchronously by default. In other words, the CLR runs one line of code, waits for the results, and then moves to the next line. This linear flow is easy for developers to understand and it is efficient for short-running processes. If you have long-running processes, such as waiting for a web service to respond, you can use asynchronous processing to allow the .NET Framework to perform other tasks instead of waiting for the response. When the asynchronous response is complete, you can retrieve the results and update the response to the user. Note Writing Asynchronous Code
Ideally, any task that does not depend on the results of other tasks should be performed asynchronously. Using asynchronous programming techniques improves performance and scalability. In practice, however, you need to weigh the benefits against the complexity of writing and maintaining asynchronous.
Designing a Client Application for a Long-Running Process The flow of a typical synchronous event handler resembles the following: 1. The user clicks a button or selects a menu item, launching an event handler. 2. The event handler displays a wait cursor. 3. The event handler performs some processing, and the application is unresponsive. 4. The event handler updates the UI with the results of the processing. 5. The event handler returns the cursor to its normal state, and the application once
again becomes responsive. With this model, however, the user gets no feedback until the processing is complete. If it takes more than a few seconds, the user might be frustrated. Because the application is unresponsive, the user does not even have the opportunity to cancel the request. The flow of a typical asynchronous event handler resembles the following: 1. The user clicks a button or selects a menu item, launching an event handler. 2. The event handler launches a new asynchronous thread that performs the processing.
Objective 1.5: Design for Optimal Processing
Chapter 1
57
3. The main application remains responsive, giving the user the opportunity to interact
with it and cancel the previous request. 4. When the asynchronous thread completes processing, the application updates the UI
with the results.
Designing a Web Service for a Long-Running Process Web service developers need to design web services to accommodate long-running asynchronous processes. If both the client and server are based on the .NET Framework, and the client is not protected by a firewall or NAT device, you can use WSDualHttpBinding, netTcpBinding, or NetNamedPipeBinding, NetPeerTcpBinding, or NetTcpContextBinding to create a callback contract on the client and then use that callback to notify the client that the process is complete. Note Duplex HTTP for Silverlight and .NET Framework clients
Silverlight clients can use PollingDuplexHttpBinding, which supports duplex communications and allows the client to be located behind a firewall or NAT device. Unfortunately, .NET Framework 4.0 does not include a polling duplex HTTP binding. However, you can download a sample custom channel that might suit your needs at http://archive.msdn.microsoft.com/duplexhttp.
If the binding type does not support duplex communications, or if you must communicate through a firewall that prevents incoming connections to the client, you should handle long-running web service requests by immediately providing a token that the client can use to later retrieve the results. To provide better feedback to the user, also provide an estimated wait time that the client can use to display the progress. Then, have the client regularly poll the server to determine if the process is complete. Use polling to retrieve the results of a long-running query using a web service by following this process: 1. The client sends the request to the web service. This might be, for example, “List all
flights between Boston and Chicago on May 1.” 2. The web service provides a unique token to the client and an approximate wait time.
The token should be large and cryptographically random, such as a 20-byte value generated by RngCryptoServiceProvider. The wait time should be based on the actual wait time for similar requests. 3. The web service client displays a progress bar to the user to let him know that
the request is continuing and the application is responsive. The web service asynchronously calls a method to process the request and store the results in a database record associated with the token.
58
Chapter 1
Designing the Layers of a Solution
4. After an interval (for example, one-quarter the estimated wait time), the web service client
queries the web service for the results, providing the unique token. If the results are ready, the web service client formats and displays the data; otherwise, it repeats this step.
Using Windows Workflow Foundation You can use the Windows Workflow Foundation (WF) to implement long-running processes using a visual designer. WF represents business logic visually, responding to input and making decisions without writing any code. Naturally, you will need to write to code to carry out individual actions, but those actions can be simple and modular, helping to simplify code organization and provide greater SoC. Figure 1-10 shows a sample flowchart for a WCF workflow application. Flowcharts and sequences can branch and call different methods based on if-then-else and switch conditionals, and can loop using DoWhile, ForEach, and ParallelForEach. The results of the methods can be further processed and returned in response to a web service request.
Start
Switch Default InvokeMathod TargetType
(null)
TargetObject
Greeting
MethodName
SayGoodMorning 1
0 InvokeMathod
InvokeMathod
TargetType
(null)
TargetType
(null)
TargetObject
Greeting
TargetObject
Greeting
MethodName
SayGoodMorning
MethodName
SayGoodMorning
Figure 1-10 A WF flowchart
Objective 1.5: Design for Optimal Processing
Chapter 1
59
WF is a deep technology, and this book will not cover it in depth. For the exam, know that WF is useful for separating business logic and process from the code itself, and for creating long-running processes that might take minutes, hours, days, or weeks. More Info Windows Workflow Foundation
For more information about WF, visit http://msdn.microsoft.com/netframework/aa663328.
Scaling Applications Scaling up and scaling out are two different ways to meet increased capacity requirements, and they each have their own advantages. Scaling up, which involves upgrading e xisting servers, keeps the architecture simple and minimizes hardware costs, setup time, and management time. Scaling up limits you to the performance of a single server, however, and becomes increasingly expensive the farther you have to scale. Scaling out, which involves adding more servers, provides unlimited scalability and adds failover to keep the application available in the event of a hardware failure. As you scale out, the cost increases are more linear. The additional complexity of scaling out greatly increases setup and maintenance costs, however. Figure 1-11 illustrates the difference between the two approaches. The sections that follow describe each in more detail.
App + SQL
Scaling Out
App
App
App
SQL
SQL
Scaling Up
App
App + SQL
Figure 1-11 Growing an application by scaling out or scaling up
60
Chapter 1
Designing the Layers of a Solution
More Info Scalability Testing
For more information about scalability testing, refer to Objective 5.2, “Evaluate and Recommend a Test Strategy,” in Chapter 5.
Scaling Up The term scaling up refers to upgrading individual servers with faster storage subsystems, more memory, and additional processors. For most applications, scaling up (also known as scaling vertically) is the most cost-effective way to meet capacity requirements because it does not require any software changes or additional infrastructure. Note that scaling up does not require all services to run on a single server. You still can use multiple servers by placing the Business Logic and Data tiers on separate servers. In practice, this typically means separating the application server and database server onto different computers. As your capacity requirements increase, you can upgrade the individual servers or replace the servers with more powerful hardware. Scaling up is a convenient way to handle gradual growth because improvements in technology over time lead to more powerful servers. For example, if you require a single high-end server to meet your current capacity requirements, and you know that the load on the server will double over the next five years, you can simply plan to replace the server in five years with a future high-end server (which is bound to be several times more powerful than a current high-end server).
Scaling Out The term scaling out refers to adding multiple servers to a single role. In practice, scaling out (also known as scaling horizontally) is implemented most often by deploying multiple application servers and a load-balancing mechanism to distribute incoming requests between the servers. Commonly used load-balancing mechanisms include the following: ■■
■■
Round-robin DNS The easiest, least expensive, and most common load-balancing mechanism. Simply specify multiple IP addresses for the application server host name and build logic into the client to connect to any of the listed IP addresses. If the first IP address does not respond, have the client continue trying subsequent addresses until a server responds. In this way, round-robin DNS can provide for failover if a single server fails. Hardware load balancers You can purchase load balancers that you connect between the client and the server. Configure the application server’s host name with the load balancer’s IP address, and then configure each application server with a unique, internal IP address. The load balancer will accept all requests from clients and forward them to one of the application servers on the internal network. While load
Objective 1.5: Design for Optimal Processing
Chapter 1
61
alancers have widely varying features, many load balancers can balance the load b between servers efficiently by monitoring the server response time and utilization. Some routers have this capability built in. Hardware load balancers can stop sending requests to a failed server, providing failover. ■■
■■
Software load balancers Many software products, including Microsoft Forefront Threat Management Gateway 2010, use software to perform the same capabilities of hardware load balancers. Software load balancers often integrate more tightly with the operating system and application by monitoring the usage of individual servers and sending requests to the server with the most capacity. In addition, software load balancers can be managed like servers (instead of network devices), which reduces maintenance costs for some organizations. Network Load Balancing (NLB) Windows Server 2008 R2 includes NLB, a type of software load balancer, which can distribute incoming requests between multiple application servers without requiring a separate server.
More Info NLB Software
For more information, read “Overview of Network Load Balancing” at http://technet. microsoft.com/library/cc725691.aspx and “Microsoft Forefront TMG—Webserver Load Balancing” at http://www.isaserver.org/tutorials/microsoft-forefront-tmg-webserver-loadbalancing.html.
Because scaling out uses multiple servers to provide a single service, the service might be able to continue to function if a single server fails. Remember to test the failover mechanism thoroughly and verify that clients with existing connections to the failed server are successfully redirected to a functioning server. Redundancy also requires that the application be able to meet peak capacity requirements with at least one failed server; therefore, providing r edundancy requires you to deploy at least one additional server beyond that required to meet your peak capacity requirements. When scaling out to increase reliability, strive to eliminate all single points of failure. For example, avoid connecting all application servers to a single network switch. Depending on your redundancy requirements, you might need to deploy your application to multiple datacenters in different geographic locations and implement load balancing between those datacenters. Scaling out increases the capacity of your application, but it also can decrease erformance. By separating the application server and database server onto separate p computers, requests have to traverse the network, which adds milliseconds of latency. In other words, application responsiveness for small numbers of users is typically better for applications that are scaled up rather than scaled out.
62
Chapter 1
Designing the Layers of a Solution
More Info Comparing Costs
When comparing the costs of scaling up and scaling out, consider software licenses and electricity costs as well. For specific examples, read “Scaling Up vs. Scaling Out: Hidden Costs” at http://www.codinghorror.com/blog/2009/06/scaling-up-vs-scaling-out-hidden-costs.html.
Moving to the Cloud One of the most exciting trends in IT is to move applications into the cloud. The term cloud refers to a group of networked servers, usually connected to the Internet, that is available to process your application requests. As needs change, you can move applications between servers, scale them across multiple servers, and reconfigure network settings. The cloud hosting model runs your application on any of many different virtual servers (such as those provided by Windows Server 2008 R2 Hyper-V) in the hosted cloud. The hosting provider is typically responsible for determining which server or servers run your application. If your application is not busy, your application might be running on only one or two servers. If your application gets busier, the cloud hosting provider can distribute your application automatically to dozens or even hundreds of servers, and those servers might be located in different datacenters. Perhaps the greatest advantage of cloud computing is the ability to scale down easily. With a traditional hosting model, you need to deploy enough hardware for your application’s peak capacity, even though you might require that peak capacity only one or two times per year. Typically, the hosting provider charges you a flat monthly rate (with varying fees for the bandwidth you use). During quieter times, you pay the same monthly rate and still need to manage the unneeded hardware. With cloud hosting, the cloud hosting provider automatically scales down your application and charges you only for the resources you use.
More Info Windows Azure
The Microsoft cloud computing network is Windows Azure. For more information, visit http://www.microsoft.com/windowsazure/.
Using Queuing Queuing is an important concept for scalability because it allows requests to be handled later, smoothing out the utilization peaks that most server applications experience. Queuing also allows the application to prioritize requests, processing the most critical requests immediately and processing less important requests when the server has capacity. Essentially, queuing allows an application to handle a request asynchronously at a later time, rather than immediately rejecting it.
Objective 1.5: Design for Optimal Processing
Chapter 1
63
While queuing is not an option for many application scenarios, it works well with scenarios that might take a long time to process or where a dependent service might not always be available. For example: ■■
■■
Credit card processing Credit card processing services are notoriously unreliable. Using queuing, an application could accept a customer’s order and add the credit card information to the queue. If the queue is short and the credit card processing service is responding immediately, the application could confirm the order while the user is still online. If the service is offline or there is a long queue, it could confirm it later or request alternate payment information by sending an email to the user. Reporting Some reports can take several minutes or even hours to process. In this scenario, the application could add the report request to a queue and email the user when the report is available. Shorter or more important reports could be prioritized so that they are handled first.
You can use Microsoft Message Queuing (MSMQ) and the System.Messaging namespace to implement queuing. There are also third-party queuing systems, such as RabbitMQ. Because this exam is design-oriented, you do not need to know how to implement queuing. However, you should understand the types of scenarios where queuing might be useful and be able to suggest queuing to meet requirements for prioritization or asynchronous request handling. More Info Implementing Queuing
For more information about the System.Messaging namespace, visit http://msdn.microsoft. com/library/gg145046.aspx. For more information about RabbitMQ, visit http://www. rabbitmq.com/.
Minimizing Latency When designing interactions between applications, consider whether your interactions will be chatty or chunky: ■■
■■
Chatty application communications create many small requests. Chatty applications perform well when latency (the time it takes to send a message across the network) is low. Chunky application communications create fewer and larger batched requests. Chunky applications perform well when bandwidth (the total amount of data that can be sent across the network) is high.
Choose to bundle your communications together into chunks when the network infrastructure is high-latency and high-bandwidth, such as satellite or intercontinental communications. Use chatty communications by sending smaller messages immediately when the network infrastructure is low-latency and low-bandwidth, such as communications with mobile devices. The difference will be insignificant on most local area networks (LANs), which are low-latency and high-bandwidth.
64
Chapter 1
Designing the Layers of a Solution
Using a Service Bus A service bus is a software architecture component that asynchronously transfers messages between clients and servers, as illustrated in Figure 1-12. Besides routing messages, service buses often transport messages through firewalls, mitigate attacks, resolve conflicts, control versioning, convert data formats, and handle exceptions.
JavaScript Client
.NET Client
JSON
SOAP
Application Server REST
Service Bus
REST
WCF web service
WS-*
Java web service
JNI
Mainframe
Figure 1-12 A service bus connects different services asynchronously
Windows Azure provides a .NET service bus using the Microsoft.ServiceBus namespace. Building a service bus using a public cloud service, such as Windows Azure, is useful because clients on the Internet can connect directly to the cloud service rather than connecting to your private network. You only need to allow connections from the cloud into your private network. More Info .NET Service Bus
For more information, visit http://msdn.microsoft.com/magazine/dd569756.aspx, http://www.microsoft.com/windowsazure/features/servicebus/, and http://msdn.microsoft. com/library/windowsazure/ee732537.aspx.
Objective 1.5: Design for Optimal Processing
Chapter 1
65
Objective Summary ■■
■■
■■
■■
■■
■■
Long-running processes require special planning. For example, if a web service will need to process a request for several minutes, you will need to design a communication mechanism to return the results when the process has completed. In addition, you should design the UI so that it remains responsive and allows the user to cancel the request. You should plan for application growth by designing your application for scalability. There are two ways you can scale an application: up and out. If you scale up, you upgrade your server hardware, which has minimal impact on the application itself. Scaling out by adding more servers might be more cost effective; however, you will need to design your application to run on multiple servers simultaneously. You can host web services in the cloud to provide scalability. Cloud hosting providers virtualize your application, simplifying the process of adding or removing servers. Queuing allows your application to process requests that it cannot respond to immediately. You can minimize application latency by designing communications to be chatty or chunky. Chatty communications consist of many small requests, whereas chunky applications batch information into larger requests. A service bus is a layer of abstraction between clients and servers. Service buses are useful for translating between different protocols and data types. Service buses are also useful for bridging between Internet and intranet communications.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are designing a WCF web service with a WPF client that provisions virtual
achines for a testing environment. Users can provision from 1 to 10 virtual machines m at a time, and each virtual machine provision might take up to 60 seconds. For security reasons, the server hosting the virtual machines allows provisioning requests only from the server running the web service. You need to design the application to keep users notified of the provisioning progress. Which approach should you recommend? A. On the server, start asynchronous processes to provision each virtual machine. On
the client, query the server every 5 seconds for a status update. B. On the server, synchronously provision each virtual machine. When complete,
return a status update to the client.
66
Chapter 1
Designing the Layers of a Solution
C. On the server, calculate the approximate total provisioning time. On the client,
connect to the server hosting the virtual machines and initiate the provisioning. D. On the client, launch a separate asynchronous process for each virtual machine
to be provisioned. Within each process, issue a web service request to provision a virtual machine. 2. You are responsible for a web service that runs on a single server. You host the
server at your local datacenter, which has a single Internet connection. After a recent application outage, management has decided to eliminate all single points of failure in the application architecture. Which approach should you recommend? (Choose all that apply; each answer forms part of the complete solution.) A. Add multiple network adapters to the server, and connect them to separate
network switches. B. Deploy the application to a cloud hosting provider. C. Deploy a second server to the same data center, and use a hardware load balancer. D. Deploy a second server to a different data center, and configure round-robin DNS. 3. You are designing a WPF client application that connects to a WCF web service. The
web service might take anywhere from several seconds to several minutes to process a request. Users need to be able to cancel a request they have started using their WPF client application. To minimize the load on the firewall protecting the web service, you want to keep the open network connections as low as possible. How should you design the client and web service? (Choose two.) A. Submit a synchronous web service request from the WPF client. B. Submit an asynchronous web service request from the WPF client. C. Use a web service that maintains a session. D. Use a sessionless web service.
Thought Experiment Scaling a Web Service
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Trey Research. Trey Research currently hosts a Search for Extraterrestrial Intelligence (SETI) web service that analyzes data gathered by astronomers around the world to determine whether it might contain radio signals from life on distant planets. Volunteers can use web service clients to connect to the web service, download and process data, and then return the data to the web service after it has been processed—days or weeks later.
Objective 1.5: Design for Optimal Processing
Chapter 1
67
Currently, the web service is accessible only from the internal company network, and computers on the internal network connect to it to perform processing. Trey Research would like to allow users on the Internet to connect to it. They have identified two challenges: ■■
Scalability Currently, the web service handles only a few requests per hour. They need to be able to handle up to 100 requests per second.
■■
Connectivity Currently, the web service is hosted on the internal network behind a firewall, and management would prefer not to allow incoming requests from the entire Internet.
The web service now runs on a single server running Windows Server 2008 R2 and SQL Server 2008 R2. Trey Research has created a plan to expose their application to the Internet and then test and scale it. The plan is as follows:
1. Lease an Internet-connected server from a hosting provider. Establish a virtual private network (VPN) connection to the internal network. Configure WCF routing to forward requests from the hosted server to the internal web service.
2. Configure logging of performance data for both the web service and database. 3. Use Visual Studio from the local network to simulate web service requests directly to the internal web service. Gradually increase the number of requests to 100 requests per second, or until the web service takes longer than two seconds to respond to a request.
4. If the web service fails to serve 100 requests per second, move SQL Server to a separate server and update the application configuration.
5. Repeat the load test. 6. If the web application again fails to serve 100 requests per second, examine the performance data to identify the bottleneck. If the server running SQL Server is the bottleneck, add an additional machine running SQL Server and configure replication. If the web service is the bottleneck, add an additional server and configure round-robin DNS.
7. Repeat steps 4 and 5 until the application successfully serves 100 requests per second.
68
Chapter 1
Designing the Layers of a Solution
Management wants your opinion on the scalability plan. Answer the following questions:
1. Will configuring a hosted server with WCF routing and a VPN be sufficient to allow incoming connections from the Internet? What other options are there to provide Internet connectivity?
2. Will this process accurately predict whether the application will stay online? Why or why not?
3. Do you agree that SQL Server should be moved to a different physical server if the web service fails the first load test? If not, what would you do instead?
4. Do you agree that the web and database servers should be scaled out if they fail the second and subsequent load tests? If not, what would you do instead?
Objective 1.6: Design for Globalization and Localization This objective provides an overview of designing applications for an international audience. Reaching users beyond a single language and country requires two primary considerations: ■■
Displaying text in the user’s preferred language
■■
Displaying information such as dates and currencies using the user’s country’s standards
The .NET Framework includes features to handle both scenarios. In fact, once you set the user’s culture, the .NET Framework handles many aspects of globalization automatically. In some cases, however, you will need to develop a custom solution to provide flexibility and performance.
This objective covers how to:
■■
Choose between CurrentCulture and CurrentUICulture.
■■
Format text for different cultures.
■■
Translate applications.
■■
Work with global time.
■■
Compare data using globalization-friendly techniques.
■■
Design databases for globalization.
Objective 1.6: Design for Globalization and Localization
Chapter 1
69
Real World
T
here are several different ways to provide globalized content to users. Recently, I redesigned an English-only application to support eight different languages
and cultures. The application stored text content for thousands of different products in a database. First, I needed to modify the database layout to support multiple languages. I moved text content into tables that were separate from other data and added an indexed column for the language code. When a translator translated a description into a different language, the management tools added a new row to the database and specified the language code. I updated the data access layer to retrieve the appropriate row based on CurrentUICulture. Besides the primary content stored in the database, the website had static English-language content stored in labels. I updated these to retrieve values from resource files instead, and had that text translated. For labels that were populated at run time, I had to update the code to access the resource file instead.
Choosing Between CurrentCulture and CurrentUICulture The .NET Framework provides two related classes for setting a page’s culture. Both classes are members of Thread.CurrentThread and require the System.Threading namespace, as follows: ■■
■■
CurrentCulture Defines how the .NET Framework renders controls. For example, English speakers in the United States abbreviate dates using the format mm/dd/yyyy, while English speakers in Great Britain prefer the format dd/mm/yyyy. If the date is January 31, 2012, setting CurrentCulture to en-US (U.S. English) will cause DateTime .Now.ToShortDateString to render as “01/31/2012.” Setting CurrentCulture to en-GB (Great Britain English) will cause the same method to render as “12/01/2031.” CurrentUICulture Defines which language-specific resources the .NET Framework chooses when controls are linked to a resource file. Set CurrentUICulture when an application has been translated to multiple languages.
Exam Tip
The naming difference between CurrentCulture and CurrentUICulture makes them easy to confuse. After all, both can cause changes to the UI. Remember to use CurrentUICulture when you have translated text in resource files, and CurrentCulture when you want to take advantage of the .NET Framework’s built-in globalization capabilities.
The .NET Framework includes built-in cultures that suit the needs of most of the world. If none of the built-in cultures meet your needs, you can create a custom culture using the
70
Chapter 1
Designing the Layers of a Solution
ultureAndRegionInfoBuilder class. For more information, visit http://msdn.microsoft.com/ C library/system.globalization.cultureandregioninfobuilder.aspx.
Format Text for Differing Cultures After setting CurrentCulture, use String.Format() to help ensure that data is formatted properly. For example, the following code sample shows how to display a value as currency in both English (as “$1,999.99”) and French (as “1 999,99 €”). It requires the System.Threading and System.Globalization namespaces. Sample of Visual Basic.NET Code Dim money As Double = 1999.99 ' Set the UI culture to English Thread.CurrentThread.CurrentCulture = New CultureInfo("en") ' Format a double as currency using the current culture USLabel.Text = [String].Format("{0:C}", money) ' Set the UI culture to French Thread.CurrentThread.CurrentCulture = New CultureInfo("fr") ' Format a double as currency using the current culture FRLabel.Text = [String].Format("{0:C}", money) Sample of C# Code double money = 1999.99; // Set the UI culture to English Thread.CurrentThread.CurrentCulture = new CultureInfo("en"); // Format a double as currency using the current UI culture USLabel.Text = String.Format("{0:C}", money); // Set the UI culture to French Thread.CurrentThread.CurrentCulture = new CultureInfo("fr"); // Format a double as currency using the current UI culture FRLabel.Text = String.Format("{0:C}", money);
More Info Formatting Strings
For detailed information about formatting strings, read “Formatting Types” at http://msdn.microsoft.com/library/26etazsy.aspx.
If a user’s operating system has been configured with her region and language, all you need to do is format data with String.Format when populating the Text properties of Label and Literal controls.
Objective 1.6: Design for Globalization and Localization
Chapter 1
71
Plan ahead to allow controls to be formatted to different cultures as follows: ■■
■■ ■■
Use relative or automatic sizing, rather than absolute positions and fixed sizes. Relative and automatic sizing scales better to the different content lengths you will experience when the UI is translated. Leave extra space around the edges of your UI for languages that require more room. Design the UI in XAML, rather than creating it at run time. XAML automatically allows the UI to be localized.
■■
Test any controls that display text with both left-to-right and right-to-left languages.
■■
Verify that calendar controls have sufficient room when different cultures are selected.
More Info Localizing User Interfaces
For more information, read “WPF Globalization and Localization Overview” at http://msdn.microsoft.com/library/ms788718.aspx.
Translating Applications The .NET Framework provides support for using resource files to render text for controls automatically. After developing your UI, follow these steps to use resource files containing translations: 1. Add a resource dictionary to your project. 2. For every control that displays text, replace the property containing the text (such as
the Content property) with a reference to a string in the resource dictionary. Add the removed text to the resource dictionary using the same key. 3. Copy the resource dictionary with a unique file name for every language that you
plan to use. For example, you might add the two-digit language code before the file extension, creating dictionaries named ‘dictionary-es.xaml’ and ‘dictionary-fr.xaml’. 4. Have the text within the resource dictionaries translated. 5. Add code to select a different language at run time. For example, you can use the
technique described at http://www.codeproject.com/KB/WPF/Multilingual.aspx. If you currently retrieve text from a database, you will need to develop a custom solution to retrieve the proper language from the database. For best results, add the translation functions in the data access layer so that the proper language text is retrieved automatically from the database based on the CurrentUICulture setting.
Working with Time You should always work with Coordinated Universal Time (UTC), which represents time globally. Unfortunately, the standard DateTime object that most developers use only represents time for a specific time zone. Using DateTime without specifying the time zone 72
Chapter 1
Designing the Layers of a Solution
could be disastrous. For example, if you were trying to reconcile debits and credits in different financial systems in different time zones, the times would not match easily. Instead of using DateTime, use DateTimeOffset. DateTimeOffset includes a DateTime value, but it adds the Offset property that specifies the difference between the local DateTime and UTC. Basically, the Offset property tracks your time zone, allowing you to store the UTC date but display the time relative to the user’s location. If you were to display the current date and time using DateTime and DateTimeOffset, it might resemble the following: DateTime: 9/15/2011 2:50:00 PM DateTimeOffset: 9/15/2011 2:50:00 PM -06:00
Use the TimeZoneInfo class to represent a time zone and to create custom time zones. Unlike the TimeZone class (which can convert times only between the local time zone and UTC), TimeZoneInfo can represent any time zone.
Comparing Data Different locales sometimes sort data differently. While you will typically rely on a database to sort query results, you might need to specify a culture manually when sorting collections. For example, consider the following console application, which sorts three words. Notice that one of the words includes the special character Æ. In most cultures, Æ is sorted as ‘AE’. In Danish German, however, Æ is treated as a single character and is sorted after Z. Sample of Visual Basic.NET Code Dim words As New List(Of String)() With { "Apple", "Æble", "Act" } Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US") Console.WriteLine("US English Sort") words.Sort() For Each word As String In words Console.WriteLine(word) Next ' Set the UI culture to Danish German Thread.CurrentThread.CurrentCulture = New CultureInfo("da-DK") Console.WriteLine("Danish German Sort") words.Sort() For Each word As String In words Console.WriteLine(word) Next Sample of C# Code List words = new List() {"Apple", "Æble", "Act"}; Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Console.WriteLine("US English Sort"); words.Sort();
Objective 1.6: Design for Globalization and Localization
Chapter 1
73
foreach (string word in words) Console.WriteLine(word); // Set the UI culture to Danish German Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK"); Console.WriteLine("Danish German Sort"); words.Sort(); foreach (string word in words) Console.WriteLine(word);
The console application outputs the following, demonstrating that the List.Sort method uses the localized settings: US English Sort Act Æble Apple Danish German Sort Act Apple Æble
What this means to you is simple—set CurrentCulture appropriately, and the .NET Framework will take care of sorting and comparing based on the user’s localization settings. Keep in mind, however, that users in different locations might have slightly different experiences as a result. Depending on the class, you might be able to override the current culture’s settings by calling a comparison method that accepts an instance of CultureInfo.
Designing Databases for Globalization Follow these best practices for designing databases for globalization: ■■
■■
■■
74
Use variable-length field types to store translatable text instead of fixed-length field types. For example, use nchar instead of char and nvarchar instead of varchar. This allows for translations that might be longer than anticipated in your normal language. Because different locations use different orders for the date, month, and year, always use the yyyy-mm-dd format when specifying text to represent a date. Databases sort Unicode data in a predictable way. However, you can use collations to specify a localized sort order for non-Unicode data. Localized sort orders are necessary because different locales might have different rules for sorting, even when they use the same alphabet. For example, although most locales would sort the word Cheese between Cat and Cot, Spanish speakers in Mexico might expect Cheese and other words starting with Ch to appear last in the list.
Chapter 1
Designing the Layers of a Solution
More Info Globalized Database Design
For more information about designing databases for globalization, read “International Considerations for SQL Server” at http://msdn.microsoft.com/library/ms142795.aspx. For more information about collations, read “Working with Collations” at http://msdn.microsoft. com/library/ms187582.aspx.
Objective Summary ■■
■■ ■■
■■
■■
■■
CurrentCulture defines how to format and sort data. For example, the .NET Framework will format dates as mm-dd-yyyy or dd-mm-yyyy, depending on the CurrentCulture setting. CurrentUICulture defines which language to display in a multilingual UI. Use String.Format to format .NET data types using the CurrentCulture. You can create multilingual applications by storing all text in resource dictionaries and then dynamically selecting the appropriate resource dictionary at run time. Use DateTimeOffset when you need to represent a global time with the time zone. Use TimeZoneInfo to calculate the time in different time zones. Different locales can sort data differently. The .NET Framework handles this automatically if the user’s computer (and thus CurrentCulture) are configured properly. You also can set the CurrentCulture manually if the default comparisons do not meet your requirements. When designing databases for globalization, use variable-length fields for text instead of fixed-length fields. When not using strong typing to update the database, always specify dates in the format yyyy-mm-dd. Finally, be aware that databases might sort non-Unicode data in localized ways.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are developing multilingual blog software. Writers will create a new blog entry in
their native language by typing it into a WPF application that stores the blog entry in a database. Translators will then translate the blog entry into other languages, and editors will review the translation before approving it. In addition, static text information (such as the copyright details) is stored in language-specific resource files. You must choose how to display the version of a blog entry in the reader’s preferred language. What should you do? (Choose two; each answer forms part of the complete solution.) A. Set Thread.CurrentThread.CurrentUICulture. B. Set Thread.CurrentThread.CurrentCulture.
Objective 1.6: Design for Globalization and Localization
Chapter 1
75
C. Store translated blog entries in the database, and read the correct entry based on
the user’s language preference. D. Store translated blog entries in resource files. 2. You are designing an English-language application for users in several different
c ountries. The countries each have their own standards for displaying dates and currencies. How can you customize the display of information according to each country’s standards? (Choose two; each answer forms part of the complete solution.) A. Set Thread.CurrentThread.CurrentUICulture. B. Set Thread.CurrentThread.CurrentCulture. C. Use String.Format to display dates and currency. D. Use Object.ToString to display dates and currency. 3. You are designing a WPF client application for international deployment. You are
creating a class that represents a financial transaction. The class must contain the UTC time and date that the transaction occurred. It also must contain the local time at the transaction’s origin. Which class should you recommend? A. TimeZone B. TimeZoneInfo C. DateTime D. DateTimeOffset
Thought Experiment Translating an Application’s User Interface
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a developer for Contoso Pharmaceuticals. You are working with management to assess the current plans for translating a WPF application into multiple languages and then maintaining the application. The application displays information about the business’s thousands of drugs, including their usage and side effects. Because the information provides medical
safety data, the legal and medical departments must review all translated information before it is published. The developers created a database design to store the information about each drug in multiple languages. It also supports review status for translated descriptions.
76
Chapter 1
Designing the Layers of a Solution
Information not related to products, such as menu items, will be stored in resource files and manually translated when required. In addition to the translated drug information, the application must display currency and dates using local formats. Developers plan to set the CurrentCulture and CurrentUICulture values according to the user’s region. Answer the following questions about the future performance of the application:
1. Is this application the best choice to store the product information in the database? Why?
2. When displaying product information, how will the application determine which language to retrieve from the database?
3. Is the application the best choice to store menu information in resource files? Why?
4. When displaying menu information, how will the application determine which language to display?
Objective 1.6: Design for Globalization and Localization
Chapter 1
77
Chapter Summary ■■
■■
■■
■■
■■
78
The four tenets of SOA are that boundaries are explicit; services are autonomous; services share schema and contract, not class; and service compatibility is based upon policy. To provide SoC, divide your application into three layers: Presentation (the UI), Business Logic (where the bulk of code belongs), and Data (a database). When designing services, consider cohesion (how methods are grouped into services), service granularity (how many different methods are grouped into a service), and method granularity (how many different capabilities an individual method has). For Internet web services, choose a protocol based on HTTP, such as wsHttpBinding or WSDualHttpBinding. If all clients use the .NET Framework and are located on the intranet, you can choose netTcpBinding or NetMsmqBinding. Always validate data that crosses a trust boundary, such as any data sent to a web service. Services can be synchronous (two-way) or asynchronous (one-way). Stateful exchanges require the service to store data for a client and retrieve it to process subsequent requests. In a stateless exchange, the client provides all the information required to complete each request. You should update the version number anytime you change functionality in your application, but not if you release a security update or fix a bug. You can host WCF services in four different ways: self-hosting, hosting within IIS, hosting within WAS, and hosting within a Windows service. You can configure your application for UAC-elevated privileges by updating the manifest or by launching a new process at run time. To help enforce least privilege, XBAP applications run with partial trust. If your application attempts to use resources outside the .NET Framework, it will authenticate using its process identity credentials. If your application needs to connect to network resources, you will need to grant the process identity sufficient privileges. Use impersonation to access local resources with the user’s credentials and use delegation to access remote resources. Federated security systems, such as the AD DS, use tokens to authenticate and authorize users. If the classes built into the .NET Framework do not suit your needs, create new classes based on the .NET Framework classes. Use hashing to store passwords while limiting the risk that they might be compromised, and use digital signatures to prove that data has not been modified. You can expose .NET Framework assemblies so that they can be accessed from unmanaged code like COM objects. To simplify access from unmanaged code, avoid parameterized constructors and static methods, define event-source interfaces, include HRESULTS in custom exception classes, and supply GUIDS. You can access COM objects from the .NET Framework. You can simplify this process when creating COM objects by minimizing the number of calls and HRESULTS, providing a type library, typing arrays using SAFEARRAY, and creating a wrapper or PIA assembly. If a web service will need to process a request for several minutes, you will need to design a communication mechanism to return the results and design the UI so that it
Chapter 1
Designing the Layers of a Solution
remains responsive. There are two ways you can scale an application: up (by upgrading a single server) and out (by adding more servers). Cloud hosting providers v irtualize your application, simplifying the process of adding or removing servers. Queuing allows your application to process requests that it cannot respond to immediately. Chatty communications consist of many small requests, whereas chunky applications batch information into larger requests. Use a service bus to translate between different protocols and data types or to bridge between Internet and intranet communications. ■■
CurrentCulture defines how to format and sort data, whereas CurrentUICulture defines which language to display in a multilingual UI. You can create m ultilingual applications by storing all text in resource dictionaries and then dynamically selecting the appropriate resource dictionary at run time. Use DateTimeOffset when you need to represent a global time with the time zone, and use TimeZoneInfo to calculate the time in different time zones. When designing databases for globalization, use variable-length fields for text instead of fixed-length fields and be aware that databases might sort non-Unicode data in localized ways.
Chapter Summary
Chapter 1
79
Answers This section contains the answers to the Object Reviews and the Thought Experiments.
Objective 1.1: Review 1. Correct Answer: B A. Incorrect: The database server is part of the Data layer. B. Correct: In this application, the web service would need to perform all processing.
Therefore, it would be placed within the Business Logic layer. C. Incorrect: The WPF client would be part of the Presentation layer. D. Incorrect: The ASP.NET client would be part of the Presentation layer. 2. Correct Answer: B A. Incorrect: Context-based routing routes requests based on IP address and port
number. In this case, you will need to look within the message to determine which of the three new services should receive the request. Therefore, you need to use content-based routing. B. Correct: If you create a WCF content-based router, you can receive requests using the
existing application’s interface. Depending on the content of the request, such as the specific method being called, you can route the request to a back-end web service. C. Incorrect: BizTalk Server is capable of this type of routing. However, using BizTalk
Server would incur software licensing costs. D. Incorrect: SQL Server does not provide web services routing. 3. Correct Answer: A A. Correct: WebHttpBinding is based on open standards, and as such is perfect for
a loosely coupled application because you would be able to replace the client or server with code written using a different development environment. B. Incorrect: NetNamedPipesBinding is used for communications between .NET
Framework applications only. A loosely coupled application should rely on open standards. C. Incorrect: The WPF client would be part of the Presentation layer. D. Incorrect: The first tenet of SOA is that boundaries are explicit. Because calls to
other services can be expensive, they should be deliberate. By making calls directly from a data model property, developers using the data model might not be aware that they are making web service calls, and the web service calls might not be performed efficiently.
80
Chapter 1
Designing the Layers of a Solution
Objective 1.1: Thought Experiment 1. The application uses a traditional three-tier architecture that has separate Presentation,
Business Logic, and Data layers. 2. There’s nothing in the architecture that is contradictory to the four tenets of SOA.
However, fully understanding whether it follows the four tenets would require more information about the implementation than the scenario provides, such as whether services are sharing classes and whether boundaries are explicit. 3. You could use WCF routing or BizTalk Server to route existing requests to the new web
services architecture.
Objective 1.2: Review 1. Correct Answer: D A. Incorrect: NetMsmqBinding does not work through many proxy servers, which
require the use of HTTP. In addition, it is not based on open standards. B. Incorrect: NetTcpBinding does not work through many proxy servers, which
require the use of HTTP. In addition, it is not based on open standards. C. Incorrect: WsHttpBinding uses SOAP, which requires the use of XML. D. Correct: WebHttpBinding supports using REST, which meets your requirements. 2. Correct Answer: B A. Incorrect: If you were to self-host the web service, you would need to start the
WPF assembly automatically, which is difficult to do on a server. In addition, you would need to create tools to allow administrators to manage the web service. B. Correct: By hosting the web service within IIS, IIS will start the web service
utomatically whenever it is required. In addition, administrators can use IIS a management tools to configure the web service. C. Incorrect: You could host the web service within WAS. However, because it is
a REST web service, you know that it will be using the HTTP protocol. WAS is designed to be used when IIS is not suitable, such as for non-HTTP protocols. Therefore, the extra complexity of hosting within WAS is unnecessary, and you should host within IIS instead. D. Incorrect: You could host the web service within a Windows service. However, you
would need to write extra code to run the Windows service, and administrators would have fewer configuration options available. Instead, you should host the web service within IIS.
Answers
Chapter 1
81
3. Correct Answers: A and B A. Correct: You can add properties to a MessageContract class and decorate those
properties with the MessageHeader attribute. Those header properties will be transferred separately from the data itself. B. Correct: Without creating a custom message contract, you can insert headers into
SOAP messages at run time, allowing you to transfer information separate from the data itself. C. Incorrect: REST and SOAP are different web services technologies, and they
c annot be used together. In addition, you cannot create custom REST verbs; REST is limited to the verbs defined by HTTP. D. Incorrect: By adding a data member to a data contract, you would be modifying
the structure of the data, which conflicts with your requirements.
Objective 1.2: Thought Experiment 1. Yes. SOAP is open standards–based and every modern development environment
includes SOAP libraries. 2. Yes. By using WsHttpBinding, you’ll allow your web service to communicate through
firewalls and proxy servers that support HTTP. HTTP is very commonly supported because it is required for web browsing. 3. The service design might be a bit too coarsely grained because it includes tasks from
multiple unrelated business processes. On the positive side, however, this design means they need to manage only a single web service, and developers can perform any task from the single web service. On the negative side, any updates will affect all services.
Objective 1.3: Review 1. Correct Answers: A and B A. Correct: Delegation issues database requests with the user’s credentials, rather
than the application’s process identity. B. Correct: Enabling delegation without restriction would allow the application to
access any network resource using the user’s credentials. If the application was compromised, unconstrained delegation could result in the entire network being compromised. Constrained delegation limits the network resources the web service can access with the user’s credentials. C. Incorrect: The Network Service account (the most common process identity) is
a local account and cannot be added to domain groups. In addition, the Domain Admins group would provide excessive privileges, increasing security risks.
82
Chapter 1
Designing the Layers of a Solution
D. Incorrect: The System account still would not have privileges to access the
atabase. In addition, the System account has unrestricted access to the local d computer, increasing security risks. 2. Correct Answer: D A. Incorrect: If you were to encrypt the data with your private key, the client could
ecrypt it with your public key, providing non-repudiation by proving that the d web service generated the data. Because encrypted data cannot be modified meaningfully, this also would provide data integrity. However, encrypting the entire block of data would have a much greater performance impact than digitally signing the data. B. Incorrect: If you were to encrypt the data with your public key, only someone with
access to the private key would be able to decrypt it. C. Incorrect: Hashing the data would provide data integrity. However, without a
digital signature, it would be possible for an attacker to modify both the data and the hash itself. In addition, hashing does not provide non-repudiation. D. Correct: Digital signatures provide both data integrity and non-repudiation while
minimizing the performance impact. 3. Correct Answer: C A. Incorrect: Although it is technically possible to do this, it is more efficient to use
CAS to limit the privileges of a component. B. Incorrect: Although it is technically possible to do this, it is more efficient to use
CAS to limit the privileges of a component. In addition, Active Directory domain accounts can have access to remote computers. C. Correct: You can create application domains with a specific trust level and launch
components within the application domain. The component will have only the privileges you grant to the trust level. In this scenario, you should configure a custom trust level that grants the component access to create files in the local folder without having other excessive privileges. D. Incorrect: Because this is a WPF application, the component would launch with
the user’s credentials by default. Therefore, this would not limit the component’s privileges.
Objective 1.3: Thought Experiment 1. No, because malicious users still could access the associated methods directly on the
WCF web service. 2. Yes. Alternatively, you could call PrincipalPermission.Demand and catch an exception if
the user is not authorized.
Answers
Chapter 1
83
3. Although the trusted subsystem will work properly, it is not the most secure way
to a ccess the database. If the design used constrained delegation instead, the web application would access the database with each user’s credentials. This would allow database administrators to configure different authorization for users and managers, adding a layer of security. It also would allow for auditing individual user actions at the database level.
Objective 1.4: Review 1. Correct Answer: C A. Incorrect: Creating a type library improves accessibility from managed code.
However, it does not allow you to replace the COM object without updating the WPF application. B. Incorrect: While optimizing COM objects for access from managed code definitely
makes development easier, it would not allow you to replace the COM object without updating the WPF application. C. Correct: By creating a wrapper assembly that forwards requests to the COM
object, you can build your WPF application with only a reference to the wrapper assembly. Later, you can update the wrapper assembly to replace the COM object without updating the WPF application. D. Incorrect: Creating a web service would allow you to replace the COM object without
updating the WPF application. However, it requires more development than creating a wrapper assembly, and it carries unnecessary overhead for a client application. 2. Correct Answer: C A. Incorrect: You should avoid using static constructors when creating assemblies to
be accessed using COM. B. Incorrect: COM works best with empty constructors. C. Correct: COM uses type libraries. You can use Visual Studio or the RegAsm.exe
tool to automatically generate a type library. D. Incorrect: Throwing exceptions carries a great deal of overhead, which can reduce
performance.
Objective 1.4: Thought Experiment 1. No. Unfortunately, you cannot simply replace a COM object with a .NET Framework
class library. You would need to update the Win32 application to refer to the assembly instead of to the COM object. 2. Yes. They should replace the Win32 application with a .NET Framework assembly that
refers to the existing COM objects. Then, they can later replace the COM objects with assemblies. 84
Chapter 1
Designing the Layers of a Solution
Objective 1.5: Review 1. Correct Answer: A A. Correct: This approach blends server-side and client-side programming to provide
a responsive UI. The web service communicates directly with the server hosting the virtual machines because the client would not have authorization. B. Incorrect: Although this approach would work, the web service would not return
a response to the user until after all the virtual machines had been provisioned, which could take as long as 10 minutes. The lack of responsiveness would be unacceptable to most users. C. Incorrect: This approach would work only if the server hosting the virtual
achines allowed provisioning requests from the client. However, the server m hosting the virtual machines allows provisioning requests only from the web service. D. Incorrect: This approach would work. However, you should strive to place as much
logic as possible within the web service rather than at the client. This architecture does not allow the web service to determine the best way to provision multiple virtual machines. 2. Correct Answers: B and D A. Incorrect: While it is possible to provide redundant hardware within a single
server, this does not eliminate single points of failure. A power outage or failure of other server components still would cause the application to fail. B. Correct: Cloud hosting providers can provide redundancy for your application. C. Incorrect: While deploying a second server and load balancer increases
r edundancy, it does not eliminate single points of failure. In this scenario, because the datacenter has only a single Internet connection, failure of that Internet connection would bring the web application offline. D. Correct: By deploying a second server to a different data center, you would be
protected against a failure that might bring the first datacenter offline, such as an extended power outage or a failure of the non-redundant Internet connection. To provide the highest level of redundancy, choose a datacenter managed by a different hosting provider, with connections to different Internet service providers. 3. Correct Answers: B and D A. Incorrect: A synchronous request would force the application UI to wait until the
web service responds before returning control to the user, preventing her from cancelling the request. B. Correct: An asynchronous request would return control to the application
immediately after initiating the web service request, allowing the user to cancel the request. Answers
Chapter 1
85
C. Incorrect: A web service that maintains a session would keep the HTTP connection
open, placing an additional burden on the firewall. D. Correct: A sessionless web service minimizes the number of current connections,
reducing the burden on the firewall.
Objective 1.5: Thought Experiment 1. Yes. Alternatively, management could implement a service bus using a cloud service
such as Windows Azure. Another option would be to move the web service to a location where it can be accessed directly from the Internet, such as a hosting provider. 2. Probably. There are two elements that the load test is not testing: the routing service
and the Internet bandwidth. Because the load test is connected to the local network, bypassing the routing service, you are not testing whether Trey Research’s connection to the Internet provides sufficient bandwidth or whether the hosted server is fast enough. The routing service does very little processing, however, so it might never become a performance bottleneck. You could estimate the bandwidth requirements by determining the average request size and multiplying it by the number of requests per second. 3. No; it might be more cost effective to keep the single-server architecture. Instead of
immediately separating SQL Server onto a different physical server, they should examine the performance data to determine the bottleneck. If the bottleneck is the database, it might be worthwhile to attempt to optimize the database indexes and the queries issued by the application. If the bottleneck is the web service, it might be worthwhile to examine the code and verify that the application follows performance best practices. Even if they don’t want to spend developer time optimizing the code, they should verify that the web service and the database server are consuming the same resource before separating them onto different servers. For example, if web service performance is limited by processor time and the database is consuming very few processor resources, separating the two processes onto separate servers would not alleviate the bottleneck, but it would increase the software licensing and management costs. 4. Maybe. They also should estimate the costs of scaling up the application; it might be
more cost effective.
Objective 1.6: Review 1. Correct Answers: A and C A. Correct: Setting CurrentUICulture will cause .NET to read the correct language
from the language-specific resource files. B. Incorrect: Setting CurrentCulture will cause .NET to format information, including
dates and currency, using the current culture’s standards. However, the scenario does not mention that type of information. 86
Chapter 1
Designing the Layers of a Solution
C. Correct: Database entries provide the flexibility and performance required for a
blog system. D. Incorrect: Resource files should not be used for dynamic information such as blog
entries. In addition, resource files do not provide an editorial review process. 2. Correct Answers: B and C A. Incorrect: Set CurrentUICulture to configure which language-specific resources
.NET reads from the resource file. B. Correct: Set CurrentCulture to configure how information such as dates and
currencies is displayed. C. Correct: The .NET Framework uses the CurrentCulture setting to determine how to
format information rendered using String.Format. D. Incorrect: You cannot format numeric values as currency using Object.ToString.
Instead, use String.Format. 3. Correct Answer: D A. Incorrect: The TimeZone class represents the current time zone and allows you
to c onvert between UTC time and local time. However, it does not store the time itself. B. Incorrect: The TimeZoneInfo class allows you to convert between UTC time and
any time zone. However, it does not store the time itself. C. Incorrect: DateTime stores a date and time, but it does not account for the time
zone. While you could use an instance of DateTime to store the UTC time, you would need to represent the local time zone separately. D. Correct: The DateTimeOffset class stores the UTC date and time, as well as the
local time zone, fulfilling all your requirements.
Objective 1.6: Thought Experiment 1. Yes, a database is the correct choice. If the developers needed to store only a few
pieces of information, they could use resource files. However, using a database will allow them to implement content control mechanisms to ensure that translations are reviewed before being published. 2. For consistency, they should use the CurrentUICulture setting when accessing the
database. 3. Yes, resource files are the best choice for displaying static strings in multiple languages
because resource files allow efficient development and the .NET Framework provides the correct translation automatically. 4. The application will display the correct language automatically because developers are
planning to set the CurrentUICulture setting. Answers
Chapter 1
87
C h apter 2
Designing the Presentation Layer T
he Presentation layer is the layer that directly interacts with the user. The user must be able to assimilate the data presented to him quickly and easily through the Presentation layer and also must be able to input data efficiently into the application. The P resentation layer itself is responsible for validating user input, maintaining responsiveness, and providing cues that enable an easy and accessible user experience. In this chapter, you will learn some of the important aspects of designing a Presentation layer. You will learn to choose between Windows Forms and Windows Presentation Foundation (WPF) for the technology used to build the Presentation layer, to design application layout and workflow, to handle data within the Presentation layer, and to develop a well-crafted and responsive user experience.
Objectives in this chapter: ■■
Objective 2.1: Choose the appropriate Windows technology
■■
Objective 2.2: Design the UI layout and structure
■■
Objective 2.3: Design application workflow
■■
Objective 2.4: Design data presentation and input
■■
Objective 2.5: Design presentation behavior
■■
Objective 2.6: Design for UI responsiveness
Real World
T
he Presentation layer is the only layer of a distributed application that the user sees, and thus from a user standpoint is the most important. When building
a Presentation layer, I always try to know my audience and keep the needs of my
users paramount. The slickest, best-looking application is useless if it doesn’t do what the users need.
89
Objective 2.1: Choose the Appropriate Windows Technology When creating applications for the desktop, today’s developer has two technologies to choose from: Windows Forms and WPF. Each technology provides its own set of advantages and drawbacks. In this section, you will learn the primary differences between the two technologies and how to decide which is most appropriate for your business situation.
This objective covers how to: ■■
Choose between Windows Forms and WPF.
■■
Identify areas for possible interoperation between WPF and Windows Forms.
Windows Forms Windows Forms are the basis for most Microsoft Windows applications and can be configured to provide a variety of user interface (UI) options. The developer can create forms of various sizes and shapes and customize them to the user’s needs. Windows Forms is the older of the two Windows development technologies currently supported by Microsoft, and many skilled developers are available for creating and maintaining Windows Forms projects. Windows Forms are the basic building blocks of the UI. They provide a container that hosts controls and menus and allow you to present an application in a familiar and consistent fashion. Forms can receive user input in the form of keystrokes or mouse interactions and can display data to the user through hosted controls. Most applications that require sustained user interaction will include at least one Windows Form, and complex applications frequently require several forms to allow the program to execute in a consistent and logical fashion. Windows Forms have no inherent support for changing styles. Thus, if the look and feel of the application must change according to conditions on a particular client desktop, considerable coding will be required. lobalization. However, Windows Forms do provide excellent support for localization and g You can create applications easily that display alternate strings and images based on the locations of deployment. Navigation in Windows Forms typically involves switching between multiple individual forms. While this allows for parts of the application to be parceled out and presented to the user as discrete functional units, it can also create a disjointed kind of user experience. A more cohesive user experience is possible with Windows Forms using Multiple Document Interface (MDI) Forms.
WPF WPF is the successor to Windows Forms for desktop application development. WPF applications differ from traditional Windows Forms applications in several ways. The most notable of these is that the code for the UI is separate from the code for application 90
Chapter 2
Designing the Presentation Layer
f unctionality. Although the code for the functionality of a project can be defined using familiar languages such as Microsoft Visual Basic .NET or Microsoft Visual C#, the UI of a WPF project typically is defined using a relatively new declarative syntax called Extensible Application Markup Language (XAML). Three basic types of applications can be created with WPF: ■■
■■
■■
Windows applications The most similar to Windows Forms applications. Windows applications are Windows-driven and provide a user experience that is familiar to Windows users and developers alike. Multiple windows can be open at any given time, and there is no built-in sense of navigation or history. Navigation applications Provide a page-based user experience, similar to the experience of using a website. Typically, only a single page can be open at any given time, and the journal functionality keeps a record of pages visited and allows back-and-forth navigation. Unlike a website, however, a Navigation application is a compiled application that runs on your desktop computer and, like a Windows application, has full access to the resources of your computer. XAML Browser Applications (XBAPs) Similar to Navigation applications, but they are designed to run in Windows Internet Explorer. These applications can be deployed to a server or to a website and are downloaded when instantiated. Applications of this type do not have full access to a computer’s resources. XBAPs run under a partial-trust environment, and resources such as the file system and the registry are inaccessible by XBAPs.
The choice of an application type depends upon several factors, the two most important of which are user experience and application requirements. ■■
■■
User experience Determines whether you choose a Windows application or a page-based application. For a user experience that most closely resembles a traditional Windows Forms application, a Windows application is the best choice. This application type allows you to create a menu-driven, multiwindow application that combines the rich functionality of a desktop application with the rich user experience that WPF provides. For a user experience that more closely resembles a website, you should choose a page-based application. Navigation applications and XBAPs provide built-in navigational functionality that allows you to structure the application paralleling a task, such as in an Internet shopping application or a wizard. Application requirements If an application requires access to system resources that fall outside the Internet security zone, then an XBAP is not a good choice—a better choice would be a Windows application or a Navigation application. On the other hand, XBAPs allow you to deploy the application to a web server and have users start it from a hyperlink, thus making it easily accessible to a large-scale audience. If your application does not require access to system resources, an XBAP might be a good choice.
All types of WPF applications provide built-in support for changing styles and themes. Thus, if you want your application to respond to the style or theme of the desktop, it is a fairly simple matter to incorporate that functionality.
Objective 2.1: Choose the Appropriate Windows Technology
Chapter 2
91
WPF applications have good support for localization and globalization, but support for this functionality is not as built-in as it is for Windows Forms. Thus, localizing an extensive WPF application will take more time and developer resources than a similarly scoped Windows Forms application.
Choosing Between Windows Forms and WPF When choosing a technology for a client application, you must take into account several considerations. What are the skills of your developer force? Must the application be localized? What kind of support for styles and themes is needed? What sort of navigational experience is required? The following table illustrates the relative strengths of Windows Forms and WPF to help you make that decision. Table 2-1 Important Properties of the Style Class
Criterion
Windows Forms
WPF
Adoption among the developer community
Strong
Growing
Support for localization and globalization
Excellent
Fair
Support for changing styles and themes
No
Excellent
Support for a traditional windows-based client application
Yes
Yes
Support for navigation through a page-based interface
No
Yes, through WPF Navigation applications
Support for navigation through a multiple document interface (MDI application)
Yes
No
Interoperating Between Windows Forms and WPF In some cases, you might want to use WPF elements in a Windows Forms application, or W indows Forms elements in a WPF application. You can use the built-in interoperation functionality of the Microsoft .NET Framework easily to incorporate these elements as you choose.
Incorporating WPF Elements into a Windows Forms Application You might want to incorporate WPF elements into your existing Windows Forms application (for example, a user control developed using WPF that takes advantage of specialized WPF behaviors in what is otherwise a Windows Forms application). You can add preexisting WPF user controls to your Windows Forms project by using the ElementHost control. As the name implies, the ElementHost control hosts a WPF element. The most important property of ElementHost is the Child property. The Child property indicates the type of WPF control to be hosted by the ElementHost control. If the WPF control to be hosted is in a project that is a member of the solution, you can set the Child property 92
Chapter 2
Designing the Presentation Layer
in the Property Grid. Otherwise, the Child property must be set to an instance of the WPF control in code, as shown here: Sample of Visual Basic.NET Code Dim aWPFcontrol As New WPFProject.UserControl1 ElementHost1.Child = aWPFcontrol Sample of C# Code WPFProject.UserControl1 aWPFcontrol = new WPFProject.UserControl1; ElementHost1.Child = aWPFcontrol;
Some of the ambient properties of Windows Forms controls have WPF equivalents. These ambient properties are propagated to the hosted WPF controls and exposed as public properties on the ElementHost control. The ElementHost control translates each Windows Forms ambient property to its WPF equivalent. For more information, see Windows Forms and WPF Property Mapping at http://msdn.microsoft.com/library/ms751565.aspx in the online MSDN library.
Incorporating Windows Forms Elements in a WPF Application Although WPF provides a wide variety of useful controls and features, you might find that some familiar functionality that you used in Windows Forms programming is not available. Notably absent are controls such as MaskedTextBox and PropertyGrid, as well as simple dialog boxes. Fortunately, you still can use many Windows Forms controls in your WPF applications.
Using Dialog Boxes in WPF Applications Dialog boxes are one of the most notable things missing from the WPF menagerie of controls and elements. Because dialog boxes are separate UIs, however, they are relatively easy to incorporate into your WPF applications.
File Dialog Boxes The file dialog box classes, OpenFileDialog and SaveFileDialog, are components that you want to use frequently in your applications. They allow you to browse the file system and return the path to the selected file. The OpenFileDialog and SaveFileDialog classes are very similar and share most important members. Important properties of the file dialog boxes are shown in Table 2-2, and important methods are shown in Table 2-3. Table 2-2 Important Properties of the File Dialog Boxes
Property
Description
AddExtension
Gets or sets a value indicating whether the dialog box automatically adds an extension to a file name if the user omits the extension.
CheckFileExists
Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a file name that does not exist.
Objective 2.1: Choose the Appropriate Windows Technology
Chapter 2
93
Property
Description
CheckPathExists
Gets or sets a value indicating whether the dialog box displays a warning if the user specifies a path that does not exist.
CreatePrompt
Gets or sets a value indicating whether the dialog box prompts the user for permission to create a file if the user specifies a file that does not exist. Available only in SaveFileDialog.
FileName
Gets or sets a string containing the file name selected in the file dialog box.
FileNames
Gets the file names of all selected files in the dialog box. Although this member exists for both the SaveFileDialog and the OpenFileDialog classes, it is relevant only to the OpenFileDialog class because it is possible to select more than one file only in OpenFileDialog.
Filter
Gets or sets the current file name filter string, which determines the choices that appear in the Save As File Type or Files Of Type box in the dialog box.
InitialDirectory
Gets or sets the initial directory displayed by the file dialog box.
Multiselect
Gets or sets a value indicating whether the dialog box allows multiple files to be selected. Available only in OpenFileDialog.
OverwritePrompt
Gets or sets a value indicating whether the Save As dialog box displays a warning if the user specifies a file name that already exists. Available only in SaveFileDialog.
ValidateNames
Gets or sets a value indicating whether the dialog box accepts only valid Win32 file names.
Table 2-3 Important Methods of the File Dialog Boxes
Method
Description
OpenFile
Opens the selected file as a System.IO.Stream object. For OpenFileDialog objects, it opens a read-only stream. For SaveFileDialog objects, it saves a new copy of the indicated file and then opens it as a read-write stream. You need to be careful when using the SaveFileDialog.OpenFile method to keep from overwriting preexisting files of the same name.
ShowDialog
Shows the dialog box modally, thereby halting application execution until the dialog box has been closed. Returns a DialogResult.
To use a file dialog box in a WPF application, follow these steps: 1. In Solution Explorer, right-click the project name and choose Add Reference.
The Add Reference dialog box opens. 2. On the .NET tab, select System.Windows.Forms, and then click OK. 3. In code, create a new instance of the desired file dialog box, as shown here: Sample of Visual Basic Code Dim aDialog As New System.Windows.Forms.OpenFileDialog()
94
Chapter 2
Designing the Presentation Layer
Sample of C# Code System.Windows.Forms.OpenFileDialog aDialog = new System.Windows.Forms.OpenFileDialog();
4. Use the ShowDialog method to show the dialog box modally. After the dialog box is
shown, you can retrieve the file name that was selected from the FileNames property. An example is shown here: Sample of Visual Basic Code Dim aResult As System.Windows.Forms.DialogResult aResult = aDialog.ShowDialog() If aResult = System.Windows.Forms.DialogResult.OK Then ' Shows the path to the selected file MessageBox.Show(aDialog.FileName) End If Sample of C# Code System.Windows.Forms.DialogResult aResult; aResult = aDialog.ShowDialog(); if (aResult == System.Windows.Forms.DialogResult.OK) { // Shows the path to the selected file MessageBox.Show(aDialog.FileName); }
Note
It is not advisable to import the System.Windows.Forms namespace because this leads to naming conflicts with several WPF classes.
WindowsFormsHost While using dialog boxes in WPF applications is fairly straightforward, using controls is a bit more difficult. Fortunately, WPF provides an element specifically designed to ease this task, which is called WindowsFormsHost. WindowsFormsHost is a WPF element that is capable of hosting a single child element that is a Windows Forms control. The hosted Windows Forms control automatically sizes itself to the size of the WindowsFormsHost. You can use the WindowsFormsHost to create instances of Windows Forms controls declaratively, and you also can set properties on hosted Windows Forms declaratively.
Adding a Windows Forms Control to a WPF Application To use the WindowsFormsHost element in your WPF applications, first you must add to the XAML view a reference to the System.Windows.Forms.Integration namespace in the WindowsFormsIntegration assembly, as shown here. (This line has been formatted as two lines to fit on the printed page, but it should be on a single line in your XAML.)
Objective 2.1: Choose the Appropriate Windows Technology
Chapter 2
95
xmlns:my="clr-namespace:System.Windows.Forms.Integration; assembly=WindowsFormsIntegration"
If you drag a WindowsFormsHost from the Toolbox to the designer, this reference is added automatically. You also must add a reference to the System.Windows.Forms namespace, as shown here: xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Then you can create an instance of the desired Windows Forms control as a child element of a WindowsFormsHost element, as shown here:
Setting Properties of Windows Forms Controls in a WPF Application You can set properties on a hosted Windows Forms control declaratively in XAML as you would any WPF element, as shown in bold here:
Although you can set properties declaratively on a hosted Windows Forms control, some of those properties will not have any meaning. For example, properties dealing with layout, such as Anchor, Dock, Top, and Left, have no effect on the position of the Windows Forms control. This is because its container is the WindowsFormsHost and the Windows Forms control occupies the entire interior of that element. To manage layout for a hosted Windows Forms control, you should set the layout properties of the WindowsFormsHost, as highlighted in bold here:
Setting Event Handlers on Windows Forms Controls in a WPF Application Similarly, you can set event handlers declaratively in XAML, as shown in bold in the following example:
Note that events raised by Windows Forms controls are regular .NET events, not routed events, and therefore they must be handled at the source.
96
Chapter 2
Designing the Presentation Layer
Obtaining a Reference to a Hosted Windows Forms Control in Code In most cases, using simple declarative syntax with hosted Windows Forms controls is not sufficient—you have to use code to manipulate hosted Windows Forms controls. Although you can set the Name property of a hosted Windows Forms control, that name does not give you a code reference to the control. Instead, you must obtain a reference by using the WindowsFormsHost.Child property and casting it to the correct type. The following code example demonstrates how to obtain a reference to a hosted Windows Forms Button control: Sample of Visual Basic Code Dim aButton As System.Windows.Forms.Button aButton = CType(windowsFormsHost1.Child, System.Windows.Forms.Button) Sample of C# Code System.Windows.Forms.Button aButton; aButton = (System.Windows.Forms.Button)windowsFormsHost1.Child;
Choosing a Presentation Pattern A presentation pattern separates the UI from its behavior and state. Compared to a traditional three-layer architecture, this results in separating the Presentation layer code from the Business Logic and Data code. Using a presentation pattern makes your code easier to maintain and test. Perhaps most important, it allows you to replace or extend the UI easily. You certainly can develop a WPF or Windows Forms application without using a resentation pattern; that is exactly what Windows developers have been doing for most p of the time Windows has existed. In fact, for a simple “Hello, world” application, using a presentation pattern would increase the complexity and decrease the code readability. When you create more complex enterprise applications that need to be tested and maintained, using a presentation pattern can reduce development costs greatly over time. For .NET Framework applications, you can choose from three different presentation patterns: ■■
■■
Model-View-Controller (MVC) Designed for web applications, this model uses views that consist of HTML and .NET Framework code to create the UI by formatting and displaying data from the model. The model stores the data and interacts with the database. The Controllers process requests and user input. Model-View-ViewModel (M-V-VM) Designed for WPF applications, this model closely resembles the MVC model. However, this model uses views that consist of XAML code to create the UI. The ViewModel is an abstraction of the view; it resides between the model and the view to present the model in a way that more closely resembles how the user will see it (thus reducing the amount of code required in the view itself). Views bind to the data they display by specifying the ViewModel as their DataContext, and views send data to the ViewModel using Commands that typically execute a ViewModel method.
Objective 2.1: Choose the Appropriate Windows Technology
Chapter 2
97
■■
Model-View-Presenter (MVP) Designed for WPF applications, this model closely resembles both M-V-VM and MVC. The user interacts with the view, and the view raises events that the presenter responds to. The presenter interacts with the model and updates the values shown in the view.
Figure 2-1 visually compares the three architectures. In particular, notice that the user interacts solely with the controller in the MVC architecture, and then the view collects information from the controller about the user’s request and retrieves data directly from the model. In the M-V-VM and MVP models, the user interacts directly with the view, and the ViewModel or presenter communicates with the model to prepare the data for the view. Currently, MVC is the best choice for web applications. If you are creating a WPF application, you can choose either M-V-VM or MVP. The key difference between M-V-VM and MVP is the way the ViewModel/presenter c onnects to the view. As illustrated by the directions of the arrows in Figure 2-1, this relationship is one-way for M-V-VM and two-way for MVP. Therefore, the M-V-VM ViewModel is loosely coupled with the view, whereas the MVP presenter is tightly coupled to the view. To clarify, with the MVP model, the presenter needs a reference to the view because the presenter is responsible for manipulating the state of the view. With the M-V-VM model, the ViewModel is completely unaware that the view exists. With M-V-VM, the view sets a ViewModel as its DataContext and binds to properties on the ViewModel. Any changes to values in the ViewModel are reflected automatically on the view through that binding. While the M-V-VM and MVP presentation patterns are very similar, M-V-VM is specialized to simplify WPF and Microsoft Silverlight development. Their structure is very similar, but there is simply better support for M-V-VM than for MVP. Therefore, M-V-VM is the best choice for WPF and Silverlight applications. MVC
M-V-VM
MVP
User
User
User
View
View
ViewModel
Presenter
Model
Model
Database
Database
Controller
View
Model
Database
Figure 2-1 The three .NET Framework presentation patterns
98
Chapter 2
Designing the Presentation Layer
More Info Understanding Presentation Patterns
For more information, read “WPF Apps With The Model-View-ViewModel Design Pattern” at http://msdn.microsoft.com/magazine/dd419663.aspx.
Objective Summary ■■
■■
■■
Windows Forms is a well-known technology that has a host of developers for development projects and provides excellent support for globalization and localization. However, it is not as good for changing styles, and it has no inherent navigation capability. WPF is a relatively new technology with very powerful style and navigation features. However, because the technology is fairly new, it has not been adopted by as many developers, and support for globalization and localization is not as good as for Windows Forms. Interoperability between WPF and Windows Forms is possible through the ElementHost and WindowsFormsHost controls.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of the chapter. 1. What class allows you to host a WPF control in a Windows Forms application? A. WindowsFormsHost B. ElementHost C. Grid D. Form 2. You are designing a Presentation layer for an application. You want this application to
be responsive to changes in the style of the desktop so that it blends seamlessly with the appearance of the desktop, and you need this application to collect a variety of specially formed data via the MaskedTextBox control. What is the best development strategy for your Presentation layer? A. Build using Windows Forms. B. Build using WPF. C. Build using Windows Forms that incorporate WPF controls. D. Build using WPF and incorporate Windows Forms controls.
Objective 2.1: Choose the Appropriate Windows Technology
Chapter 2
99
Thought Experiment Designing the Presentation Layer
I
n the following thought experiment, design a Presentation layer for an application that will be distributed throughout the United States, Great Britain,
and the English-speaking parts of Canada. This application is part of a large-scale psychology study. The application will stream videos in the UI and collect user input responses to the video stream in real time. Furthermore, processing behind the scenes in response to the user’s real-time input changes the look and feel of the application in accordance with the user’s perceived mood. The input collected from the user will be stored locally and uploaded to a data tier after the user has completed the test. You can find answers to these questions in the “Answers” section at the end of this chapter.
1. What technology, Windows Forms or WPF, should be used to implement this application and why?
2. How can we maintain responsiveness in the UI while performing processing in the background?
Objective 2.2: Design the UI Layout and Structure The layout of an application can mean the difference between an application that is easy to use and efficient and an application that is complicated and frustrates the user. Careful evaluation of the application design is vital to serving the needs of your users. In this section, you will learn how to make decisions that are important to the design of the layout.
This objective covers how to: ■■
Evaluate the conceptual design.
■■
Design for inheritance and the reuse of visual elements.
■■
Design for accessibility.
■■
Decide when custom controls are required.
Evaluate the Conceptual Design The design of your UI should fulfill the needs of the application, the client, and the user. The interface should enable the user to complete tasks in the application quickly and easily and without distraction. A good UI will be internally and externally consistent and allow the user 100
Chapter 2
Designing the Presentation Layer
to learn how to use the application intuitively. In their book Software for Use: A Practical Guide to the Models and Methods of Usage-Centered Design, authors Larry Constantine and Lucy Lockwood describe the following principles for UI design: ■■
■■
■■
■■
■■
■■
Design for structure A well-structured UI is logical, consistent, and intuitive. Use a model when designing your UI. Put elements with related functionality together in logically organized groups. Functional groups should be easily recognizable by the user, and dissimilar items or functional groups should be easily differentiated. A well-structured UI will not have users hunting blindly for desired functionality but will allow them to find what they need intuitively. Design for simplicity A well-designed UI is simple and easy to use. Common tasks are easy to access, and navigation to more infrequently used tasks is uncomplicated and intuitive. The use of menu items for common tasks is an example of incorporating simplicity into a UI. Design for visibility A good UI will have necessary functional units easily visible and accessible to the user. It is good to keep simplicity in mind with this principle, however, as an overly visible UI is likely to be cluttered, presenting the user with too many options and thwarting the intent of being easy to use. Design for feedback A good UI will keep the user informed as to changes in the state of the application. Errors that are relevant to the user or actions that need to be performed by the user should be communicated clearly and concisely. Conversely, the internal state of the application, if it does not require user input, should not be unnecessarily exposed. Design for tolerance Users make mistakes. A well-designed UI will allow the user to recover quickly and easily from mistakes. User interfaces should allow for faulty input to be easily and rapidly corrected and for mistaken changes to be rolled back. Design for reuse A UI should be consistent. Components and visual styles should be reused whenever possible. Not only does this reduce the amount of time spent coding but it also helps your UI to be consistent and purposeful.
Designing for Inheritance and the Reuse of Visual Elements Both Windows Forms and WPF allow you to extend and reuse controls and forms in your application. Windows Forms allows extending existing controls and forms through visual inheritance, and WPF takes it further by allowing the visual appearance and behavior of every WPF element to be modified through the use of styles. WPF further allows the reuse of components designated as resources.
Creating Extended Controls in Windows Forms Extended controls are user-created controls that extend a preexisting .NET Framework control. By extending existing controls, you can retain all the functionality of the control but add properties and methods and, in some cases, alter the rendered appearance of the control.
Objective 2.2: Design the UI Layout and Structure
Chapter 2
101
Extending a Control You can create an extended control by creating a class that inherits the control in question. The following example demonstrates how to create a control that inherits the Button class: Sample of Visual Basic.NET Code Public Class ExtendedButton Inherits System.Windows.Forms.Button End Class Sample of C# Code public class ExtendedButton : System.Windows.Forms.Button {}
The ExtendedButton class created in the previous example has the same appearance, behavior, and properties as the Button class, but now you can extend this functionality by adding custom properties or methods. You also can override existing methods to incorporate custom functionality.
Extending a Form To create a cohesive look and feel to your application, you might want to start with a base form that all other forms in the application derive from. You can extend preexisting forms in the same way that you extend controls. The following example shows how to inherit from a previously created form named BaseForm: Sample of Visual Basic.NET Code Public Class ExtendedForm Inherits MyProject.BaseForm ' Implementation omitted End Class Sample of C# Code public class ExtendedForm : MyProject.BaseForm { // Implementation omitted }
Creating Custom Dialog Boxes Dialog boxes are commonly used to gather information from the application user. Microsoft Visual Studio provides prebuilt dialog boxes that enable the user to select a file, font, or color. You can create custom dialog boxes to collect specialized information from the user. For example, you might create a dialog box that collects user information and relays it to the main form of the application. A dialog box generally includes an OK button, a Cancel button, and whatever controls are required to gather information from the user. The general behavior of an OK button is to accept the information provided by the user and then to close the form, returning a result
102
Chapter 2
Designing the Presentation Layer
of DialogResult.OK. The general behavior of the Cancel button is to reject the user input and close the form, returning a result of DialogResult.Cancel. A modal dialog box is a dialog box that pauses program execution until the dialog box is closed. Conversely, a modeless dialog box allows application execution to continue. You can display any form as a modal dialog box by calling the ShowDialog method.
Using Styles in WPF Styles can be thought of as analogous to cascading style sheets as used in HTML pages. Styles basically tell the Presentation layer to substitute a new visual appearance for the standard one. They allow you to make changes easily to the UI as a whole and to provide a consistent look and feel for your application in a variety of situations. Styles enable you to set properties and hook up events on UI elements through the application of those styles. Further, you can create visual elements that respond dynamically to property changes through the application of triggers, which listen for a property change and then apply style changes in response. The primary class in the application of styles is, unsurprisingly, the Style class. The Style class contains information about styling a group of properties. A Style can be created to apply to a single instance of an element, to all instances of an element type, or across multiple types. The important properties of the Style class are shown in Table 2-4. Table 2-4 Important Properties of the Style Class
Property
Description
BasedOn
Indicates another style that this style is based on. This property is useful for creating inherited styles.
Resources
Contains a collection of local resources used by the style.
Setters
Contains a collection of Setter or EventSetter objects. These are used to set properties or events on an element as part of a style.
TargetType
This property identifies the intended element type for the style.
Triggers
Contains a collection of Trigger objects and related objects that allow you to designate a change in the UI in response to changes in properties.
The basic skeleton of a element in XAML markup looks like the following:
Objective 2.2: Design the UI Layout and Structure
Chapter 2
103
Setters
The most common class you will use in the construction of styles is the Setter. As their name implies, Setters are responsible for setting some aspect of an element. Setters come in two flavors: property setters (or just Setters, as they are called in markup), which set values for properties; and event setters, which set handlers for events. Property Setters
Property setters, represented by the tag in XAML, allow you to set properties of roperty elements to specific values. A property setter has two important properties: the P property, which designates the property that is to be set by the Setter, and the Value property, which indicates the value to which the property is to be set. The following example demonstrates a Setter that sets the Background property of a Button element to Red:
The value for Property must take the following form: Element.PropertyName
If you want to create a style that sets a property on multiple different types of elements, you could set the style on a common class that the elements inherit, as shown here:
This style sets the Background property of all elements that inherit from the Control to which it is applied. Event Setters
Event setters (represented by the tag) are similar to property setters, but they set event handlers rather than property values. The two important properties for an EventSetter are the Event property, which specifies the event for which the handler is being set; and the Handler property, which specifies the event handler to attach to that event. An example is shown here:
The value of the Handler property must specify an extant event handler with the correct signature for the type of event with which it is connected. Similar to property setters, the format for the Event property is ., where the element type is specified, followed by the event name.
Using Resources in WPF Logical resources allow you to define objects in XAML that are not part of the visual tree but are available for use by WPF elements in your UI. Elements in your UI can access the resource as needed. An example of an object that you might define as a resource is a Brush used to provide a common color scheme for the application. 104
Chapter 2
Designing the Presentation Layer
By defining objects that are used by several elements in a Resources section, you gain a few advantages over defining the object each time you use it. First, you gain reusability because you define your object only once rather than multiple times. You also gain flexibility—by separating the objects used by your UI from the UI itself, you can refactor parts of the UI without having to redesign it completely. For example, you might use different collections of resources for different cultures in localization or for different application conditions. Any type of object can be defined as a resource. Every WPF element defines a Resources collection, which can be used to define objects that are available to that element and the elements in its visual tree. Although it is most common to define resources in the Resources collection of the Window, you can define a resource in any element’s Resources collection and access it, so long as the accessing element is part of the defining element’s visual tree.
Declaring a Logical Resource You declare a logical resource by adding it to a Resources collection, as seen here:
If you don’t intend a resource to be available to the entire Window, you can define it in the Resources collection of an element in the Window, as shown in this example:
The usefulness of this is somewhat limited, and the most common scenario is to define r esources in the Window.Resources collection. One point to remember is that when using static resources, you must define the resource in the XAML code before you refer to it. Static and dynamic resources are explained later in this section. Every object declared as a Resource must set the x:Key property. This is the name that will be used by other WPF elements to access the resource. There is one exception to this rule: Style objects that set the TargetType property do not need to set the x:Key property explicitly because it is set implicitly, behind the scenes. In the previous two examples, the key is set to myBrush. The x:Key property does not have to be unique in the application, but it must be unique in the Resources collection where it is defined. Thus, you could define one resource in the Grid.
Objective 2.2: Design the UI Layout and Structure
Chapter 2
105
Resources collection with a key of myBrush and another in the Window.Resources collection with the same key. Objects within the visual tree of the Grid that refer to a resource with the key myBrush refer to the object defined in the Grid.Resources collection, and objects that are not in the visual tree of the Grid but are within the visual tree of the Window refer to the object defined in the Window.Resources collection.
Application Resources In addition to defining resources at the level of the element or Window, you can define resources that are accessible by all objects in a particular application. You can create an application resource by opening the App.xaml file (for C# projects) or the Application.xaml file (for Visual Basic projects) and adding the resource to the Application .Resources collection, as shown in bold here:
Accessing a Resource in XAML You can access a resource in XAML by using the following syntax: {StaticResource myBrush}
In this example, the markup declares that a static resource with the key myBrush is accessed. Because this resource is a Brush object, you can plug that markup into any place that expects a Brush object. This example demonstrates how to use a resource in the context of a WPF element:
When a resource is referred to in XAML, the Resources collection of the declaring object first is searched for a resource with a matching key. If one is not found, the Resources collection of that element’s parent is searched, and so on, up to the Window that hosts the element and to the application Resources collection.
Static and Dynamic Resources In addition to the syntax described previously, you can refer to a resource with the following syntax: {DynamicResource myBrush}
106
Chapter 2
Designing the Presentation Layer
The difference between the syntax of DynamicResource and the syntax of StaticResource lies in how the resources are retrieved by the referring elements. Resources referred to by the StaticResource syntax are retrieved once by the referring element and used for the lifetime of the resource. Resources referred to with the DynamicResource syntax, on the other hand, are acquired every time the referred object is used. It might seem intuitive to think that if you use the StaticResource syntax, the referring object does not reflect changes to the underlying resource, but this is not necessarily the case. WPF objects that implement dependency properties automatically incorporate change notification, and changes made to the properties of the resource are picked up by any objects using that resource. Take the following example:
This code renders the Grid in the Window with a Blue background. If the color property of the SolidColorBrush object defined in the Window.Resources collection were changed in code to Red, for instance, the background of the Grid would render as Red because change n otification would notify all objects using that resource that the property had changed. The difference between static and dynamic resources comes when the underlying object changes. If the Brush defined in the Windows.Resources collection were accessed in code and set to a different object instance, the Grid in the previous example would not detect this change. However, if the Grid used the following markup, the change of the object would be detected and the Grid would render the background with the new Brush:
Accessing resources in code is discussed in the not found “Retrieving Resources in Code” section later in this chapter. The downside of using dynamic resources is that they tend to decrease application erformance. Because the resources are retrieved every time they are used, dynamic p resources can reduce the efficiency of an application. The best practice is to use static resources unless there is a specific reason for using a dynamic resource. Examples of when you would want to use a dynamic resource include when you use the SystemBrushes, SystemFonts, and SystemParameters classes as resources, or any other time when you expect the underlying object of the resource to change.
Objective 2.2: Design the UI Layout and Structure
Chapter 2
107
Creating a Resource Dictionary A resource dictionary is a collection of resources that reside in a separate XAML file and can be imported into your application. They can be useful for organizing your resources in a single place or for sharing resources between multiple projects in a single solution. The following procedure describes how to create a new resource dictionary in your application: To create a resource dictionary 1. From the Project menu, choose Add Resource Dictionary. The Add New Item dialog
box opens. Choose the name for the resource dictionary and click Add. The new resource dictionary is opened in XAML view. 2. Add resources to the new resource dictionary in XAML view. You can add resources to
the file in XAML view, as shown in bold here:
Merging Resource Dictionaries For objects in your application to access resources in a resource dictionary, you must merge the resource dictionary file with a Resources collection that is accessible in your application, such as the Windows.Resources or Application.Resources collection. You merge resource dictionaries by adding a reference to your resource dictionary file in the R esourceDictionary. MergedDictionaries collection. The following example demonstrates how to merge the resources in a Windows.Resources collection with the resources in resource dictionary files named Dictionary1.xaml and Dictionary2.xaml:
Note that if you define additional resources in your Resources collection, they must be defined within the bounds of the tags.
Choosing Where to Store a Resource You have seen several options regarding where resources should be stored. The factors that should be weighed when deciding where to store a resource include ease of accessibility by referring elements, readability and maintainability of the code, and reusability.
108
Chapter 2
Designing the Presentation Layer
For resources to be accessed by all elements in an application, you should store resources in the Application.Resources collection. The Window.Resources collection makes resources available only to elements in that Window, but that is typically sufficient for most purposes. If you need to share individual resources over multiple projects in a solution, your best choice is to store your resources in a resource dictionary that can be shared among different projects. Readability is important for enabling maintenance of your code by other developers. The best choice for readability is to store resources in the Window.Resources collection because this allows developers to read your code in a single file rather than having to refer to other code files. If making your resources reusable is important, then the ideal method for storing them is to use a resource dictionary. This allows you to reuse resources among different projects and to extract those resources easily for use in other solutions as well.
Designing for Accessibility The workforce contains a significant number of people with accessibility requirements, requiring applications that meet the broad demands of today’s business environment. Accessible applications begin in the design phase. When you plan for accessibility in application design, you can integrate the principles of accessibility into the UI. Some of these principles are: ■■
Flexibility
■■
Choice of input and output methods
■■
Consistency
■■
Compatibility with accessibility aids
An accessible program requires flexibility. Users must be able to customize the UI to suit their specific needs—for example, by increasing font sizes. A user also should have a choice of input methods, such as keyboard and mouse devices. That is, the application should provide keyboard access for all important features and mouse access for all main features. A choice of output methods also renders an application more accessible, and the user should have the ability to choose among visual cues, sounds, text, and graphics. An accessible application should interact within its own operation and with other applications in a consistent manner, and it should be compatible with existing accessibility aids.
Support Standard System Settings For your application to be accessible, it must support standard system settings for size, color, font, and input. Adopting this measure will ensure that all users' applications have a consistent UI that conforms to the system settings. Users with accessibility needs can thus configure all their applications by configuring their system settings. You can implement standard system settings in your application by using the classes that represent the UI options used by the system. Table 2-5 lists the classes that expose the system settings. These classes are found in the System.Drawing namespace.
Objective 2.2: Design the UI Layout and Structure
Chapter 2
109
Table 2-5 Classes That Expose System Settings
Class
Description
SystemBrushes
Exposes Brush objects that can be used to paint in the system colors
SystemColors
Exposes the system colors
SystemFonts
Exposes the fonts used by the system
SystemIcons
Exposes the icons used by the system
SystemPens
Exposes Pen objects that can be used to draw in the system colors
These classes monitor changes to the system settings and adjust correspondingly. For example, if you build an application that uses the SystemFonts class to determine all the fonts, the fonts in the application will be reset automatically when the system settings are changed.
Ensure Compatibility with the High-Contrast Option The high-contrast option (which users can set themselves in the Control Panel) sets the Windows color scheme to provide the highest possible level of contrast in the UI. This option is useful for users requiring a high degree of legibility. By using only system colors and fonts, you can ensure that your application is compatible with the high-contrast settings. You also should avoid the use of background images because they tend to reduce contrast in an application.
Provide Documented Keyboard Access to All Features Your application should provide keyboard access for all features and comprehensive documentation that describes this access. Shortcut keys for controls and menu items, as well as setting the Tab order for controls on the UI, allow you to implement keyboard navigation in your UI. Documentation of these features is likewise important. A user must have some means of discovering keyboard access to features, whether that is through UI cues or actual documentation.
Provide Notification of the Keyboard Focus Location The location of the keyboard focus is used by accessibility aids such as Magnifier and Narrator. Thus, it is important that the application and the user have a clear understanding of where the keyboard focus is at all times. For most purposes the .NET Framework provides this functionality, but when designing your program flow, you should incorporate code to set the focus to the first control on a form when the form is initially displayed and the Tab order should follow the logical program flow.
Convey No Information by Sound Alone Although sound is an important cue for many users, an application should never rely on conveying information by using sound alone. When using sound to convey information, you should combine that with a visual notification, such as flashing the form or displaying a message box.
110
Chapter 2
Designing the Presentation Layer
Accessibility Properties of Windows Forms Controls In addition to properties that affect the visual interface of a control, Windows Forms controls have five properties related to accessibility that determine how the control interacts with accessibility aids. These properties are summarized in Table 2-6. Table 2-6 Accessibility Properties of Windows Controls
Property
Description
AccessibleDefaultActionDescription
Contains a description of the default action of a control. This property cannot be set at design time and must be set in code.
AccessibleDescription
Contains the description that is reported to accessibility aids.
AccessibleName
Contains the name that is reported to accessibility aids.
AccessibleRole
Contains the role that is reported to accessibility aids. This value is a member of the AccessibleRole enumeration, and a variety of accessibility aids use it to determine what kind of UI element an object is.
AccessibilityObject
Contains an instance of AccessibleObject, which provides information about the control to usability aids. This property is read-only and set by the esigner. d
These properties provide information to accessibility aids about the role of the control in the application. Accessibility aids then can present this information to the user or make decisions about how to display the control.
Deciding When Custom Controls Are Needed User controls, custom controls, and templates all allow you to create custom elements with custom appearances. Because each of these methods is so powerful, you might be confused about what technique to use when creating a custom element for your application. The key to making the right decision isn’t based on the appearance that you want to create, but rather the functionality that you want to incorporate into your application. The standard WPF controls provide a great deal of built-in functionality. If the functionality of one of the preset controls, such as a progress bar or a slider, matches the functionality that you want to incorporate, then you should create a new template for that preexisting control to achieve your visual appearance goals. Creating a new template is the lightest solution to creating a custom element, and you should consider that option first. If the functionality that you want to incorporate into your application can be achieved through a combination of preexisting controls and code, you should consider creating a user control. User controls allow you to bind together multiple preexisting controls in a single interface and add code that determines how they behave.
Objective 2.2: Design the UI Layout and Structure
Chapter 2
111
If no preexisting control or combination of controls can approach the functionality that you want to create, then you should create a custom control. Custom controls allow you to create a completely new template that defines the visual representation of the control and add completely custom code that determines the control’s functionality.
Objective Summary ■■
■■
■■
■■
The conceptual design should be evaluated for structure, simplicity, visibility, feedback, tolerance, and reuse. Whenever possible, code should be inherited and reused. You can extend controls and forms in Windows Forms applications and reuse styles and resources in WPF. When designing for accessibility, you should support standard system settings, ensure compatibility with the high-contrast option, provide documented keyboard access to all features, provide notification of the keyboard focus location, and convey no information by sound alone. User controls should be employed when you want to bind multiple preexisting controls into a single functional unit. Custom controls should be used when no preexisting control or controls incorporate the functionality you desire.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of the chapter. 1. Which of the following is not a best practice for designing for accessibility? A. Provide audio for all important information. B. Support standard system settings. C. Ensure compatibility with the high-contrast mode. D. Provide keyboard access to all important functionalities. 2. You have created a series of customized Brush objects that will be used to create a
common color scheme for every window in each of several applications in your company. The Brush objects have been implemented as resources. What is the best place to define these resources? A. In the Resources collection of each control that needs them B. In the Resources collection of each Window that needs them C. In the Application.Resources collection D. In a separate resource dictionary
112
Chapter 2
Designing the Presentation Layer
Thought Experiment Customizing the Appearance of an Application
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Adventure Works. Adventure Works is designing the UI layout and structure for a new WPF application. Adventure Works wants the application to match the organization’s style by using its colors and typography. Adventure Works has created what they call an Appearance Standards list. This list includes a set of property values that developers must set for any UI elements and window backgrounds. These property values define the colors and shapes of those UI elements so that they are consistent with the organization’s standards. To ensure that developers follow these standards, Adventure Works has updated its testing process to verify individual UI properties. Answer the following questions about the performance of the application:
1. Are their appearance standards the best way to maintain a common style? 2. Will the application be accessible to visually impaired users? If not, what would you do differently?
Objective 2.3: Design Application Workflow Some applications require no directed workflow or have very simplistic workflow requirements. Other applications can be very directional or even branching in the workflow design. This section describes how to implement user navigation in Windows Forms and WPF applications.
This objective covers how to:
■■
Implement user navigation.
■■
Create navigation applications in WPF.
■■
Handle navigation events.
■■
Use the Hyperlink, NavigationService, PageFunction, and Journal objects.
■■
Host pages in frames.
Objective 2.3: Design Application Workflow
Chapter 2
113
Implementing User Navigation Simple client interfaces can require no more than a single form, but for more complex interfaces a navigable user experience is frequently required. Both Windows Forms and WPF allow you to incorporate user navigation into your applications.
MDI Forms in Windows Forms MDI applications follow a parent form/child form model. An MDI application generally has a single parent form that contains and organizes multiple child forms (although it is possible for an application to have multiple parent forms). Microsoft Excel is an example of an MDI application—you can open multiple documents and work with them separately within the parent form. The parent form organizes and arranges all the child documents that are currently open.
Creating an MDI Parent Form The parent form is the main form of any MDI application. This form contains all child forms that the user interacts with and handles the layout and organization of the child forms as well. It is a simple task to create an MDI parent form in Visual Studio. To create an MDI parent form 1. Create a new Windows Forms application. 2. In the Properties window for the startup form, set the IsMDIContainer property to True.
This designates the form as an MDI parent form.
Creating MDI Child Forms MDI child forms are at the center of user interaction in MDI applications. They present the data to the user and generally contain individual documents. Child forms are contained within, and are managed by, a parent form. You can create an MDI child form by setting the MdiParent property of the form. To create an MDI child form 1. Create an MDI parent form, as described previously. 2. In Visual Studio, add a second form to the project and add controls to implement the
UI. This is the child form. 3. In a method in the parent form, such as a menu item Click event handler, create a new
instance of the child form and set its MdiParent property, as shown in the following example: Sample of Visual Basic.NET code ' This example takes place in a method in the parent form, and ' assumes a Form called ChildForm Dim aChildForm As New ChildForm
114
Chapter 2
Designing the Presentation Layer
' Sets the MdiParent property to the parent form aChildForm.MdiParent = Me aChildForm.Show Sample of C# code // This example takes place in a method in the parent form, and // assumes a Form called ChildForm ChildForm aChildForm = new ChildForm(); // Sets the MdiParent property to the parent form aChildForm.MdiParent = this; aChildForm.Show();
Identifying the Active Child Form At times, you will want to identify the active child form in an MDI application. For example, a common feature of MDI applications is a central menu on the parent form that contains commands that act upon the child form that has the focus. You can use the ActiveMDIChild property of the parent form to obtain a reference to the form that was last accessed. The following code example demonstrates how to obtain a reference to the active child form: Sample of Visual Basic.NET Code ' This example demonstrates how to obtain a reference to the active child ' form from a method inside the parent form Dim aForm As Form aForm = Me.ActiveMDIChild Sample of C# Code // This example demonstrates how to obtain a reference to the active child // form from a method inside the parent form Form aForm; aForm = this.ActiveMDIChild;
Sending Data to the Active Child Form from the Clipboard Once you have identified the active MDI form, you can use the properties of the form to send data from the Clipboard to an active control on the form. You might use this functionality to implement a Paste menu item to paste data from the Clipboard into a control. The following code example demonstrates how to determine if the active control is a text box and paste text from the Clipboard into the text box: Sample of Visual Basic.NET Code Dim activeForm As Form = Me.ActiveMDIChild ' Checks to see if an active form exists If Not activeForm Is Nothing Then If activeForm.ActiveControl.GetType Is GetType(TextBox) Then Dim aTextBox As TextBox = CType(activeForm.ActiveControl, TextBox) ' Creates a new instance of the DataObject interface. Dim data As IDataObject = Clipboard.GetDataObject() ' Checks to see of the data in the data object is text. If it is, ' the text of the active Textbox is set to the text in the clipboard.
Objective 2.3: Design Application Workflow
Chapter 2
115
If data.GetDataPresent(DataFormats.Text) Then aTextBox.Text = data.GetData(DataFormats.Text).ToString() End If End If End If Sample of C# Code Form activeForm = this.ActiveMDIChild; // Checks to see if an active form exists if (activeForm != null) { if (activeForm.ActiveControl.GetType() is TextBox) { TextBox aTextBox = (TextBox)activeForm.ActiveControl; // Creates a new instance of the DataObject interface. IDataObject data = Clipboard.GetDataObject(); // Checks to see of the data in the data object is text. If it is, // the text of the active Textbox is set to the text in the clipboard. if (data.GetDataPresent(DataFormats.Text)) { aTextBox.Text = data.GetData(DataFormats.Text).ToString(); } } }
Arranging MDI Child Forms You will commonly want to organize the forms in an MDI application so that they are ordered. The MDI parent form can arrange the child forms that it contains by calling the LayoutMdi method. The LayoutMdi method takes a parameter that is a member of the MdiLayout enumeration. This method causes the forms contained by the parent form to be arranged in the manner specified by the parameter. The members of the MdiLayout enumeration are described in Table 2-7. Table 2-7 MdiLayout Enumeration Members
116
Member
Description
ArrangeIcons
All MDI child icons are arranged within the client region of the MDI parent form.
Cascade
All MDI child windows are cascaded within the client region of the MDI parent form.
TileHorizontal
All MDI child windows are tiled horizontally within the client region of the MDI parent form.
TileVertical
All MDI child windows are tiled vertically within the client region of the MDI parent form.
Chapter 2
Designing the Presentation Layer
The following example demonstrates the LayoutMdi method by causing the contained forms to cascade in the parent form: Sample of Visual Basic.NET Code ' Causes the contained forms to cascade in the parent form Me.LayoutMdi(System.Windows.Forms.MdiLayout.Cascade) Sample of C# Code // Causes the contained forms to cascade in the parent form this.LayoutMdi(System.Windows.Forms.MdiLayout.Cascade);
Navigation Applications in WPF Unlike Windows applications, which are based on the WPF Window object and are toolbar-driven and menu-driven, page-based applications are based on the WPF Page object and are navigation-driven, meaning that the flow of the program is driven by navigating through multiple pages rather than interacting with existing windows by using menu and t oolbar commands. Although the page-based model is limited in some ways, it lends itself very well to lightweight applications that are focused around a single task, such as a wizard or a shopping cart application. This section will focus primarily on the navigation of page-based applications.
Using Hyperlinks The most familiar method of page-based navigation is by using hyperlinks. Hyperlinks are displayed as a section of text, usually underlined and in a different color than the surrounding text, which the user can click. When the user clicks a hyperlink, the application navigates to the page indicated by the hyperlink. Hyperlinks expose a property called NavigateUri that indicates the target of the hyperlink. You set the NavigateUri property in XAML to indicate the navigation target when the hyperlink is clicked, as shown here: This is a hyperlink
Hyperlinks are not controls themselves—rather, they are inline flow elements. That means they must be placed within another element that supports inline flow elements, such as a TextBlock element. When a hyperlink pointing to another XAML page is clicked, a new instance of that page is created and the application navigates to that page. A hyperlink also can point to a PageFunction, but it is not possible to return a value from a PageFunction using a hyperlink. PageFunction objects are discussed in greater detail later in this section. In addition to linking to other WPF pages, hyperlinks can link your application to ebpages. You can link an application to a webpage by supplying the Hypertext Transfer w Protocol (HTTP) address in the NavigateUri property, as shown here: This is a hyperlink
Objective 2.3: Design Application Workflow
Chapter 2
117
You can set the NavigateUri property of a hyperlink dynamically in code. This allows you to change the navigation target of a hyperlink in response to program conditions. To change the NavigateURI property dynamically, you must set the Name property of the hyperlink in XAML, as shown here in bold: This is a hyperlink
Then you can set the NavigateUri property in code, as shown here: Sample of Visual Basic.NET Code myLink.NavigateUri = New System.Uri("Page2.xaml", System.UriKind.Relative) Sample of C# Code myLink.NavigateUri = new System.Uri("Page2.xaml", System.UriKind.Relative);
Using NavigationService Hyperlinks provide a fairly easy way to navigate between pages, but for more complicated navigational models, the NavigationService class provides finer control. You can obtain a reference to the NavigationService class by calling the static GetNavigationService method, as shown here: Sample of Visual Basic.NET Code Dim myNav As NavigationService myNav = NavigationService.GetNavigationService(Me) Sample of C# Code NavigationService myNav; myNav = NavigationService.GetNavigationService(this);
The NavigationService exposes a method called Navigate, which causes the application to navigate to the specified page. The most common way to use the Navigate method is to provide an instance of a Uniform Resource Identifier (URI), as shown here: Sample of Visual Basic.NET Code myNav.Navigate(New System.Uri("Page2.xaml", UriKind.Relative)) Sample of C# Code myNav.Navigate(new System.Uri("Page2.xaml", UriKind.Relative));
You also can create an instance of a new page in memory and navigate to it with the Navigate method, as shown here: Sample of Visual Basic.NET Code Dim aPage As New Page2() myNav.Navigate(aPage) Sample of C# Code Page2 aPage = new Page2(); myNav.Navigate(aPage);
118
Chapter 2
Designing the Presentation Layer
There are advantages and disadvantages to each method. When passing a URI to the Navigate method, the application’s journal can maintain the page data without having to maintain the entire page object in memory. Thus, memory overhead is lower using this method. However, it is not possible to pass information between pages using a URI. You can pass information between pages by creating a custom constructor for your page and using it to pass information or by setting properties on the page prior to navigating to it. You can use the NavigationService to refresh your page by calling NavigationService. Refresh. NavigationService also allows you to navigate forward and backward in the journal by calling NavigationService.GoForward and NavigationService.GoBack, respectively. These methods are demonstrated here: Sample of Visual Basic.NET Code myNav.Refresh() myNav.GoForward() myNav.GoBack() Sample of C# Code myNav.Refresh(); myNav.GoForward(); myNav.GoBack();
The NavigationService also exposes two Boolean properties called CanGoBack and CanGoForward, which you can query to determine if the application can navigate backward or forward. An example is shown here: Sample of Visual Basic.NET Code If myNav.CanGoBack = True Then myNav.GoBack() End If Sample of C# Code if (myNav.CanGoBack) myNav.GoBack();
Navigation is asynchronous. Thus, you can cancel navigation before it is completed by calling NavigationService.StopLoading, as shown here: Sample of Visual Basic.NET Code myNav.StopLoading() Sample of C# Code myNav.StopLoading();
Hosting Pages in Frames In addition to hosting a stand-alone Navigation application in a NavigationWindow, you also can host a page inside a control called a frame. A frame is simply a host for a XAML page or a webpage and is itself hosted inside a page or window. This allows you to incorporate navigation-based sections into a Windows application.
Objective 2.3: Design Application Workflow
Chapter 2
119
The Source property of the Frame control indicates the page to be loaded into the frame. The following code demonstrates how to set the source for a frame in XAML:
Using the Journal The journal is a bit of built-in technology in XBAPs and Navigation applications that keeps a list of the pages that have been visited and allows you to navigate this list. This will be familiar to anyone who uses Internet Explorer—the Back button navigates backward in the history to previously visited pages. The NavigationService allows you to manipulate the contents of the journal. Removing Items from the Journal
You might want to remove items from the journal. For example, suppose that your application has a complex configuration step that runs through several pages initially but is required only once. After configuration, you might want to remove these journal entries so that the user could navigate the regular pages without reloading the configuration pages. Removing items from the journal is fairly straightforward. NavigationService provides a method called RemoveBackEntry, which removes the last entry in the journal and returns an instance of JournalEntry that describes the instance that was removed. The following example demonstrates how to remove the last item from the journal: Sample of Visual Basic.NET Code myNav.RemoveBackEntry() Sample of C# Code myNav.RemoveBackEntry();
You can use the CanGoBack property to remove all the items in the journal, as shown here: Sample of Visual Basic.NET Code While myNav.CanGoback myNav.RemoveBackEntry() End While Sample of C# Code while (myNav.CanGoBack) { myNav.RemoveBackEntry(); }
Adding Items to the Journal
Adding items to the journal is considerably less straightforward than removing them. In general, you want to add items to the journal only when you want to take a “snapshot” of the state of a single page and allow the user to navigate back to previous states. For example, if you were performing a complex configuration task with multiple steps on a single page, you could provide custom journal entries to allow the user to roll back changes before they were committed. 120
Chapter 2
Designing the Presentation Layer
NavigationService provides a method called AddBackEntry, but it is more difficult to use than it appears and is considerably more complicated than RemoveBackEntry. It takes a single parameter, which is an instance of a class that derives from CustomContentState. This class, which you must implement, stores the state information for the page and r econstitutes the page state when the custom entry is navigated to. You also must implement the IProvideCustomContentState interface in the page for which you want to provide custom journal entries. Finally, you must add the custom journal entry manually at the point that you want to take the snapshot. The following is a high-level procedure that describes the general protocol for adding custom journal entries. To add custom journal entries 1. Create a class that inherits CustomContentState. (You need a separate class for each
page for which you want to add custom entries.) This class also must be marked with the Serializable attribute. 2. Add member variables and public properties to this class that hold the state of each
control on the page that you want to constitute. 3. Add code to override the JournalEntryName property, which indicates the
name that will be displayed in the journal. Often you might want to set this value in the constructor for this class, or use a method to determine an automatic name. 4. Override the Replay method. This method is called when the application navigates
backward or forward in the journal and is used to reconstitute the page. Although there are a few different approaches here, the best method is to create a callback method that executes a method in the page that receives the class instance as a parameter, thereby allowing the page access to the stored data. 5. Create a constructor for this class. The constructor should set the value of all data
about the state of the page that needs to be stored. It also should indicate the address of the callback method for the Replay method and any other parameters that you need for this instance (such as the JournalEntryName). 6. In the page for which a custom journal entry will be created, create a method that
handles the callback from the Replay method. This method should use the information in the passed parameter to restore the state of the page. 7. Implement the IProvideCustomContentState in the page. This involves implement-
ing the method GetContentState. GetContentState must return a CustomContentState object—you return an instance of your class in this method. 8. Add code that calls the NavigationService.AddBackEntry method at each point for
which you want to create a custom journal entry. Each time you call this method, you must provide a new instance of your class to save the custom state.
Objective 2.3: Design Application Workflow
Chapter 2
121
Handling Navigation Events Navigation in WPF applications occurs asynchronously. Thus, the NavigationService.Navigate method will return before navigation is complete. NavigationService exposes several events that allow your application to react at different points in the navigation process. You can handle these events to provide custom validation, to update navigation progress, or to add any other custom navigation functionality that is required. Table 2-8 summarizes the navigation events exposed by NavigationService. The events are listed in the order in which they occur. Table 2-8 Navigation Events Exposed by NavigationService
Event
Description
Navigating
The Navigating event occurs just as navigation begins.
Navigated
The Navigated event occurs after navigation has been initiated but before the target page has been retrieved.
NavigationProgress
The NavigationProgess event is raised after every 1 kilobyte (KB) of data has been received from the new page.
LoadCompleted
The LoadCompleted event is raised after the page has finished loading but before any of the page events fire.
FragmentNavigation
The FragmentNavigation event occurs as the page is about to be scrolled to the target element. This event does not fire if you do not use a URI with a target element.
NavigationStopped
The NavigationStopped event fires when the StopLoading method is called. Note that this event is not fired if navigation is canceled in the Navigating event handler.
NavigationFailed
The NavigationFailed event is raised if the requested page cannot be located or downloaded.
Note that the NavigationService events fire whether navigation occurs through the NavigationService or through hyperlink clicks. Because NavigationService events are regular .NET events, not routed events, you can c reate event handlers by creating methods with the correct signature and then attaching them to the event with the AddHandler operator (in Visual Basic) or the += operator (in C#), as shown here: Sample of Visual Basic.NET Code Public Sub HandleNavigated(ByVal sender As Object, _ ByVal e As System.Windows.Navigation.NavigationEventArgs) ' Event Handling Code goes here End Sub Public Sub HookupEventHandler() ' Hookup the event handler AddHandler NavigationService.Navigated, AddressOf HandleNavigated End Sub
122
Chapter 2
Designing the Presentation Layer
Sample of C# Code public void HandleNavigated(object sender, System.Windows.Navigation.NavigationEventArgse) { // Event handling code goes here } public void HookupEventHandler() { NavigationService.Navigated += HandleNavigated; }
Passing Information to Navigation Events
The NavigationService.Navigate method exposes overloads that allow you to pass additional information that becomes available when navigation events are being handled. For example, you might pass time stamp information or an object that could be used to validate the page request. To pass additional information to the event handlers, simply call one of the overloads of NavigationService.Navigate that takes an additional object parameter, as shown here: Sample of Visual Basic.NET Code NavigationService.Navigate(New System.Uri("page2.xaml"), "user = Joe") Sample of C# Code NavigationService.Navigate(new System.Uri("page2.xaml"), "user = Joe");
The additional information will be available in the Navigated, NavigationStopped, and LoadCompleted events through the e.ExtraData property, as shown here: Sample of Visual Basic.NET Code Public Sub Navigate(ByVal sender As Object, ByVal e As _System.Windows.Navigation.NavigationEventArgs) If e.ExtraData.ToString = "user=Kilroy" Then Trace.WriteLine("Kilroy was here") End If End Sub Sample of C# Code public void Navigate(object sender, System.Windows.Navigation.NavigationEventArgs e) { if (e.ExtraData.ToString() == "user=Kilroy") Trace.WriteLine("Kilroy was here"); }
Cancelling Navigation
You can cancel navigation in the Navigating event handler by setting the e.Cancel property to True, as shown here: Sample of Visual Basic.NET Code Public Sub NavigatingHandler(ByVal sender As Object, _ ByVal e As System.Windows.Navigation.NavigatingCancelEventArgs) e.Cancel = True End Sub
Objective 2.3: Design Application Workflow
Chapter 2
123
Sample of C# Code public void NavigatingHandler(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e) { e.Cancel = true; }
Using PageFunction Objects The PageFunction class is very similar to the Page class. You can design a PageFunction in the designer, you can add controls to a PageFunction, and you can navigate to a PageFunction through hyperlinks or by using NavigationService. The principal difference between Page objects and PageFunction objects is that PageFunction objects can return a value. This allows you to create pages that act in an analogous manner to dialog boxes—they can collect user information and then return that information to the main page. To add a PageFunction object to a project 1. From the Project menu, choose Add New Item to open the Add New Item dialog box. 2. In the Add New Item dialog box, select PageFunction (WPF). Name your PageFunction
and click Add. A PageFunction can return any type of .NET object. When you add a PageFunction to your project, Visual Studio automatically configures it to return a String instance. Although this is frequently useful, you might want to return some other kind of object from a PageFunction, such as an integer or an object. Changing the return type of your PageFunction is relatively straightforward. To change the return type of your PageFunction 1. In XAML view, locate the line in the PageFunction XAML that reads: x:TypeArguments="sys:String"
Then change the TypeArguments parameter to the type you want, as follows: x:TypeArguments="sys:Object"
For Visual Basic, that is all you need to do. For C#, the following additional step is required. 2. In Code view, locate the class declaration and change the type, as shown here: public partial class PageFunction1 : PageFunction
When you are ready for your PageFunction to return a value, you should call the OnReturn method. The OnReturn method takes a parameter of the type specified for the PageFunction. You also can return null for this parameter if no return value is required. The page that navigated to the PageFunction should handle the Returned event for that PageFunction. The instance of ReturnEventArgs returned by that event contains the returned value. 124
Chapter 2
Designing the Presentation Layer
To return a value from a PageFunction 1. In the page that navigates to the PageFunction, create a method that handles the
Returned method of that PageFunction. An example is shown here: Sample of Visual Basic.NET Code Public Sub ReturnHandler(ByVal sender As Object, _ ByVal e As ReturnEventArgs(Of String)) myString = e.Result End Sub Sample of C# Code public void ReturnHandler(object sender, ReturnEventArgs e) { myString = e.Result; }
2. In the page that navigates to the PageFunction, instantiate the PageFunction
programmatically and add code to hook up the PageFunction.Returned event to the new event handler, as shown here: Sample of Visual Basic.NET Code Dim myPage As New PageFunction1 AddHandler myPage.Return, AddressOf ReturnHandler Sample of C# Code PageFunction1 myPage = new PageFunction1(); myPage.Return += ReturnHandler;
3. In the PageFunction, after the task is completed, call the OnReturn method and pass
the return value in a new instance of ReturnEventArgs, as shown here: Sample of Visual Basic.NET Code OnReturn(New ReturnEventArgs(Of String)("Kilroy was here")) Sample of C# Code OnReturn(new ReturnEventArgs("Kilroy was here"));
Removing PageFunction Entries from the Journal Because PageFunction objects are used frequently to collect user input, you might not want to allow the user to return to a PageFunction via the journal after the task is completed. The PageFunction class exposes a property called RemoveFromJournal. When RemoveFromJournal is set to True, PageFunction entries are deleted automatically from the journal once the user is finished with the task.
Simple Navigation and Structured Navigation Simple navigation is a common design model in lightweight page-based applications. An application with simple navigation has a start, an end, and a series of pages though which the user navigates. There is generally little or no branching, and after a page is visited, it generally
Objective 2.3: Design Application Workflow
Chapter 2
125
is not returned to unless the user wants to back up. Although this paradigm is well suited to certain types of applications, such as a configuration wizard, other kinds of applications might find it lacking. Consider a shopping cart application. A user might want to add items to a shopping cart, return to shop for more items, add them to the shopping cart, repeat this a few more times, and then check out. Strictly linear navigation would be insufficient in this case. PageFunction objects allow you to build more structure into your application. With PageFunction objects, you can allow your users to leave the main line of execution to perform tasks and then return. Using PageFunction objects, you can create execution models with complex flow structures, and by manipulating the journal, you can control how a user is able to navigate back through the application.
Designing for Different Input Types Different input types require different UI designs. When designing an application for deployment in a public kiosk, keep these factors in mind: ■■
■■ ■■
■■
■■
■■
Design the application to be full-screen, and remove control buttons that might allow a user to move, resize, or minimize a window. Users will speak different languages, so rely more on icons and images than text. Users will have varying accessibility requirements. Keep buttons and text large, simple, and high-contrast. Users will not spend time learning how to use your application. Defaults should be carefully selected, the UI must be as simple as possible, and you should limit the number of choices presented to the user at a time to less than four. Even with a touch-screen interface, avoid complex interactions such as dragging, pinching, or using multiple fingers. The only user interaction should be touching the screen or pressing a button. Perform extensive usability testing. For more information about testing, refer to Objective 5.2.
When designing a UI for a mobile device, keep these factors in mind: ■■ ■■
■■ ■■
■■
■■
126
Build a task-based UI focused on the most common tasks. Use buttons instead of menus. Minimize the amount of typing required. For example, instead of prompting the user to type their state, provide them with a list they can select from. Avoid requiring users to scroll. Instead, separate the UI into more pages. Plan for users to be interrupted regularly. Mobile application use tends to be interrupted more often. Design the UI behavior to be as similar as possible to the operating system’s default behavior. For example, support swiping, panning, and pinch-to-zoom (but do not require the user to take advantage of those capabilities). Support both horizontal and vertical screen orientations.
Chapter 2
Designing the Presentation Layer
Objective Summary ■■
■■
■■
■■
User navigation can be implemented in Windows Forms application through the use of MDI forms. MDI forms allow multiple forms to be arranged and navigated as part of a single application. Navigation applications in WPF provide an easy-to-program navigational experience that allows forward and backward navigation as well as branching. The NavigationService object provides the basic functionality required for user navigation. The Journal object allows you to keep a record of past states of a navigation application in case changes need to be rolled back. PageFunction objects behave like pages, but they return a value and can be useful for receiving user input.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of the chapter. 1. Which of the following is NOT required to implement custom back entries? A. A Page or PageFunction that implements IProvideCustomContentState B. Code that calls NavigationService.AddBackEntry C. An instance of the JournalEntry class D. A class that inherits from CustomContentState 2. Which of the following is the correct firing order for navigation events in a navigation
application? A. Navigating
NavigationProgress Navigated FragmentNavigation LoadCompleted B. Navigating
Navigated NavigationProgress LoadCompleted FragmentNavigation C. Navigating
NavigationProgress
Objective 2.3: Design Application Workflow
Chapter 2
127
Navigated LoadComplete FragmentNavigation D. Navigating
Navigated NavigationProgress FragmentNavigation LoadCompleted
Thought Experiment Designing a Data Entry Wizard
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Lucerne Publishing. Lucerne is creating a new WPF application that its data entry team can use to enter newly published books into its database. Because the data entry team is often staffed with temporary personnel, the UI must be intuitive and must minimize the opportunity for making mistakes. The process of entering information about a new book requires the data entry staff member to enter about 40 different pieces of information. The developers have designed a UI with these attributes: ■■
A WPF page-based application.
■■
A series of eight pages with five fields to enter different pieces of information on each.
■■
Next and Cancel buttons in the lower-right corner of the window.
■■
A Back button in the upper-left corner of the window.
Answer the following questions about the future performance of the application:
1. Would it be better to design the application using MDI forms? Why or why not?
2. The application creates a new record for the book after page 4. This process cannot be changed easily. Is it possible to prevent users from going back to earlier pages after they finish page 4? If so, how?
3. We would like to allow the user to click the Back button to undo changes within a single page. Is this possible? If so, how?
128
Chapter 2
Designing the Presentation Layer
Objective 2.4: Design Data Presentation and Input Most of what your Presentation layer is doing in a distributed application is either presenting or receiving data. Your Presentation layer will be responsible for validating data, binding data and presenting it to the user, and presenting media to the user. In this section, you will learn how to handle data in the Presentation layer.
This objective covers how to: ■■
Design data validation.
■■
Design a data binding strategy.
■■
Manage data shared between forms.
■■
Choose media services.
Designing Data Validation An important part of designing any UI is how to handle data input. Users can be error-prone, and to ensure the integrity of your database, it is vital that data be validated for correctness. You might need to use several different data validation techniques, depending on the needs of your application.
Data Type Validation The simplest form of data validation is data type validation. In this type of validation, data is simply checked to ensure that it is of the appropriate type. For example, input that should be a string is checked to see if it is a string, and numeric values are parsed to an integer or decimal. This type of validation can usually be accomplished with fairly simple methods in the UI.
Range Checking An extension of data type validation, range checking ensures not only that data is of the appropriate data type, but also that it falls within an acceptable range of values. For example, a field that asked for the age of an employee might require a value between 18 and 100, or some other range that represented the actual range of working employees within the company. This type of validation is also not complex and usually can be accomplished within the UI.
Lookup Validation Lookup validation can be thought of as a more specialized form of range checking. In lookup validation, fields are only allowed to be one of a certain set of values, which may or may not be sequential in any way. For example, consider an application that required the serial
Objective 2.4: Design Data Presentation and Input
Chapter 2
129
number of an item being sold to correspond to a serial number of an item in an inventory database. The typical solution to lookup validation is to create a lookup table and validate against the values contained in that table. Lookup validation can take place against a static set of values or against values that change over time, requiring dynamic generation of the lookup table. In either case, this kind of data validation usually requires a more complex business rule to validate against.
Complex Validation Your input data might require a more complex type of validation than any of the types described here. For complex validation, separate business rules will be required.
Validating Data at the Client and Server When validating user input, you should validate it on the client (for immediate responsiveness and to assist data entry) and again at the server (for security). Never trust data validation performed at the client because it is possible for users to bypass client-side validation controls by modifying the application, altering data after it is sent by the application, or creating an entirely different application that connects to the server. The easiest way to validate data in a Windows Form application is to use the Validating event. The Validating event occurs before a control loses the focus. This event is raised only when the CausesValidation property of the control that is about to receive the focus is set to True. Thus, if you want to use the Validating event to validate data entered in your control, the CausesValidation of the next control in the tab order should be set to true. In order to use Validating events, the CausesValidation property of the control to be validated must also be set to True. By default, the CausesValidation property of all controls is set to True when controls are created at design time. Controls such as Help buttons are typically the only kind of controls that have CausesValidation set to False. The Validating event allows you to perform sophisticated validation on your controls. You could, for example, implement an event handler that tested whether the value entered corresponded to a very specific format. Another possible use is an event handler that doesn’t allow the focus to leave the control until a value has been entered. The Validating event includes an instance of the CancelEventArgs class. This class contains a single property, Cancel. If the input in your control does not fall within the required parameters, you can use the Cancel property within your event handler to cancel the Validating event and return the focus to the control. The Validated event fires after a control has been validated successfully. You can use this event to perform any actions based upon the validated input. The following example demonstrates a handler for the Validating event. This method requires an entry in TextBox1 before it will allow the focus to move to the next control. Sample of Visual Basic.NET Code Private Sub TextBox1_Validating(ByVal sender As Object, ByVal e As _ System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
130
Chapter 2
Designing the Presentation Layer
' Checks the value of TextBox1 If TextBox1.Text = "" Then ' Resets the focus if there is no entry in TextBox1 e.Cancel = True End If End Sub Sample of C# Code private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e) { // Checks the value of textBox1 if (textBox1.Text == "") // Resets the focus if there is no entry in TextBox1 e.Cancel = true; }
Validating Data in a WPF Application WPF allows you to set validation rules that define how your application validates its data. Each Binding object exposes a ValidationRules collection. You can add new rules to the ValidationCollection, as shown in bold in this example:
In this example, the CandyBarValidationRule and SweetTreatsValidationRule declarations represent two custom validation rules that have been defined in your application. When a new value is bound, each of the validation rules are evaluated in the order in which they are declared. In this example, the CandyBarValidationRule is evaluated first, followed by the SweetTreatsValidationRule. If there are no validation problems, the application proceeds normally. If there is a problem that violates a validation rule, however, the following things happen: ■■
The element with the validation error is outlined in red.
■■
The attached property Validation.HasError is set to True.
■■
■■
■■
A new ValidationError object is created and added to the attached Validation.Errors collection. If the Binding.NotifyOnValidationError property is set to True, the Validation. Error – attached event is raised. The data-binding source is not updated with the invalid value and instead remains unchanged. Objective 2.4: Design Data Presentation and Input
Chapter 2
131
Implementing Custom Validation Rules You can create specific validation rules by creating classes that inherit the abstract class ValidationRule. The ValidationRule class has one virtual method that must be overridden: the Validate method. The Validate method receives an object parameter, which represents the value that is being evaluated, and returns a ValidationResult object, which contains an IsValid property and an ErrorCondition property. The IsValid property represents a Boolean value that indicates whether or not the value is valid, and the ErrorCondition property is text that can be set to provide a descriptive error condition. If a ValidationResult with an IsValid value of True is returned, the value is considered to be valid and application execution proceeds normally. If a ValidationResult with an IsValid result of False is returned, a ValidationError is created as described previously. The following example demonstrates a simple implementation of the ValidationRule abstract class: Sample of Visual Basic.NET Code Public Class NoNullStringsValidator Inherits ValidationRule Public Overrides Function Validate(ByVal value As Object, ByVal _ cultureInfo As System.Globalization.CultureInfo) As _ System.Windows.Controls.ValidationResult Dim astring As String = value.ToString If astring = "" Then Return New ValidationResult(False, "String cannot be empty") Else Return New ValidationResult(True, Nothing) End If End Function End Class Sample of C# Code public class NoNullStringsValidator : ValidationRule { public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureinfo) { string aString = value.ToString(); if (aString == "") return new ValidationResult(false, "String cannot be empty"); return new ValidationResult(true, null); } }
In this example, the string contained in the value object is evaluated. If it is a zero-length string, the validation fails; otherwise, the validation succeeds.
Handling Validation Errors Once validation errors are raised, you must decide how to respond to them. In some cases, the visual cues provided by the validation error are enough—the user can see that the element is surrounded by a red outline and can detect and fix the problem. In other cases, 132
Chapter 2
Designing the Presentation Layer
however, you might need to provide feedback to the user regarding the nature of the validation problem. When validation is enabled for a binding, the Validation.Error event is attached to the bound element. Validation.Error includes an instance of ValidationErrorEventArgs, which contains two important properties, as described in Table 2-9. Table 2-9 Important Properties of ValidationErrorEventArgs
Property
Description
Action
Describes whether the error in question is a new error or an old error that is being cleared
Error
Contains information about the error that occurred, the details of which are described in further detail in Table 5-7
The Error object of ValidationErrorEventArgs contains a host of useful information r egarding the error that occurred. Important properties of the Error object are described in Table 2-10. Table 2-10 Important Properties of the Error Object
Property
Description
BindingInError
Contains a reference to the Binding object that caused the validation error
ErrorContent
Contains the string set by the ValidationRule object that returned the validation error
Exception
Contains a reference to the Exception, if any, that caused the validation error
RuleInError
Contains a reference to the ValidationRule that caused the validation error
The Validation.Error event is not fired unless the NotifyOnValidationError property of the Binding object is specifically set to True, as shown in bold here:
When this property is set to True, the Validation.Error event is raised anytime any ValidationRule in the ValidationRules collection of the Binding object detects a validation error. The Validation.Error event is a bubbling event. It is raised first in the element where the validation error occurs and then in each higher-level element in the visual tree. Thus, you can
Objective 2.4: Design Data Presentation and Input
Chapter 2
133
create a local error-handling method that specifically handles validation errors from a single element, as shown in bold here:
Alternatively, you can create an error-handling routine that is executed higher in the visual tree to create a more generalized validation error handler, as shown in bold here:
The Validation.Error event is fired both when a new validation error is detected and when an old validation error is cleared. Thus, it is important to check the e.Action property to determine whether the error is being cleared or it is a new error. The following example demonstrates a sample validation error handler that displays the error message to the user when a new error occurs and writes information to Trace when a validation error is cleared: Sample of Visual Basic.NET Code Private Sub Grid_Error(ByVal sender As System.Object, ByVal e As _ System.Windows.Controls.ValidationErrorEventArgs) If e.Action = ValidationErrorEventAction.Added Then MessageBox.Show(e.Error.ErrorContent.ToString) Else Trace.WriteLine("Validation error cleared") End If End Sub Sample of C# Code private void Grid_Error(object sender, ValidationErrorEventArgs e) { if (e.Action == ValidationErrorEventAction.Added) MessageBox.Show(e.Error.ErrorContent.ToString()); else System.Diagnostics.Trace.WriteLine("Validation error cleared"); }
Design a Data Binding Strategy Data binding in the Presentation layer typically refers to binding presentation controls to a cached client-side copy of the bound data. This data can be presented to the user and changed or added to as need be, and then updated to or from the central data store when the application requires it. When deciding on a data binding strategy for your Presentation layer, you must decide how you want to store your local data, and then determine what component in the Presentation layer you want to use to connect the presentation logic to the local data.
Data Binding in Windows Forms Local data in a Windows Forms application is typically held in a Dataset object. Dataset is a very versatile class that can handle multiple data tables. While you can bind your Presentation layer directly to a Dataset, a better strategy is to access the individual tables in a Dataset via a BindingSource component. 134
Chapter 2
Designing the Presentation Layer
The BindingSource component manages data currency and navigation for the underlying data source. Thus, by binding your Presentation layer controls to a BindingSource object that refers to the local copy of data, you are able to manage the presentation of the underlying data easily. The BindingSource component contains the information that controls need to bind to a BindingSource by passing it a reference to a DataTable in a DataSet. By binding to the BindingSource instead of to the DataSet, you can redirect your application to another source of data easily without having to redirect all the data binding code to point to the new data source. The following code shows how to create a BindingSource and assign it a reference to the Northwind Customers table in a Northwind database-based dataset named NorthwindDataSet1: Sample of Visual Basic.NET Code customersBindingSource = New BindingSource(NorthwindDataSet1, "Customers") Sample of C# Code customersBindingSource = new BindingSource(northwindDataSet1, "Customers");
Binding to Types Other Than Dataset The BindingSource component can be used to bind to several different types of objects and will in most cases expose the underlying data of that object as an IBindingList interface. Table 2-11 explains the types that the BindingSource.DataSource property can be set to and what the result will be. Table 2-11 Types for the DataSource Property of BindingSource
DataSource Property
List Results
Nothing
An empty IBindingList of objects. Adding an item sets the list to the type of the added item.
Nothing with DataMember set
Not supported; raises ArgumentException.
Non-list type or object of type “T”
Empty IBindingList of type “T”.
Array instance
IBindingList containing the array elements.
IEnumerable instance
An IBindingList containing the IEnumerable items.
List instance containing type “T”
IBindingList instance containing type “T”.
Data Binding in WPF WPF has data binding built in at all levels, and you can bind a WPF Presentation layer easily to a variety of data sources, including datasets, objects, or XML representations in memory. Binding to collections in WPF is handled in essentially the same way, whether the bound collection is a dataset, datatable, or other in-memory object. For simple displaying of bound
Objective 2.4: Design Data Presentation and Input
Chapter 2
135
members, you must set the ItemsSource property to the collection to which you are binding and set the DisplayMemberPath property to the collection member that is to be displayed. The following example demonstrates how to bind a ListBox to a static resource named myList and a display member called FirstName:
A more common scenario when working with bound lists, however, is to bind to an object that is defined and filled with data in code. In this case the best way to bind to the list is to set the DisplayMemberPath in the XAML and then set the DataContext of the element or its container in code. The following example demonstrates how to bind a ListBox to an object called myCustomers that already has been created and populated. The ListBox displays the entries from the CustomerName property: Sample of Visual Basic.NET Code ' Code to initialize and fill myCustomers has been omitted grid1.DataContext = myCustomers; Sample of Visual C# Code // Code to initialize and fill myCustomers has been omitted grid1.DataContext = myCustomers; Sample of XAML Code
Note that in the XAML for this example, the ItemsSource property is set to a Binding object that has no properties initialized. The Binding object binds the ItemsSource of the ListBox, but because the Source property of the Binding is not set, WPF searches upward through the visual tree until it finds a DataContext that has been set. Because the DataContext for grid1 has been set in code to myCustomers, this then becomes the source for the binding.
Navigating Bound Data in WPF WPF has a built-in navigation mechanism for data and collections. When a collection is bound to by a WPF Binding, an ICollectionView is created behind the scenes. The ICollectionView interface contains members that manage data currency, as well as managing views, grouping, and sorting. You can get a reference to the ICollectionView by calling the CollectionViewSource.GetDefaultView method, as shown here: Sample of Visual Basic.NET Code ' This example assumes a collection named myCollection Dim myView As System.ComponentModel.ICollectionView myView = CollectionViewSource.GetDefaultView (myCollection)
136
Chapter 2
Designing the Presentation Layer
Sample of Visual C# Code // This example assumes a collection named myCollection System.ComponentModel.ICollectionView myView; myView = CollectionViewSource.GetDefaultView (myCollection);
When calling this method, you must specify the collection or list for which to retrieve the view (which is myCollection in the previous example). CollectionViewSource.GetDefaultView returns an ICollectionView object that is actually one of three different classes depending on the class of the source collection. If the source collection implements IBindingList, the view returned is a indingListCollectionView object. If the source collection implements IList but not IBindingList, B the view returned is a ListCollectionView object. If the source collection implements IEnumerable but not IList or IBindingList, the view returned is a CollectionView object.
Binding to XML in WPF The XmlDataProvider allows you to bind WPF elements to data in the XML format. The following example demonstrates an XmlDataProvider providing data from a source file called Items.xml:
You can also provide the XML data inline as an XML data island. In this case you wrap the XML data in XData tags, as shown here:
You can bind elements to the data provided by an XmlDataProvider in the same way that you would bind to any other data source—namely, using a Binding object and specifying the XmlDataProvider in the Source property, as shown here:
Using XPath When Binding to XML You can use XPath expressions to filter the results exposed by the XmlDataProvider or to filter the records displayed in the bound controls. By setting the XPath property of the XmlDataProvider to an XPath expression, you can filter the data provided by the source. The following example filters the results exposed by an XmlDataProvider object to include only those nodes called in the top-level node:
Objective 2.4: Design Data Presentation and Input
Chapter 2
137
You also can apply XPath expressions in the bound controls. The following example sets the XPath property to Diamond (shown in bold), which indicates that only data contained in tags will be bound:
Using Data Templates in the WPF Presentation Layer A data template is a bit of XAML that describes how bound data is displayed. A data template can contain elements that are bound to a data property, along with additional markup that describes layout, color, and other aspects of appearance. The following example demonstrates a simple data template that describes a Label element bound to the ContactName property. The Foreground, Background, BorderBrush, and BorderThickness properties are also set:
You set the data template on a control by setting one of two properties. For content controls, you set the ContentTemplate property, as shown in bold here:
For item controls, you set the ItemsTemplate property, as shown in bold here:
Note that for item controls, the DisplayMemberPath and ItemTemplate properties are mutually exclusive—you can set one but not the other. A frequent pattern with data templates is to define them in a resource collection and reference them in your element, rather than defining them inline as shown in the previous 138
Chapter 2
Designing the Presentation Layer
examples. All that is required to reuse a data template in this manner is to define the template in a resource collection and set a Key for the template, as shown here:
Then you can set the template by referring to the resource, as shown in bold here:
Managing Data Shared Between Forms If you must share data between forms in the Presentation layer, the best way to do so is by scoping the objects containing the data as application scope variables. This allows members in all forms in an application to access the data.
Creating an Application Variable in Windows Forms with Visual Basic When programming in Visual Basic .NET, the easiest way to create a variable with application scope is to create a module and declare a public variable within that module. This variable then will be available to all forms in the application.
Creating an Application Variable in Windows Forms with Visual C# When programming in Visual C#, the best way to create an application-scoped variable is to add a public, static variable to the .cs file that contains the Main sub. In Visual Studio–generated applications, this is usually Program.cs. Variables created in this fashion will be available to all forms in an application, though they will need to be prefaced with the name of the class.
Creating an Application Variable in WPF When creating an application variable in WPF, the best way is to create an application resource that will be accessible by all objects in a particular application. You can create an application resource by opening the App.xaml file (for C# projects) or the Application.xaml file (for Visual Basic projects) and adding the resource to the Application.Resources collection, as shown in bold here:
Objective 2.4: Design Data Presentation and Input
Chapter 2
139
Managing Media The .NET Framework provides tools that allow you to manage sound and video media presentations in your client applications. For simple sound support, the SoundPlayer component is provided; and for more complex sound or video media, the MediaPlayer and MediaElement components can be used.
SoundPlayer The SoundPlayer class was introduced in .NET Framework 2.0 as a managed class to enable audio in Windows applications. It is lightweight and easy to use, but it has significant limitations. The SoundPlayer class can play only uncompressed .wav files. It cannot read compressed .wav files, nor can it read files in other audio formats. Furthermore, the developer has no control over volume, balance, speed of playback, or any other aspects of sound playback. In spite of its limitations, SoundPlayer can be a useful and lightweight way to incorporate sound into your applications. It provides a basic set of members that allow you to load and play uncompressed .wav files easily.
MediaPlayer and MediaElement The MediaPlayer and MediaElement classes provide deep support for playing audio and video media files in a variety of formats. Both of these classes use the functionality of Windows M edia Player 10, so while they are guaranteed to be usable in applications running on Windows Vista and later, which come with Media Player 11 as a standard feature, these classes will not function on Windows XP installations that do not have at least Windows Media Player 10 installed. The MediaPlayer and MediaElement classes are very similar and expose many of the same members. The primary difference between the two classes is that although MediaPlayer loads and plays both audio and video, it has no visual interface and thus cannot display video in the UI. On the other hand, MediaElement is a full-fledged WPF element that can be used to display video in your applications. MediaElement wraps a MediaPlayer object and provides a visual interface to play video files. Furthermore, MediaPlayer cannot be used easily in XAML, whereas MediaElement is designed for XAML use. While MediaPlayer and MediaElement are designed for use in WPF applications, you can use them in your Windows Forms applications through interoperability, as described earlier in this chapter.
Objective Summary ■■
140
Data validation is a common task for the Presentation layer. Depending on the data, any of a number of different types of validation might be necessary. Both WPF and Windows Forms provide technologies to enable validation of user input.
Chapter 2
Designing the Presentation Layer
■■
■■ ■■
WPF and Windows Forms both enable binding to datasets and other collections. In Windows Forms, the BindingSource object manages data currency and n avigation. In WPF, these tasks are managed through the ICollectionView interface. WPF also incorporates technology that allows direct binding to XML and the use of XPath queries. Data can be shared between multiple forms via application variables. The SoundPlayer, MediaPlayer, and MediaElement classes incorporate functionality that enables multimedia presentations.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of the chapter. 1. You are creating a Presentation layer that will validate user input. The fields that need
to be validated include an employee age field, which must be an integer between 18 and 85. What is the correct data validation strategy for this scenario? A. Data type validation B. Range checking C. Lookup validation D. Complex validation 2. Which of the following code snippets correctly demonstrates a data template that
binds the ContactName field set in a ListBox? Assume that the DataContext is set correctly. A.
B.
Objective 2.4: Design Data Presentation and Input
Chapter 2
141
C.
D.
Thought Experiment Designing Data Validation
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You can
find answers to these questions in the “Answers” section at the end of this chapter. Lucerne Publishing has designed the UI for a new page-based WPF application that its data entry team can use to enter newly published books into its database. With the eight pages of the UI designed, the company is ready to begin designing the page functionality. The process of entering information about a new book requires the data entry staff member to enter about 40 different pieces of information. Once the information has been entered to the database, a copy of it is sent to distributors, making it impossible to change. Therefore, accurate data entry is of paramount importance. The WPF application retrieves information from and submits information to a Windows Communication Foundation (WCF) web service. The web service, in turn, communicates with the underlying database. In the near future, Lucerne Publishing plans to expose the web service to partner organizations so that it can develop its own client applications. The developers have designed an application with the following traits: ■■
Numeric values are validated using WPF range validation as the user populates a field.
■■
Text data, such as a book’s ISBN (a unique identifier for a book) are validated within WPF using regular expressions.
142
Chapter 2
Designing the Presentation Layer
■■
Data that refers to existing values in the database are verified by using a custom validation rule. The ValidationRule-derived class makes a call to the WCF web service when the user clicks the Next button.
■■
When the elements on a page can be validated, the application’s title bar is green. If validation fails for any element on the page, the application changes the color of the title bar to red.
Answer the following questions about the future performance of the application:
1. What would you change about the data validation design? 2. Will indicating the validation state by changing the title bar work?
Objective 2.5: Design Presentation Behavior With the advent of WPF, the developer now has a great deal of control over how the UI interacts with the user. While familiar behaviors such as dragging are still easy to implement, you can also use attached events, triggers, and animation to make the user experience more responsive than ever before.
This objective covers how to: ■■
Determine which behaviors will be implemented and how.
■■
Implement drag-and-drop functionality.
Determine Which Behaviors Will Be Implemented and How WPF provides unprecedented functionality for implementing behaviors in the Presentation layer through attached events, triggers, and animation.
Attached Events It is possible for a control to define a handler for an event that the control cannot itself raise. These incidents are called attached events. For example, consider Button controls in a Grid. The Button class defines a Click event, but the Grid class does not. However, you still can define a handler for buttons in the grid by attaching the Click event of the Button control in the XAML code. The following example demonstrates attaching an event handler for a Button contained in a Grid: Button
Objective 2.5: Design Presentation Behavior
Chapter 2
143
Now every time a button contained in the Grid shown here is clicked, the changeGridColor event handler will handle that event. In this way, the Grid can respond to another element’s event. Attached events make it possible for any element in your Presentation layer to respond directly to an event raised by other elements.
Triggers Along with Setters, Triggers make up the bulk of objects that you use in creating styles in WPF applications. Triggers allow you to implement property changes declaratively in response to other property changes that would have required event-handling code in Windows Forms programming. There are five kinds of Trigger objects, as listed in Table 2-12. Table 2-12 Types of Trigger Objects
Type
Class Name
Description
Property trigger
Trigger
Monitors a property and activates when the value of that property matches the Value property.
Multi-trigger
MultiTrigger
Monitors multiple properties and activates only when all the monitored property values match their corresponding Value properties.
Data trigger
DataTrigger
Monitors a bound property and activates when the value of the bound property matches the Value property.
Multi-data-trigger
MultiDataTrigger
Monitors multiple bound properties and activates only when all the monitored bound properties match their corresponding Value properties.
Event trigger
EventTrigger
Initiates a series of Actions when a specified event is raised.
A Trigger is active only when it is part of a Style.Triggers collection—with one exception. EventTrigger objects can be created within a Control.Triggers collection outside a Style. The Control.Triggers collection can accommodate only EventTriggers, and any other Trigger placed in this collection causes an error. EventTriggers are used primarily with animation.
Property Triggers The most commonly used type of Trigger is the property trigger. The property trigger monitors the value of a property specified by Property. When the value of the specified property equals the Value property, the Trigger is activated. Triggers listen to the property indicated by Property and compare that property to the Value property. When the referenced property and the Value property are equal, the Trigger is a ctivated. Any Setter objects in the Setters collection of the Trigger are applied to the style, and any Actions in the EnterActions collections are initiated. When the referenced property no longer matches the Value property, the Trigger is inactivated. All Setter objects in the Setters collection of the Trigger are inactivated, and any Actions in the ExitActions collection are initiated. 144
Chapter 2
Designing the Presentation Layer
The following example demonstrates a simple Trigger object that changes the FontWeight of a Button element to Bold when the mouse enters the Button:
In this example, the Trigger defines one Setter in its Setters collection. When the Trigger is activated, that Setter is applied.
Multi-Triggers Multi-triggers are similar to property triggers in that they monitor the value of properties and activate when those properties meet a specified value. The difference is that multi-triggers are capable of monitoring several properties at a single time and they activate only when all monitored properties equal their corresponding Value properties. The properties that are monitored and their corresponding Value properties are defined by a collection of Condition objects. The following example demonstrates a MultiTrigger that sets the Button.FontWeight property to Bold only when the Button is focused and the mouse has entered the control:
Data Triggers and Multi-Data-Triggers Data triggers are similar to property triggers in that they monitor a property and activate when the property meets a specified value, but they differ in that the property they monitor is a bound property. Instead of Property, data triggers expose a Binding property that indicates the bound property to listen to. The following shows a data trigger that changes the Background property of a Label to Red when the bound property CustomerName equals “Fabrikam”:
Objective 2.5: Design Presentation Behavior
Chapter 2
145
Multi-data-triggers are to data triggers as multi-triggers are to property triggers. They contain a collection of Condition objects, each of which specifies a bound property via its Binding property and a value to compare to that bound property. When all the c onditions are satisfied, the MultiDataTrigger activates. The following example demonstrates a MultiDataTrigger that sets the Label.Background property to Red when CustomerName equals “Fabrikam” and OrderSize equals 500:
Event Triggers Event triggers are different from the other Trigger types. Whereas other Trigger types m onitor the value of a property and compare it to an indicated value, event triggers specify an event and activate when that event is raised. In addition, event triggers do not have a Setters collection—rather, they have an Actions collection. The following two examples demonstrate the EventTrigger class. The first example uses a SoundPlayerAction to play a sound when a Button is clicked:
The second example demonstrates a simple animation that causes the Button to grow in height by 200 units when clicked:
Animation The term animation brings to mind hand-drawn anthropomorphic animals performing amusing antics in video media, but in WPF, animation has a far simpler meaning. Generally speaking, an animation in WPF refers to an automated property change over a set period
146
Chapter 2
Designing the Presentation Layer
of time. You can animate an element’s size, location, color, or virtually any other property or properties associated with an element. You can use the Animation classes to implement these changes. The Animation classes are a large group of classes designed to implement these utomated property changes. There are 42 Animation classes in the System.Windows.Media. a Animation namespace, and each one has a specific data type that they are designed to animate. Animation classes fall into three basic groups: linear animations, key frame–based animations, and path-based animations. Linear animations, which automate a property change in a linear way, are named in the format Animation, where is the name of the type being animated. DoubleAnimation is an example of a linear animation class, and that is the animation class you are likely to use the most. Key frame–based animations perform their animation on the basis of several waypoints, called key frames. The flow of a key-frame animation starts at the beginning and then progresses to each of the key frames before ending. The progression is usually linear. Key-frame animations are named in the format AnimationUsingKey Frames, where is the name of the Type being animated. An example is StringAnimationUsingKeyFrames. Path-based animations use a Path object to guide the animation. They are used most often to animate properties that relate to the movement of visual objects along a complex course. Path-based animations are named in the format AnimationUsingPath, where is the name of the type being animated. There are currently only three path-based Animation classes—PointAnimationUsingPath, DoubleAnimationUsingPath, and MatrixAnimationUsingPath.
Creating Attached Behaviors You also can create custom attached behaviors that change or extend the behavior of standard classes. Usually, developers do this to change the way a UI element interacts with the user. For example, you might create a custom attached behavior to allow a button to respond to right-clicking in an M-V-VM application, or to add drag-and-drop behavior to a class that does not support it natively. Custom attached behaviors are simpler to implement than deriving a new class from a standard control, and they do not require you to violate the presentation pattern (as discussed in Objective 2.1) by writing behavior logic in the ViewModel. To implement an attached behavior, follow these high-level steps: 1. Create a new static class that represents the behavior. 2. Within the class, expose any attached properties as public dependency objects. The
attached property will be used to enable your attached behavior.
Objective 2.5: Design Presentation Behavior
Chapter 2
147
3. Within the change event for your attached property, write the code that implements
your attached behavior. 4. In the XAML, apply the attached behavior to the element.
For example, imagine that you wanted to extend the standard text box behavior with auto-complete functionality: whenever the user typed a character, the view would send the typed phrase to a web service that returns a list of options the user can select from. You could do this by handling the appropriate text box event in the ViewModel and then populating the list from the event handler. However, this technique requires writing UI logic in your ViewModel, breaking the M-V-VM presentation pattern. It also requires writing event handlers for every text box that you want to add this behavior to. You also could do this by creating an attached behavior. By creating an attached behavior, the behavior code is contained within the view, which fits the presentation pattern better. In addition, the attached behavior uses less, more maintainable code. More Info Creating Attached Behaviors
For detailed information, read “Introduction to Attached Behaviors in WPF” at http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx, and “Attached Behavior” at http://eladm.wordpress.com/2009/04/02/attached-behavior/.
Implementing Drag-and-Drop Functionality Drag-and-drop functionality is ubiquitous in Windows programming. It refers to allowing the user to grab data—such as text, an image, or another object—with the mouse and drag it to another control. When the mouse button is released over the other control, the data that is being dragged is dropped onto the control, and a variety of effects can then occur. The drag-and-drop operation is similar to cutting and pasting. The mouse pointer is ositioned over a control and the mouse button is pressed. Data is copied from a source p control; when the mouse button is released, the action is completed. All code for copying the data from the source control and any actions taken on the target control must be coded explicitly. Drag-and-drop operations are very similar in WPF and Windows Forms applications. The primary difference is that in Windows Forms, drag-and-drop methods and events are exposed on individual controls, and in WPF, the methods are exposed on a static class called DragDrop, which also provides attached events to WPF controls to facilitate drag-and-drop operations. The drag-and-drop process is primarily an event-driven process. There are events that occur on the source control and events that occur on the target control. The drag-and-drop events for the source control are described in Table 2-13. The drag-and-drop events for the target control are described in Table 2-14.
148
Chapter 2
Designing the Presentation Layer
Table 2-13 Source Control Events Involved in Implementing Drag-and-Drop Operations
Event
Description
MouseDown
Occurs when the mouse button is pressed while the pointer is over the control. In general, the DoDragDrop method is called in the method that handles this event. In WPF applications, this is a bubbling event.
GiveFeedBack
Provides an opportunity for the user to set a custom mouse pointer. In WPF applications, this is a bubbling event.
QueryContinueDrag
Enables the drag source to determine whether a drag event should be canceled. In WPF applications, this is a bubbling event.
PreviewMouseDown
WPF only. The tunneling version of the MouseDown event.
PreviewGiveFeedBack
WPF only. The tunneling version of the GiveFeedback event.
PreviewQueryContinueDrag
WPF only. The tunneling version of the QueryContinueDrag event.
Table 2-14 Target Control Events Involved in Implementing Drag-and-Drop Operations
Event
Description
DragEnter
Occurs when an object is dragged within a control’s bounds. The handler for this event receives a DragEventArgs object. In WPF, this is a bubbling event.
DragOver
Occurs when an object is dragged over a target control. The handler for this event receives a DragEventArgs object. In WPF, this is a bubbling event.
DragDrop
Occurs when the mouse button is released over a target control. The handler for this event receives a DragEventArgs object. In WPF, this is a bubbling event.
DragLeave
Occurs when an object is dragged out of the control’s bounds. In WPF, this is a bubbling event.
PreviewDragEnter
WPF only. The tunneling version of DragEnter.
PreviewDragOver
WPF only. The tunneling version of DragOver.
PreviewDragDrop
WPF only. The tunneling version of DragDrop.
PreviewDragLeave
WPF only. The tunneling version of DragLeave.
In addition, the DoDragDrop method on the source control is required to initiate the drag-and-drop process in Windows Forms, and the DoDragDrop method of the DragDrop class is required for WPF. Furthermore, the target control must have the AllowDrop property set to True. If you are creating an XBAP application, you must run the application with full trust to take advantage of true drag-and-drop; partial trust allows only a simulated drag-and-drop using limited mouse events. For more information about partial trust, refer to Objective 1.3,
Objective 2.5: Design Presentation Behavior
Chapter 2
149
“Design the Security Implementation,” in Chapter 1, “Designing the Layers of a Solution.” For more information about deploying ClickOnce applications with full trust, refer to Objective 4.1, “Define a Client Deployment Strategy,” in Chapter 4, “Planning a Solution Deployment.” More Info Partial Trust Drag-and-Drop Operations
For detailed instructions about how to implement drag-and-drop functionality in a partial trust environment, read “Implementing Drag Drop Operations for Browser Based WPF Applications (XBAP)” at http://www.codeproject.com/KB/WPF/XBAPDragDrop.aspx.
The General Sequence of a Drag-and-Drop Operation The general sequence of events that takes place in a drag-and-drop operation is as follows: 1. The drag-and-drop operation is initiated by calling the DoDragDrop method on the
source control (for Windows Forms) or the DragDrop.DoDragDrop method for WPF applications. This is usually done in the MouseDown event handler. DoDragDrop c opies the desired data from the source control to a new instance of DataObject and sets flags that specify which effects are allowed with this data. 2. The GiveFeedBack and QueryContinueDrag events are raised at this point. The
GiveFeedback event handler can set the mouse pointer to a custom shape, and the QueryContinueDrag event handler can be used to determine if the drag operation should be continued or aborted. 3. The mouse pointer is dragged over a target control. Any control that has the
AllowDrop property set to True is a potential drop target. When the mouse pointer enters a control with the AllowDrop property set to True, the DragEnter event for that control is raised. The DragEventArgs object that the event handler receives can be examined to determine if data appropriate for the target control is present. If so, the Effect property of the DragEventArgs object then can be set to an appropriate value. 4. The user releases the mouse button over a valid target control, raising the DragDrop
event. The code in the DragDrop event handler then obtains the dragged data and takes whatever action is appropriate in the target control.
The DragDropEffects Enumeration To complete a drag-and-drop operation, the drag effect specified in the DoDragDrop method must match the value of the Effect parameter of the DragEventArgs object associated with the drag-and-drop event, which is generally set in the DragEnter handler. The Effect property is an instance of the DragDropEffects enumeration. The members of the DragDropEffects enumeration are described in Table 2-15.
150
Chapter 2
Designing the Presentation Layer
Table 2-15 DragDropEffects Enumeration Members
Member
Explanation
All
Data is copied, removed from the drag source, and scrolled in the target.
Copy
The data is copied to the target.
Link
The data is linked to the target.
Move
The data is moved to the target.
None
The target does not accept the data.
Scroll
Scrolling is about to start or is currently occurring in the target.
Note that the main function of the Effect parameter is to change the mouse cursor when it is over the target control. The value of the Effect parameter has no actual effect on the action that is executed except that when the Effect parameter is set to None, no drop can take place on that control because the DragDrop event will not be raised.
Initiating the Drag-and-Drop Operation in Windows Forms Applications The drag-and-drop operation is initiated by calling the DoDragDrop method on the source control. The DoDragDrop method takes two parameters: an Object, which represents the data to be copied to the DataObject, and an instance of DragDropEffects, which specifies what drag effects will be allowed with this data. The following example demonstrates how to copy the text from a text box and set the allowed effects to Copy or Move: Sample of Visual Basic.NET Code Private Sub TextBox1_MouseDown(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles TextBox1.MouseDown TextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Copy Or DragDropEffects.Move) End Sub Sample of Visual C# Code private void textBox1_MouseDown(object sender, MouseEventArgs e) { textBox1.DoDragDrop(textBox1.Text, DragDropEffects.Copy | DragDropEffects.Move); }
Note that you can use the Or operator (Visual Basic) or the | operator (C#) to combine members of the DragDropEffects enumeration to indicate multiple effects.
Initiating the Drag-and-Drop Operation in WPF Applications In WPF applications, you initiate the drag-and-drop operation by calling DragDrop.Do DragDrop. This method takes three parameters: a DependencyObject that represents the source control for the drag operation, an Object that represents that data that will be copied
Objective 2.5: Design Presentation Behavior
Chapter 2
151
to the DataObject, and an instance of DragDropEffects, which specifies what drag effects will be allowed with this data. The following example demonstrates how to copy the text from a text box and set the allowed effects to Copy or Move: Sample of Visual Basic.NET Code Private Sub TextBox1_MouseDown(ByVal sender As System.Object, _ ByVal e As System.Windows.Input.MouseButtonEventArgs) _ Handles TextBox1.MouseDown DragDrop.DoDragDrop(TextBox1, TextBox1.Text, DragDropEffects.Copy Or DragDropEffects.Move) End Sub Sample of Visual C# Code private void textBox1_MouseDown(object sender, MouseButtonEventArgs e) { DragDrop.DoDragDrop(textBox1, textBox1.Text, DragDropEffects.Copy | DragDropEffects. Move); }
Handling the DragEnter Event The DragEnter event should be handled for every target control. This event occurs when a drag-and-drop operation is in progress and the mouse pointer enters the control. This event passes a DragEventArgs object to the method that handles it, and you can use the DragEventArgs object to query the DataObject associated with the drag-and-drop operation. If the data is appropriate for the target control, you can set the Effect property to an appropriate value for the control. The following example demonstrates how to examine the data format of the DataObject and set the Effect property: Sample of Visual Basic.NET Code ' This is a Windows Forms example Private Sub TextBox2_DragEnter(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.DragEventArgs) _ Handles TextBox2.DragEnter If e.Data.GetDataPresent(DataFormats.Text) = True Then e.Effect = DragDropEffects.Copy End If End Sub ' This is a WPF example Private Sub TextBox2_DragEnter(ByVal sender As System.Object, _ ByVal e As System.Window.DragEventArgs) _ Handles TextBox2.DragEnter If e.Data.GetDataPresent(DataFormats.Text) = True Then e.Effect = DragDropEffects.Copy End If End Sub Sample of Visual C# Code // The Windows Forms and WPF examples look the same in C# private void textBox2_DragEnter (object sender, DragEventArgs e)
152
Chapter 2
Designing the Presentation Layer
{ if (e.Data.GetDataPresent(DataFormats.Text)) { e.Effect = DragDropEffects.Copy; } }
Note that in WPF applications, this is an attached event that is based off of the DragDrop class. You can attach this event in the Events pane of the Properties window in Visual Studio.
Handling the DragDrop Event When the mouse button is released over a target control during a drag-and-drop operation, the DragDrop event is raised. In the method that handles the DragDrop event, you can use the GetData method of the DataObject to retrieve the copied data from the DataObject and take whatever action is appropriate for the control. The following example demonstrates how to drop a String into a TextBox: Sample of Visual Basic.NET Code ' This is a Windows Forms example Private Sub TextBox2_DragDrop(ByVal sender As System.Object, ByVal e As _ System.Windows.Forms.DragEventArgs) Handles TextBox2.DragDrop TextBox2.Text = TryCast(e.Data.GetData(DataFormats.Text), String) End Sub ' This is a WPF example Private Sub TextBox2_DragDrop(ByVal sender As System.Object, ByVal e As _ System.Windows.DragEventArgs) Handles TextBox2.DragDrop TextBox2.Text = TryCast(e.Data.GetData(DataFormats.Text), String) End Sub Sample of Visual C# Code // The Windows Forms and WPF examples look the same in C# private void textBox2_DragDrop(object sender, DragEventArgs e) { textBox2.Text = (string)e.Data.GetData(DataFormats.Text); }
Note that in WPF applications, this is an attached event that is based off the DragDrop class. You can attach this event in the Events pane of the Properties window in Visual Studio.
Implementing Drag-and-Drop Operations Between Applications The system intrinsically supports drag-and-drop operations between .NET Framework applications. You don’t need to take any additional steps to enable drag-and-drop operations that take place between applications. The only conditions that must be satisfied to enable a drag-and-drop operation between applications are: ■■
The target control must allow one of the drag effects specified in the DoDragDrop method call.
Objective 2.5: Design Presentation Behavior
Chapter 2
153
■■
The target control must accept data in the format that was set in the DoDragDrop method call.
Objective Summary ■■
Attached events allow you to enable any WPF element to respond to events raised by any other WPF element.
■■
Triggers allow WPF UIs to respond dynamically to user input or actions.
■■
Animation allows you to alter the appearance of WPF elements in real time.
■■
Drag-and-drop operations are supported in both Windows Forms and WPF UIs.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of the chapter. 1. Look at the following XAML snippet:
When will TextBox1 appear with a red background? A. When the mouse is over TextBox1 B. When TextBox1 is focused C. When TextBox1 is focused and the mouse is over TextBox1 D. All of the above E. Never
154
Chapter 2
Designing the Presentation Layer
2. Which of the following events must be handled to execute a drag-and-drop operation?
(Choose all that apply.) A. MouseDown B. MouseUp C. DragLeave D. DragDrop
Thought Experiment Designing UI Behavior
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for the Graphic Design Institute. The Graphic Design Institute is creating a new XBAP WPF application to allow users to upload image files. They are designing the application to be extremely user-friendly; the UI design highlights every selection they need to make. The developers have designed a UI with these attributes: ■■
Users will drag a file from Windows Explorer to the WPF window. The file name will be displayed in a text box.
■■
If the selected file has one of the valid image extensions, the button will animate.
■■
Buttons on other pages will use the same animation when the form is ready to be submitted.
Answer the following questions about how to implement the application:
1. How can the developers initiate the button animation? 2. How can they implement the animation itself? 3. Will the drag-and-drop operation work as expected? If not, what can the developers do?
Objective 2.6: Design for UI Responsiveness While the primary function of the Presentation layer is to interact with the user, you also can use the computer power of the client by handling computing tasks that do not require communication with the server or data layers. Using multithreaded techniques enables you to use the processing power of the client while maintaining responsiveness in the UI.
Objective 2.6: Design for UI Responsiveness
Chapter 2
155
This objective covers how to: ■■
Offload operations from the UI thread.
■■
Report progress.
■■
Avoid unnecessary screen refreshes.
■■
Determine whether to sort and filter data on the client or server.
Offloading Operations from the UI Thread and Reporting Progress The BackgroundWorker component is designed to allow you to execute time-consuming operations on a separate, dedicated thread. This allows you to run operations that take a lot of time, such as file downloads and database transactions asynchronously and allow the UI to remain responsive. The key method of the BackgroundWorker component is the RunWorkerAsync method. When this method is called, the BackgroundWorker component raises the DoWork event. The code in the DoWork event handler is executed on a separate, dedicated thread, allowing the UI to remain responsive.
Announcing the Completion of a Background Process When the background process terminates, whether because the process is completed or because the process is cancelled, the RunWorkerCompleted event is raised. You can alert the user to the completion of a background process by handling the RunWorkerCompleted event.
Returning a Value from a Background Process You might want to return a value from a background process. For example, if your process is a complex calculation, you would want to return the end result. You can return a value by s etting the Result property of DoWorkEventArgs in DoWorkEventHandler. This value will then be available in the RunWorkerCompleted event handler as the Result property of the RunWorkerCompletedEventArgs parameter.
Cancelling a Background Process You might want to implement the ability to cancel a background process. BackgroundWorker supports the ability to cancel a background process, but you must implement most of the cancellation code yourself. The WorkerSupportsCancellation property of the BackgroundWorker component indicates whether the component supports cancellation. You can call the CancelAsync method to attempt to cancel the operation; doing so sets the CancellationPending property of the BackgroundWorker component to True. By polling the CancellationPending property of the BackgroundWorker component, you can determine whether to cancel the operation.
156
Chapter 2
Designing the Presentation Layer
Reporting Progress of a Background Process with BackgroundWorker For particularly time-consuming operations, you might want to report progress back to the p rimary thread. You can report progress of the background process by calling the ReportProgress method. This method raises the BackgroundWorker.ProgressChanged event and allows you to pass a parameter that indicates the percentage of progress that has been completed to the methods that handle that event. The following example demonstrates how to call the ReportProgress method from within the BackgroundWorker.DoWork event handler and then to update a ProgressBar control in the BackgroundWorker.ProgressChanged event handler: Sample of Visual Basic.NET Code Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) _ Handles BackgroundWorker1.DoWork For i As Integer = 1 to 10 RunTimeConsumingProcess() ' Calls the Report Progress method, indicating the percentage ' complete BackgroundWorker1.ReportProgress(i*10) Next End Sub Private Sub BackgroundWorker1_ProgressChanged( _ ByVal sender As System.Object, _ ByVal e As System.ComponentModel.ProgressChangedEventArgs) _ Handles BackgroundWorker1.ProgressChanged ProgressBar1.Value = e.ProgressPercentage End Sub Sample of C# Code private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 1;i < 11; i++) { RunTimeConsumingProcess(); // Calls the Report Progress method, indicating the percentage // complete backgroundWorker1.ReportProgress(i*10); } } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; }
Note that in order to report progress with the BackgroundWorker component, you must set the WorkerReportsProgress property to True.
Objective 2.6: Design for UI Responsiveness
Chapter 2
157
Requesting the Status of a Background Process You can determine if a BackgroundWorker component is executing a background process by reading the IsBusy property. The IsBusy property returns a Boolean value. If True, the BackgroundWorker component is currently running a background process. If False, the BackgroundWorker component is idle.
Creating Process Threads For applications that require more precise control over multiple threads, you can create new threads with the Thread object. The Thread object represents a separate thread of execution that runs concurrently with other threads. You can create as many Thread objects as you like, but the more threads there are, the greater the impact on performance and the greater the possibility of adverse threading conditions, such as deadlocks.
Creating and Starting a New Thread The Thread object requires a delegate to the method that will serve as the starting point for the thread. This method must be a Sub (void in C#) method and must take either no parameters or a single Object parameter. In the latter case, the Object parameter is used to pass any required parameters to the method that starts the thread. Once a thread is created, you can start it by calling the Thread.Start method. The following example demonstrates how to create and start a new thread: Sample of Visual Basic.NET Code Dim aThread As New System.Threading.Thread(Addressof aMethod) aThread.Start() Sample of C# Code System.Threading.Thread aThread = new System.Threading.Thread(aMethod); aThread.Start();
For threads that accept a parameter, the procedure is similar, except that the starting method can take a single Object as a parameter and that object must be specified as the parameter in the Thread.Start method. An example is shown below: Sample of Visual Basic.NET Code Dim aThread As New System.Threading.Thread(Addressof aMethod) aThread.Start(anObject) Sample of C# Code System.Threading.Thread aThread = new System.Threading.Thread(aMethod); aThread.Start(anObject);
158
Chapter 2
Designing the Presentation Layer
Destroying Threads You can destroy a Thread object by calling the Thread.Abort method. This method causes the thread on which it is called to cease its current operation and to raise a ThreadAbortException. If there is a Catch block that is capable of handling the exception, it will execute along with any Finally blocks. The thread then is destroyed and cannot be restarted. Sample of Visual Basic.NET Code aThread.Abort() Sample of C# Code aThread.Abort();
Synchronizing Threads Two of the most common difficulties involved in multithread programming are deadlocks and race conditions. A deadlock occurs when one thread has exclusive access to a particular variable and then attempts to gain exclusive access to a second variable at the same time that a second thread has exclusive access to the second variable and attempts to gain exclusive access to the variable that is locked by the first thread. The result is that both threads wait indefinitely for the other to release the variables and they cease operating. A race condition occurs when two threads attempt to access the same variable at the same time. For example, consider two threads that access the same collection. The first thread might add an object to the collection. The second thread then might remove an object from the collection based on the index of the object. The first thread then might attempt to access the object in the collection to find that it had been removed. Race conditions can lead to unpredictable effects that can destabilize your application. The best way to avoid race conditions and deadlocks is by careful programming and judicious use of thread synchronization. You can use the SyncLock keyword in Visual Basic and the lock keyword in C# to obtain an exclusive lock on an object. This allows the thread that has the lock on the object to perform operations on that object without allowing any other threads to access it. Note that if any other threads attempt to access a locked object, those threads will pause until the lock is released. The following example demonstrates how to obtain a lock on an object: Sample of Visual Basic.NET Code SyncLock anObject ' Perform some operation End SyncLock Sample of C# Code lock (anObject) { // Perform some operation }
Objective 2.6: Design for UI Responsiveness
Chapter 2
159
Some objects, such as collections, implement a synchronization object that should be used to synchronize access to the greater object. The following example demonstrates how to obtain a lock on the SyncRoot object of an ArrayList object: Sample of Visual Basic.NET Code Dim anArrayList As New System.Collections.ArrayList SyncLock anArrayList.SyncRoot ' Perform some operation on the ArrayList End SyncLock Sample of C# Code System.Collections.Arraylist anArrayList = new System.Collections.ArrayList(); lock (anArrayList.SyncRoot) { // Perform some operation on the ArrayList }
It is generally good practice when creating classes that will be accessed by multiple threads to include a synchronization object that is used for synchronized access by threads. This allows the system to lock only the synchronization object, thus conserving resources by not having to lock every single object contained in the class. A synchronization object is simply an instance of Object and does not need to have any functionality except to be available for locking. The following example demonstrates a class that exposes a synchronization object: Sample of Visual Basic.NET Code Public Class aClass Public SynchronizationObject As New Object() ' Insert additional functionality here End Class Sample of C# Code public class aClass { public object SynchronizationObject = new Object(); // Insert additional functionality here }
Special Considerations When Working with Controls Because controls are always owned by the UI thread, it is generally unsafe to make calls to controls from a different thread. In WPF applications, you can use the Dispatcher object, discussed later in this section, to make safe function calls to the UI thread. In Windows Forms applications, you can use the Control.InvokeRequired property to determine if it is safe to make a call to a control from another thread. If InvokeRequired returns False, it is safe to make the call to the control. If InvokeRequired returns True, however, you should use the Control. Invoke method on the owning form to supply a delegate to a method to access the control. Using Control.Invoke allows the control to be accessed in a thread-safe manner. The following example demonstrates setting the Text property of a TextBox control named Text1:
160
Chapter 2
Designing the Presentation Layer
Sample of Visual Basic.NET Code Public Delegate Sub SetTextDelegate(ByVal t As String) Public Sub SetText(ByVal t As String) If TextBox1.InvokeRequired = True Then Dim del As New SetTextDelegate(AddressOf SetText) Me.Invoke(del, New Object() {t}) Else TextBox1.Text = t End If End Sub Sample of C# Code public delegate void SetTextDelegate(string t); public void SetText(string t) { if (textBox1.InvokeRequired) { SetTextDelegate del = new SetTextDelegate(SetText); this.Invoke(del, new object[]{t}); } else { textBox1.Text = t; } }
In the preceding example, the method tests InvokeRequired to determine if it is dangerous to access the control directly. In general, this will return True if the control is being accessed from a separate thread. If InvokeRequired does return True, the method creates a new instance of a delegate that refers to itself and calls Control.Invoke to set the Text property in a thread-safe manner.
Using Dispatcher to Access Controls Safely on Another Thread in WPF At times, you might want to change the UI from a worker thread. For example, you might want to enable or disable buttons based on the status of the worker thread, or provide more detailed progress reporting than is allowed by the ReportProgess method. The WPF threading model provides the Dispatcher class for cross-thread calls. Using Dispatcher, you can update your UI safely from worker threads. You can retrieve a reference to the Dispatcher object for a UI element from its Dispatcher property, as shown here: Sample of Visual Basic.NET Code Dim aDisp As System.Windows.Threading.Dispatcher aDisp = Button1.Dispatcher Sample of C# Code System.Windows.Threading.Dispatcher aDisp; aDisp = button1.Dispatcher;
Objective 2.6: Design for UI Responsiveness
Chapter 2
161
Dispatcher provides two principal methods that you will use: BeginInvoke and Invoke. Both methods allow you to call a method safely on the UI thread. The BeginInvoke method allows you to call a method asynchronously, and the Invoke method allows you to call a method synchronously. Thus, a call to Dispatcher.Invoke will block execution on the thread on which it is called until the method returns, whereas a call to Dispatcher.BeginInvoke will not block execution. Both the BeginInvoke and Invoke methods require you to specify a delegate that points to a method to be executed. You also can supply a single parameter or an array of parameters for the delegate, depending on the requirements of the delegate. You also are required to set the DispatcherPriority property, which determines the priority with which the delegate is executed. In addition, the Dispatcher.Invoke method allows you to set a period of time for the Dispatcher to wait before abandoning the invocation. The following example demonstrates how to invoke a delegate named MyMethod using BeginInvoke and Invoke: Sample of Visual Basic.NET Code Dim aDisp As System.Windows.Threading.Dispatcher = Button1.Dispatcher ' Invokes the delegate synchronously aDisp.Invoke(System.Windows.Threading.DispatcherPriority.Normal, MyMethod) ' Invokes the delegate asynchronously aDisp.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, MyMethod) Sample of C# Code System.Windows.Threading.Dispatcher aDisp = button1.Dispatcher; // Invokes the delegate synchronously aDisp.Invoke(System.Windows.Threading.DispatcherPriority.Normal, MyMethod); // Invokes the delegate asynchronously aDisp.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, MyMethod);
Avoiding Unnecessary Screen Refreshes Client applications refresh the screen when the visible contents of a control are updated. Refreshing the screen is processor-intensive, and can negatively affect the performance of applications such as Remote Desktop, which transfer the UI across the network. While most Windows applications will work fine without any special planning, there are several best practices you can follow to avoid unnecessary screen refreshes: ■■
Reduce the default animation rate of 60 frames per second (fps). The following code sample sets it to 20 fps: Sample of Visual Basic.NET Code Timeline.DesiredFrameRateProperty.OverrideMetadata( _ GetType(Timeline), _ New FrameworkPropertyMetadata() With {Key.DefaultValue = 20}) Sample of C# Code Timeline.DesiredFrameRateProperty.OverrideMetadata( typeof(Timeline), new FrameworkPropertyMetadata { DefaultValue = 20 } );
162
Chapter 2
Designing the Presentation Layer
■■
■■
Reduce the animation rate for a Storyboard or individual objects within a Storyboard. By default, animations run at 60 fps. The lower the value of the Timeline.DesiredFrame Rate attached property, the better your performance will be. WPF will refresh controls automatically when you specify a DependencyProperty or implement INotifyPropertyChanged. Avoid rapidly updating values that will initiate a refresh; for example, by updating the value within a loop. Instead, update the value once after the loop has completed.
Determining Whether to Sort and Filter Data on the Client or Server Many tasks can be performed on either the client or the server. The most important considerations are as follows: ■■
■■
■■
■■
Security Security-related tasks, such as authentication, authorization, auditing, and data validation, always must occur on the server. Client applications should never be trusted. Responsiveness Perform tasks on the client to provide the best responsiveness. For example, imagine a WPF application that retrieves a list of products from a WCF web service. If the user wants to sort the list differently, the WPF application could re-sort the list in memory, or it could send a second request to the web service to retrieve the list in a different order. Sending the request to the web service incurs a delay, however, because the request and response must be sent across the network. The higher the network latency, the longer the delay. Client and server load Performing tasks on the client increases the load on the client, and performing tasks on the server increases the load on the server. For clients with sufficient processing power, perform the task on the client to improve server scalability. If your client computers do not have the processing power necessary to perform a task in a reasonable time, perform the task on the server. Network utilization Every task that you perform on the server requires data to be sent from the client to the server. This increases network utilization. While it might be difficult to notice the impact on a higher-performance local area network (LAN), wireless and wide area network (WAN) links can be saturated much more easily, slowing the performance of every application running on the network.
As a general rule, process non-security Presentation layer tasks on the client, and process Business Logic layer tasks on the server. If a particular Business Logic layer task would be much faster to process on the client, then create a separate client-side Business Logic layer assembly, and perform that processing on the client (but validate the data on the server).
Objective 2.6: Design for UI Responsiveness
Chapter 2
163
Addressing UI Memory Issues UI elements can consume a great deal of memory if not used carefully. Follow these best practices to avoid common UI-related memory leaks in WPF: ■■
■■
Un-register event handlers from a child window to a parent window when you no longer need them. Otherwise, the child window will remain in memory until the parent window is closed, even if the user closes the child window. Un-register event handlers to static objects when you no longer need them. Static objects always stay alive in memory.
■■
Stop Timer objects when you no longer need them.
■■
Set the TextBox.UndoLimit property if you plan to update a TextBox repeatedly.
■■
■■
■■
If a data-binding target refers back to the class that is bound to it, manually remove the binding before closing the window. For detailed information, refer to Microsoft Knowledge Base article 938416 at http://support.microsoft.com/kb/938416/. Freeze objects whenever possible. Freezing an object disables change notifications, improving performance and reducing memory usage. For example, if you create a brush to set the background color of an object, the .NET Framework must monitor that brush for changes so that the changes can be reflected in any objects that use the brush. Freezing the brush would spare the .NET Framework from having to do that task. Freezable objects derive from the Freezable class and are usually graphics-related, including bitmaps, brushes, pens, and animations. Before freezing an object, verify that the value of the CanFreeze property is true. For detailed information, read “Freezable Objects Overview” at http://msdn.microsoft.com/library/ms750509.aspx. Use the CLR Profiler, described in Objective 5.3 in Chapter 5, to identify memory leaks. If you examine the working set using Task Manager or Performance Monitor, you will see an exaggerated memory size. To get a more realistic size, minimize your .NET Framework application. The CLR Profiler is available as a free download at http://www.microsoft.com/download/en/details.aspx?id=16273.
More Info Addressing Memory Leaks
For more information, read “Finding Memory Leaks in WPF-based applications” at http:// blogs.msdn.com/b/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-basedapplications.aspx, “Memory Leaks in WPF applications” at http://svetoslavsavov.blogspot. com/2010/05/memory-leaks-in-wpf-applications.html, and “Top 11 WPF Performance Tips” at http://www.wpftutorial.net/10PerformanceTips.html. For information about why examining the working set using Task Manager or Performance Monitor is misleading, read “How much memory does my .NET application use?” at http://www.itwriting.com/ dotnetmem.php.
164
Chapter 2
Designing the Presentation Layer
Objective Summary ■■
■■ ■■
The BackgroundWorker component encapsulates a worker thread and provides methods to report progress from that thread. You can create new threads directly using the Thread object. When using threads that communicate directly with the UI thread, you must take care to avoid cross-thread function calls. In Windows Forms interfaces, query the Control. InvokeRequired property to determine if Invoke is required. In WPF, use the Dispatcher object to communicate safely with the UI thread.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect are located in the “Answers” section at the end of the chapter. 1. Which of the following are required to start a background process with the Back-
groundWorker component? (Choose all that apply.) A. Calling the RunWorkerAsync method B. Handling the DoWork event C. Handling the ProgressChanged event D. Setting the WorkerSupportsCancellation property to True 2. Which of the following are good strategies for updating the UI from the worker
thread? (Choose all that apply.) A. Use Dispatcher.BeginInvoke to execute a delegate to a method that updates the UI. B. Invoke a delegate to a method that updates the UI. C. Set the WorkerReportsProgress property to True, call the ReportProgress method in
the background thread, and handle the ProgressChanged event in the main thread. D. Call a method that updates the UI from the background thread.
Objective 2.6: Design for UI Responsiveness
Chapter 2
165
Thought Experiment Designing a Responsive User Interface
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Margie’s Travel. Margie’s Travel recently began developing
a new WPF application that its travel agents will use to book flights, hotels, and activities. The priorities for the application are: ■■
To remain responsive when the application looks up information using public web services
■■
To run well on low-powered computers with limited memory
■■
To guide agents through the UI using animations to notify them of the progress of web services and to draw their attention to the next step
The developers have designed an application with these attributes: ■■
All web service requests occur in background processes.
■■
The agent can sort lists by price, time, or company by clicking column headings. Sorting occurs at the client.
■■
Pictures and video related to hotels and activities are loaded only when the user clicks a button.
Answer the following questions about the future performance of the application:
1. Will using a background process allow the application to remain responsive while web service requests occur? If so, what would you recommend the developers do differently?
2. Is it the right choice to sort lists on the client, or should the sorting occur on the server? Why?
3. Might the animations affect the performance of lower-powered computers? If so, what would you recommend the developers do differently?
4. Are there any free tools you could recommend to catch memory leaks in the application?
166
Chapter 2
Designing the Presentation Layer
Chapter Summary ■■
■■
■■
■■
■■
■■
■■
■■
There are two dominant technologies for Presentation layer development—Windows Forms and WPF. Both have inherent advantages and disadvantages. Windows Forms has a strong and dedicated developer base and offers superior globalization and localization technology. WPF is relatively new and has not been adopted as strongly by developers, but it offers substantial improvements in the interactivity of the UI. You can use Windows Forms and WPF controls in the other application type through interoperability. Your UI should be designed with the user in mind. The principles of good UI include: ■■
Structure
■■
Simplicity
■■
Visibility
■■
Feedback
■■
Tolerance
■■
Reuse
You should design for inheritance and code reuse whenever possible. Use of resources in WPF and extended controls in Windows Forms are examples of code reuse. Your UI should be designed for accessibility, and thus it should support standard system settings, ensure compatibility with a high-contrast mode, provide keyboard access to all important functionality, provide notification of the keyboard focus location, and convey no important information by sound alone. Application workflow can be simple or complex. For applications that require navigation, Navigation applications in WPF provide a range of options from simple navigation to highly structured branching. Windows Forms applications can use MDI technology for navigation. Data validation is most commonly handled in the Presentation layer. Both Windows Forms and WPF provide technology to create and implement validation rules in your UI. Management of data binding is usually handled through an intermediary class—the DataSource in Windows Forms and ICollectionView in WPF. In addition, WPF provides technology that enables direct binding to XML data. WPF provides a great deal of technology to implement different behaviors in the UI, including attached events, triggers, and animation. Drag-and-drop functionality is very similar in both Windows Forms and WPF. The BackgroundWorker object encapsulates a background task and contains functionality to enable stopping and starting the task, as well as reporting to the main thread. For finer control, Thread objects can be created directly. In Windows Forms, you must query the Control.InvokeRequired property of a control to ensure safe cross-thread access. In WPF, you can use the Dispatcher object for safe cross-thread access. Chapter Summary
Chapter 2
167
Answers This section contains the answers to the Object Reviews and the Thought Experiments.
Objective 2.1: Review 1. Correct Answer: B A. Incorrect: The WindowsFormsHost allows you to host a Windows Forms control in
a WPF application. B. Correct: The ElementHost allows you to host a WPF control in a Windows Forms
application. C. Incorrect: The Grid element is a container that hosts other controls in a WPF
application, but it cannot be used independently in Windows Forms applications. D. Incorrect: The Form class is the base class for Windows Forms application, but it
cannot host WPF controls independently. 2. Correct Answer: D A. Incorrect: Windows Forms lack the required responsiveness to styles for this
application. B. Incorrect: WPF does not have an inherent MaskedTextBox control. C. Incorrect: Windows Forms lack the required responsiveness to styles, and added
WPF controls would not be helpful in this instance. D. Correct: A WPF application will be responsive to styles, but you can use the
Windows Forms MaskedTextBox control for the specialized input.
Objective 2.1: Thought Experiment 1. For this application, WPF is the best choice for implementing the Presentation layer.
While there are some globalization and localization concerns for which Windows Forms would offer a superior development experience, they are fairly minor, requiring no changes in the presentation language of the UI, and these concerns are outweighed by the WPF advantages in the use of Styles and multimedia presentation. 2. User responsiveness can be maintained by using the BackgroundWorker component to
process the background tasks while the UI remains responsive. When the UI needs to be updated from the background thread, this can be done safely using this Dispatcher object.
Objective 2.2: Review 1. Correct Answer: C A. Incorrect: It is not necessary to provide audio cues for all important information,
and it is important to convey no information by sound alone. 168
Chapter 2
Designing the Presentation Layer
B. Incorrect: An accessible application should supply support for standard system
settings. C. Correct: An accessible application should ensure compatibility with the
high-contrast mode. D. Incorrect: An accessible application should provide documented keyboard access
to all important functionalities. 2. Correct Answer: D A. Incorrect: Because these Brush objects need to be used in many different
lements, defining them at the individual element level would require a great deal e of redundancy. B. Incorrect: Because these Brush objects need to be used in each window, defining
them at the window level would be redundant. C. Incorrect: Although defining these resources at the application level would enable use
across the entire application, it would not facilitate reuse across multiple applications. D. Correct: By defining your resources in a resource dictionary, you can share the
file between applications, as well as import the resources contained therein at the window or application level as needed.
Objective 2.2: Thought Experiment 1. It’s a good start. However, they could simplify development and improve consistency
by using styles instead. 2. Maybe. However, for accessibility, it’s better to use standard system settings for
size, color, and font. When you use standard system settings, any changes the user makes to the operating system to better support accessibility needs will be reflected in the application. Since the organization is concerned about it, it should perform accessibility testing, as described in Objective 5.2, “Evaluate and Recommend a Test Strategy,” in Chapter 5, “Designing for Stability and Maintenance.”
Objective 2.3: Review 1. Correct Answer: C A. Incorrect: The Page or PageFunction for the custom journal entry must implement
IProvideCustomContent. B. Incorrect: You must call NavigationService.AddBackEntry to create the custom
back entry. C. Correct: You do not need an instance of JournalEntry to create a custom back entry. D. Incorrect: You must create a class that inherits from CustomContentState to store
the state of the page. Answers
Chapter 2
169
2. Correct Answer: B A. Incorrect: FragmentNavigation occurs after LoadCompleted, and Navigation-
Progress occurs after Navigated. B. Correct: This is the correct order in which navigation events fire. C.
Incorrect: NavigationProgress occurs after Navigated.
D. Incorrect: FragmentNavigation occurs after LoadCompleted.
Objective 2.3: Thought Experiment 1. No, a WPF page-based application is the best choice. Page-based applications are
perfect for completing straightforward, linear tasks such as data entry. 2. Yes. You can remove pages from the journal by calling NavigationService.Remove
BackEntry repeatedly. 3. Yes (though it might be confusing to the user). To accomplish this, call
NavigationService.AddBackEntry and provide an instance of a class that derives from CustomContentState and contains the state information for the page that the user should return to when she clicks Back.
Objective 2.4: Review 1. Correct Answer: B A. Incorrect: Although data type validation will validate that the input is an integer, it
will not validate the allowed range of values correctly. B. Correct: Range checking will validate both data type and range. C. Incorrect: Lookup validation is not required because the expected data falls within
a consistent range. D. Incorrect: Complex validation is not required because the expected data falls
within a consistent range. 2. Correct Answer: D A. Incorrect: The tags must be enclosed in tags to set the data template to the ItemTemplate property. B. Incorrect: The data template must be set to the ItemTemplate property, not the
ItemsSource property. C.
Incorrect: The data template must be enclosed in tags.
D. Correct: The data template is set correctly.
170
Chapter 2
Designing the Presentation Layer
Objective 2.4: Thought Experiment 1. Overall, the design is solid. However, it has one major flaw: all data validation occurs
on the client. Client data validation is useful for improving the responsiveness of the application; however, for security and data integrity, you also must implement data validation on the server. This is particularly important because Lucerne plans to expose the web service to other clients, which might not implement proper data validation. 2. It might work, but it’s not a good UI design choice. First, it fails to indicate which
lement on a page failed to validate. Second, it provides poor accessibility because it e would not be meaningful to users who could not distinguish the colors red and green.
Objective 2.5: Review 1. Correct Answer: C A. Incorrect: A multi-trigger requires that all conditions be met before activating its
Setters. In this case, Textbox1 is not focused. B. Incorrect: A multi-trigger requires that all conditions be met before activating its
Setters. In this case, the mouse is not over Textbox1. C. Correct: A multi-trigger is active when all conditions defined in it are met. D. Incorrect: A multi-trigger requires that all conditions be met before activating its
Setters, and will only activate when all conditions are met. E. Incorrect: When all the conditions are met, the multi-trigger activates. 2. Correct Answer: D A. Incorrect: Although most drag-and-drop operations begin in the MouseDown
event on the source control, it is not required that they begin there. B. Incorrect: Although it is recommended that the DragEnter event handler be used
to examine the data object and set the Effect property as appropriate, it is not required. C. Incorrect: The DragLeave event is used to execute code when data is dragged out
of a control, but it is not necessary for the drag-and-drop operation. D. Correct: The DragDrop event is the only event that must be handled to complete
a drag-and-drop operation.
Objective 2.5: Thought Experiment 1. There are many ways to initiate the animation. The simplest would be to use validation
and validate that the file name ends in one of the acceptable image file extensions. 2. There are also many ways to implement the animation. However, an attached behavior
would simplify adding the behavior to multiple buttons.
Answers
Chapter 2
171
3. Not if it’s running in a partial trust environment, which most XBAP applications do.
They either need to configure the application for full trust (which supports true drag-and-drop functionality) or they need to simulate drag-and-drop functionality in the partial trust environment.
Objective 2.6: Review 1. Correct Answers: A and B A. Correct: RunWorkerAsync raises the DoWork event, which must be handled to run
code on the background thread. B. Correct: The DoWork event handler contains the code that will be run on the
background thread. C. Incorrect: Handling the ProgressChanged event is not required. D. Incorrect: Setting WorkerSupportsCancellation to True is not required. 2. Correct Answers: A and C A. Correct: The Dispatcher.BeginInvoke method will execute a method
asynchronously and safely on the main thread. B. Incorrect: Direct access of the UI from a background thread is not allowed. C. Correct: Using the built-in mechanism of BackgroundWorker for reporting is the
preferred way to report progress that can be expressed numerically. D. Incorrect: Direct access of the UI from a background thread is not allowed.
Objective 2.6: Thought Experiment 1. Yes. 2. Yes, the sorting should occur on the client. Sorting by these values should happen
quickly, even on lower-powered clients. In addition, because the application is connecting to public web services, the application might not have the ability to request the list to be sorted differently by the server. 3. Yes. They could lower the frame rate of the animations to reduce the performance
impact. 4. Yes, the CLR Profiler is an excellent tool for identifying memory leaks, and it can be
downloaded for free from Microsoft.
172
Chapter 2
Designing the Presentation Layer
C h apter 3
Designing the Data Access Layer E
nterprise applications provide data to users, and most collect input from users, too. Although accessing data is a common task, designing data access is a complex topic. If you make poor choices during the design phase, the application still can work, but development or maintenance might be more costly, performance or scalability might suffer, or users might not have access to data when they need it. This objective domain provides an overview of how to design the data access layer, including choosing the best data access technology, designing your data object model, planning data caching and synchronization, designing multiuser data access, and optimizing data access performance. Like all objectives for the 70-518 exam, this objective domain is design-oriented. Therefore, the content focuses on making high-level decisions rather than writing code. Before you attempt to understand the design-level concepts, you should already understand how to implement the technologies and you should have passed the 70-516 exam, “Accessing Data with Microsoft .NET Framework 4.”
Objectives in this chapter: ■■
Objective 3.1: Choose the appropriate data access strategy
■■
Objective 3.2: Design the data object model
■■
Objective 3.3: Design data caching
■■
Objective 3.4: Design offline storage and data synchronization
■■
Objective 3.5: Design for a concurrent multiuser environment
■■
Objective 3.6: Analyze data services for optimization
173
Objective 3.1: Choose the Appropriate Data Access Strategy Anytime you are accessing a database directly from a .NET application, you should use the Entity Framework as your data access layer. If clients need to access a database across the Internet, use Windows Communication Foundation (WCF) data services (formerly known as ADO.NET data services) to implement an open, standards-based Open Data Protocol (OData) and a Representational State Transfer (REST) service. This objective provides an overview of the available data access technologies and describes the scenarios in which you should choose each one.
This objective covers how to: ■■
Understand the differences between key data access technologies.
■■
Support different data sources.
■■
Choose a data access strategy.
Understanding .NET Data Access Technologies The Microsoft .NET Framework provides several different ways to access databases. The sections that follow describe the most important technologies: ADO.NET, the Entity Framework, LINQ to SQL, LINQ to Objects, and WCF Data Services.
Using ADO.NET ADO.NET provided early versions of the .NET Framework with basic, but direct, access to databases. First, you use a connection string to create a connection object. Using the connection object, you can run stored procedures; read data using DataReader, DataSet, and DataTable objects; and submit updates to the database. Although you can still use ADO.NET, recent versions of the .NET Framework support the Entity Framework (sometimes called the ADO.NET Entity Framework). The Entity Framework provides an additional layer of abstraction between the application and the database, making data easier to work with, especially for developers who do not have a database development background. For the 70-518 exam, you should be familiar with ADO.NET technologies and know that ADO.NET almost never is the best choice for data access in a new application. One exception to this is accessing flat files, such as CSV files: ADO.NET supports accessing flat files, whereas newer technologies do not.
Using the Entity Framework Databases are relational, which means tables with rows and columns relate to each other through keys and indexes. Relational databases support queries and stored procedures, which act on tables as separate entities. 174
Chapter 3
Designing the Data Access Layer
The .NET Framework, however, is object-oriented. Objects can relate to each other using references, which closely resemble the structure of a relational database. Object-oriented application design provides many powerful features that cannot be supported directly by relational databases. For example, objects can have parent-child relationships, which allow developers to access child and parent objects easily. Objects also can support methods to provide built-in behavior and logic. The Entity Framework creates a mapping from object-oriented programming to relational databases. In a few seconds, Microsoft Visual Studio can use the Entity Framework to examine an existing database and create a complex object-oriented class structure with built-in relationships and methods, allowing you to access data using classes. With Model First capabilities, Visual Studio can even create a new database based on an entity data model. Translating between a relational database and an object- oriented application does not require you to give up the power of queries, however. LINQ to Entities provides SQL-like querying for collections, including those implemented using the Entity Framework. More Info Model First
For more information, watch “Creating an Entity Data Model the Model-First Way” at http://msdn.microsoft.com/data/ff628199.
Developers often add an Entity Framework model directly to their project to act as the data access layer. If you need to use a single entity model as the data access layer for multiple applications, create a separate Visual Studio project containing only the entity model. After you add the project to your solution, create a reference to the entity model project and to System.Data.Entity. Then add the connection string to your new project’s configuration file. More Info Creating a Separate Data Access Project
For more information, watch “Consuming an Entity Data Model from a Separate .NET Project” at http://msdn.microsoft.com/data/ff628208.
Using LINQ to SQL Like the Entity Framework, LINQ to SQL uses LINQ to select and update data from an underlying database. LINQ to SQL differs from the Entity Framework in that it provides more direct access to the underlying database by allowing you to query it directly, without creating object-oriented classes that map to the underlying tables. Whereas the Entity Framework provides an application-centric view of a database, LINQ to SQL provides a database-centric view. In a nutshell, you should use LINQ to SQL only for small projects when you need to query an existing database with a good design. LINQ to SQL also performs better, though those performance differences will not be noticeable in most applications. Most of the time, you should use the Entity Framework.
Objective 3.1: Choose the Appropriate Data Access Strategy
Chapter 3
175
Using LINQ to Objects If your data is stored in a collection that implements IEnumerable or IEnumerable, you can use LINQ to Objects to query it as a database. More Info Using LINQ to Objects to Query the File System
To view an example of how to use LINQ to Objects, read “How to: Query for Files with a Specified Attribute or Name” at http://msdn.microsoft.com/library/bb546159.aspx.
If you need to query large numbers of objects, you might be able to improve performance by using Parallel LINQ (PLINQ). PLINQ uses multiple cores on the computer simultaneously to reduce the time required to process a query. More Info Using PLINQ
For more information, consult “Parallel LINQ (PLINQ)” at http://msdn.microsoft.com/library/ dd460688.aspx.
Using WCF Data Services Formerly known as ADO.NET data services, WCF Data Services allows you to build an Open Data Protocol (OData) or Representational State Transfer (REST) service quickly. OData and REST are Hypertext Transfer Protocol (HTTP)–based open standards for exposing data across the web, where they can be consumed by clients built on a wide variety of platforms, including .NET, Microsoft Silverlight, jQuery, PHP, and JavaScript. EXAM TIP
The exam might refer to WCF Data Services as “ADO.NET data services.” Because developers working with early versions of the .NET Framework were familiar with using ADO.NET to access data, Microsoft used the ADO.NET name for both WCF Data Services and the Entity Framework. However, recent Microsoft documentation seems to be dropping the ADO.NET name.
WCF Web Services and WCF Data Services have similar names, but they should be used in very different ways. WCF Data Services implement the data layer; use them when you need to provide low-level access to database tables, queries, and stored procedures. WCF Web Services are better suited to implementing the Business Logic layer; use them when your code needs to make decisions about the underlying data. To create a WCF data service, follow these steps: 1. Add a data model to your project. 2. Add a WCF data service project to your solution.
176
Chapter 3
Designing the Data Access Layer
3. Specify your data source class name as the type for the new generic DataService class. 4. Set the rules used to indicate which entity sets and service operations are available and
what privileges users have. For example, you can use SetEntitySetAccessRule to allow users to read or write a table. Visual Studio provides example methods that are commented out by default. Now the application exposes your data using OData and REST. You can view the WCF data service in a browser by right-clicking the service in Solution Explorer and then clicking View In Browser. Accessing the data using OData requires providing specially formed URLs with your query information. For example, the following request provides the entire Widget table (which has an entity framework collection name of Widgets) in XML format: /WcfDataService1.svc/Widgets To view the row with the primary key of three, use the following path: /WcfDataService1.svc/Widgets(3) You can create Language Integrated Query (LINQ) queries by adding query parameters. For example, the following path sorts the Widgets collection by title and then shows you the second set of 10 results: /WcfDataService1.svc/Widgets?$orderby=title&$skip=10&$top=10 You also can filter results, draw data from related tables, and do almost anything else you can do with LINQ. Naturally, you do not typically consume a WCF data service using your browser directly. Instead, you create a client that consumes the data. If you create a JavaScript client, the data service automatically provides data in the JSON format. More Info WCF Data Services
For more information about implementing WCF Data Services, watch “Getting Started with OData Part 1: Building an OData Service” at http://msdn.microsoft.com/data/gg601462, and then watch the other videos in the same series.
Supporting Different Data Sources When you create your own database, you can choose how you would like to store it. The .NET Framework supports connecting to many different types of databases, but for the 70-518 certification exam, you should familiarize yourself with the different editions of Microsoft SQL Server: ■■
SQL Server Express Edition A free database that you can redistribute (subject to agreement). This edition is the best choice for independent software vendors, non-professional developers, and hobbyists building client applications.
Objective 3.1: Choose the Appropriate Data Access Strategy
Chapter 3
177
■■
■■
■■
■■
■■
■■
■■
SQL Server Compact Edition A free, embedded database ideal for building stand-alone and occasionally connected applications for mobile devices, desktops, and web clients on all Windows platforms. SQL Server Web Edition A low total-cost-of-ownership option for web hosts and websites that provide scalability and manageability capabilities for small to large scale web properties. SQL Server Workgroup Edition The ideal option for running branch location databases providing a reliable data management and reporting platform that includes secure, remote synchronization and management capabilities. SQL Server Standard Edition A complete data management and business intelligence platform for departments and small organizations to run their applications. SQL Server Enterprise Edition Includes all the features of SQL Server Standard, plus features that improve security, availability, and scalability. SQL Server Datacenter Edition Includes all the features of SQL Server Enterprise edition, but with greater scalability for larger workloads. SQL Server Developer Edition Includes all the features of SQL Server Datacenter edition but is licensed only for use as a development and test system. You cannot use SQL Server Developer edition in a production environment.
At times, you might need to connect to more primitive, file-based data sources. For example, you might need to import a data file provided by a partner organization. The most common types of file-based data sources are: ■■
■■
■■
Flat files The term flat file refers to data stored in a document. Typically, flat files are comma-delimited text files, a format known as CSV. Naturally, you can use the .NET Framework classes to read the text file line by line and use regular expressions to parse out the data you need. You also can use the System.VisualBasic.FileIO.TextFieldParser (even if you are a C# developer). Unfortunately, the Entity Framework and LINQ to SQL do not include classes to access flat files natively as a database—you would need to use ADO.NET. As an alternative, there are several free libraries that simplify accessing CSV files, such as FileHelpers (http://www.filehelpers.com/ ) and CsvReader (http://www.codeproject.com/KB/database/CsvReader.aspx). XML files Typically, you will use LINQ to XML to query XML files. LINQ to XML is built on top of XmlReader, which you can use directly to achieve faster performance. Microsoft Access A user-friendly database that provides built-in Business Logic and Presentation layers.
Choosing a Data Access Strategy This objective requires you to understand the different data access technologies and when you use each one. In the real world, you can use any of the technologies for many scenarios, but you should choose the Entity Framework when you have direct access to the database
178
Chapter 3
Designing the Data Access Layer
server; choose WCF Data Services when you will access the database server across HTTP, such as when you access it from a Windows Presentation Foundation (WPF) or JavaScript client on the Internet; and choose WCF Web Services when you must provide access to both the Data and Application Logic layers. The guidelines that follow will help you select the correct data access technology. Choose the Entity Framework when you: ■■
Are implementing the data layer
■■
Connect to the database server without network protocol restrictions
■■
Require rapid application development
■■
Require strongly typed objects
Choose LINQ to SQL when you: ■■
Have a well-designed database
■■
Want to interact directly with the database with little abstraction
■■
Need to optimize performance
Choose WCF Data Services when you: ■■
Are implementing the data layer
■■
Connect to the database server using HTTP
■■
Require access from JavaScript
■■
Require application-level control over access to the data
Objective Summary ■■
■■
■■
ADO.NET provided earlier versions of the .NET Framework direct access to databases. Although ADO.NET is still supported, it should not typically be used for new applications. However, ADO.NET still can provide some performance benefits, as described in Objective 3.6, “Analyze Data Services for Optimization.” There are many different versions of SQL Server, and the version you choose will depend on how you plan to use it. SQL Server Workgroup, Standard, Enterprise, and Datacenter editions are appropriate for large-scale shared databases on a network. SQL Server Express and Compact editions are appropriate for stand-alone databases. For more scenarios, you should use the Entity Framework. Choose LINQ to SQL when you want more direct access to the database and you do not need to abstract the database design from the object model. Choose WCF Data Services when you need to access a database across the Internet or using open standards.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter.
Objective 3.1: Choose the Appropriate Data Access Strategy
Chapter 3
179
1. You are designing a data access service that will allow several types of clients to
access a back-end database server running SQL Server Express edition. Non-.NET client developers plan to access the data access service across the Internet using secure open standard protocols. Developers creating .NET clients plan to access the service using faster, .NET-only protocols that use Transfer Control Protocol (TCP). Which approach should you use? A. Create a WCF data service. B. Create a WCF web service. C. Configure the firewall to forward Internet traffic to the database server. D. Generate an ADO.NET Entity Data Model. 2. You are designing a data access layer for a WCF web service that will connect to an
existing database running on the local network. You want to develop the layer as efficiently as possible, use strongly typed data objects, and use an object-oriented development model. Which data access technology should you use? A. ADO.NET B. WCF Data Services C. WCF Web Services D. WCF Entity Framework 3. You are designing a data access layer that will be accessed by remote clients on the
Internet. Developers will create clients using both WPF and JavaScript. You would like to create the data access layer with the least amount of development effort. Which data access technology should you use? A. ADO.NET B. WCF Data Services C. WCF Web Services D. WCF Entity Framework
Thought Experiment Exposing the Application Logic and Data Layers
I
n the following thought experiment, apply what you have learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter.
180
Chapter 3
Designing the Data Access Layer
You are a consultant for City Power & Light. As part of an effort to expose information about its operations more transparently to the public, City Power & Light has decided to allow third-party developers to connect directly to both the Application Logic and Data layers of its existing application, which uses WPF for the Presentation layer, WCF for the Business Logic layer, and SQL Server for the Data layer. The company’s goal is to allow developers to either create new user interfaces that connect directly to its Application Logic layer or create entire applications that access its database directly. The developers have designed a solution with these features: ■■
Decorate the methods that the Application Logic layer consists of with the WebMethod attribute.
■■
Enable port forwarding for TCP port 1433 on its firewall to allow Internet connections directly to the SQL Server.
Management wants your opinion on the application design. Answer the following questions about the future performance of the application:
1. Will third-party developers be able to create a new Presentation layer that connects to the Application Logic layer?
2. Will third-party developers be able to create a new application that connects to the database?
3. What are some of the challenges third-party developers will have connecting to the database?
4. Do you think the City Power & Light database administrators will have concerns about this architecture?
5. What benefits would WCF Data Services offer?
Objective 3.2: Design the Data Object Model The data object model is a set of custom .NET Framework classes that relate to data in a database or a file. You can use LINQ to SQL or the Entity Framework to map classes directly to an underlying database, but often the ideal design for a relational database and an objectoriented set of classes aren’t exactly the same. When the two designs don’t match, you can alter the design of your classes while still connecting directly to the underlying database. At times, database developers might change the database schema. You don’t necessarily have to change your data object model, however. In fact, you have the option of abstracting the database schema from your application and modifying the data object model mappings without recompiling your application. This can simplify application upkeep in enterprise environments.
Objective 3.2: Design the Data Object Model
Chapter 3
181
This objective covers how to: ■■
Map to persistent storage.
■■
Design a schema change management strategy.
■■
Abstract from the service layer.
Mapping to Persistent Storage A quick review of some fundamental truths about storing data: Objects in memory are lost when an application restarts. To allow important information, such as a list of products or customer details, to be stored and later retrieved, you must map your objects in memory to persistent storage. The two most common types of persistent storage are databases and files. While those fundamentals are easy to understand, the process of mapping objects to a database or file can be very complex: ■■
■■
Database mapping Databases are relational, whereas the .NET Framework is object-oriented. Databases tend to store simple, scalar types like integers and strings, whereas objects often have much more complex types. In addition, for performance and usability reasons, you might not want the .NET classes and the database to have exactly the same structure. File mapping XML files provide a structure and hierarchy, but they often have to conform to specific schema which might not be a good structure for .NET classes. In addition, XML files are text-only, requiring object instances to be serialized.
Database Mapping with LINQ to SQL You can map classes to database tables using LINQ to SQL simply by using attributes to decorate the classes and properties with the names of the associated tables and columns. For example, the following code sample shows how to associate a class named Product and a property named ProductID with a table named Products and a column named ProductID. Notice that the ColumnAttribute.Storage property specifies the field to use for private storage, bypassing any logic that might be contained within the set accessor: Sample of Visual Basic.NET Code _ Public Class Customer Private _ProductID As String _ Public Property ProductID() As String Get Return Me._ProductID End Get Set Me._ProductID = value End Set End Property End Class
182
Chapter 3
Designing the Data Access Layer
Sample of C# Code [Table(Name = "Products")] public class Customer { private string _ProductID; [Column(IsPrimaryKey=true, Storage="_ProductID")] public string ProductID { get { return this._ProductID; } set { this._ProductID = value; } } }
More Info Mapping LINQ to SQL Classes to a Database
For more information, read “The LINQ to SQL Object Model” at http://msdn.microsoft.com/ library/bb386989.aspx.
Database Mapping with the Entity Framework The simplest way to map an Entity Framework data model to a database is to use the wizards built into Visual Studio. These wizards either can build classes based on an existing database or create a new database using your existing classes as a model. At any time, you can update the data model to adapt to changes in the database schema simply by right-clicking the data model in the Visual Studio designer and selecting Update Model From Database. If you do not want to use table and column names for your classes and properties, you can modify the names of your classes and properties using the Entity Data Model Designer in Visual Studio. Simply select a column name in the designer, and then update the Name property.
File Mapping The simplest way to persist objects to a file is serialization and deserialization. You can use the DataContractSerializer class to persist objects to XML files according to a specific schema, and then read them back from those XML files. By using DataContract, DataMember, and other related attributes, you can control the structure of the XML file created by the .NET Framework when serializing your classes. For example, consider the following class: Sample of Visual Basic.NET Code Public Class Person
Objective 3.2: Design the Data Object Model
_
Chapter 3
183
Public theAddress As Address End Class
_
Sample of C# Code [DataContract(Name = "PersonContract", Namespace = "http://schemas.contoso.com")] public class Person { [DataMember(Name = "AddressMember")] public Address theAddress; }
When serialized with DataContractSerializer, the .NET Framework would create an XML file with this structure: 123 Main Street
More Info Serialization and Deserialization
For more information, read “Serialization and Deserialization” at http://msdn.microsoft. com/library/ms731073.aspx.
Designing a Schema Change Management Strategy To minimize the impact on your application when the database schema changes, design a schema change management strategy. The most important elements of a schema change management strategy are: ■■
■■
184
Knowing your schema Too often, developers have no authoritative source for their database schema. They tend to change the schema casually, without any formal change management. For example, if a database has a FirstName field that is 10 characters long, and sample data includes a first name that is 12 characters long, the developer might simply change the database schema to lengthen the field. How will other developers discover the change? How will that schema change be deployed into the production environment? If you need to re-create the database, how will you ensure that change is applied? The most common answer to these questions is to script schema changes, version those scripts along with your other code files, and apply those scripts as part of the update and rollback processes. Knowing the dependencies Track the applications that connect to a database so you can ensure those developers are aware of any schema changes. For an existing database, you can discover the applications using it by using SQL Profiler or by auditing login events.
Chapter 3
Designing the Data Access Layer
■■
■■
Versioning Ideally, a schema change management strategy is integrated into your application’s overall change management strategy, and changes to the database schema should be deployed only along with a new version of every application that uses the database. When you deploy a new version of your application to the production environment, part of that process is updating the production database schema. If the application fails in the production environment, you need to be able to roll back those schema changes as well. Testing Because schema changes can affect the application, any changes to database schema must be applied to the development environment first and then thoroughly tested in a lab environment.
More Info Database Change Management
You can use Visual Studio 2010 to manage the schema of a database and perform version control. For detailed information, read “Starting Team Database Development” at http://msdn.microsoft.com/library/dd193294.aspx.
Abstracting from the Service Layer At times, the database and your application might be too loosely coupled to implement change management. Database developers might change the database schema with little or no notice, requiring you to adapt your application as quickly as possible. For example, database developers might design a database and populate it with sample data so that you can design and test a new WCF web service. After you finish development, the database developers might add a field to one of the tables that you use, change a data type, or modify one of the field’s properties. Depending on how you designed your application, this might not impact you at all; it might require you to update a configuration file; or it might require you to update, recompile, and redeploy your entire application.
Abstracting the Entity Framework Data Model If you use the Entity Framework and might need to update the database structure without recompiling your application, choose to copy the model and mapping schemas to the build output directory rather than embedding them in the output assembly by selecting your model and setting Metadata Artifact Process to Copy To Output Directory. These three schemas work together to map a relational database to .NET classes. Within your project, they are defined within the .edmx file. When you build your project with Copy To Output Directory selected, the schemas will be created as three separate XML files as follows: ■■
Store Schema Definition Language (SSDL) Describes the tables and fields in a database
Objective 3.2: Design the Data Object Model
Chapter 3
185
■■
■■
Conceptual Schema Definition Language (CSDL) Describes your .NET Framework classes Mapping Schema Language (MSL) Describes how the Common Language Runtime (CLR) will convert between the database described in the SSDL file and the classes described in the CSDL file
By not compiling them into the assembly, you can update these files later and restart the application to adapt to changes in the underlying database. While you could update the files manually, it is easier to use the Visual Studio wizards to update the model, build the project, and use the updates files generated by Visual Studio.
Abstracting the LINQ to SQL Data Model LINQ to SQL supports using an XML mapping file to relate the database’s data model to your object model. Using a separate mapping file allows you to adapt your application to changes in the underlying database without recompiling the application. You can generate an XML mapping file using the SqlMetal.exe code generation tool. The resulting mapping file associates types and properties with fields and relationships in a database, as the following example demonstrates:
More Info LINQ to SQL Mapping Files
For general information, read “External Mapping Reference” at http://msdn.microsoft. com/library/bb386907.aspx. For information about generating a mapping file, read “How to: Generate the Object Model as an External File” at http://msdn.microsoft.com/library/ bb546173.aspx and “Using LINQ to SQL XML Mapping Files—Step by Step” at http://weblogs. asp.net/dwahlin/archive/2008/08/18/using-linq-to-sql-xml-mapping-files-step-by-step.aspx.
186
Chapter 3
Designing the Data Access Layer
Objective Summary ■■
■■
■■
Enterprise applications need to be able to store data between application restarts. Typically, data is stored in a database, although data can also be stored in files. The techniques that you use to map data to persistent storage vary slightly depending on the storage type. To avoid compatibility problems, you should integrate your database schema into your application’s change management strategy. You can use Visual Studio 2010 to manage your database schema. You can abstract your application from the data access layer by storing the schema in a separate mapping file. When the database schema changes, you can update the mapping file after compiling your application.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are planning the deployment of a new WCF web service that will act as the
Business Logic layer of an application. You are designing the web service to connect to the database using the Entity Framework. The database owner plans to update the database schema at some point in the near future, and you would like to adjust to those changes without recompiling your application. The changes will not require you to change your object model, but you will need to adjust your application’s record of the database schema and how it maps to your object model. Which files will you need to update? (Choose all that apply.) A. Update the .xml file. B. Update the .ssdl file. C. Update the .csdl file. D. Update the .msl file. 2. You are planning the deployment of a new WPF accounting application. Users will run
the application to parse XML files containing information about financial transactions. You need to design a method that will parse an XML file and extract information about a specific transaction (which the user will specify with a transaction ID) and then map that transaction to a custom object. In the future, the XML files will be replaced with a database connection. You would like the code to be as readable as possible and be upgraded to a database connection easily when it is available. Which approach would you recommend?
Objective 3.2: Design the Data Object Model
Chapter 3
187
A. Read the file with TextReader, and use String member methods to isolate the con-
tent. B. Read the file with TextReader, and use regular expressions to isolate the content. C. Use LINQ to query the file. D. Read the file with XmlReader, and use regular expressions to isolate the content.
Thought Experiment Changing a Database Layout
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Lucerne Publishing. Lucerne maintains an internal database of their authors and the books they publish. Users run a WPF application to control a WCF web service capable of managing the database and pushing changes out to distributors. Customers can access the information online using a Model-ViewController (MVC) web application. In addition, the WCF web service exposes several different methods to developers on the Internet. Recently, Lucerne created a plan to expand the information it stores about each book and author, which is as follows:
1. Select the new columns that will be added to each table. Determine how existing columns need to be updated.
2. Update the database schema on the production server. 3. Update the WCF web service to the new schema, and update methods to share the newly added information.
4. Update the WPF application. 5. Hire temporary staff to use the WPF application to populate the new information in the database.
6. Update the MVC web application. Answer the following questions about the future performance of the application:
1. What problems might the company encounter with the plan? 2. How would you change the plan to avoid the problems mentioned in the first question?
3. If Lucerne needs to update the schema in the future, is there a way to avoid recompiling the WCF web service?
188
Chapter 3
Designing the Data Access Layer
Objective 3.3: Design Data Caching When data is expensive to retrieve, such as when you must access a database or call a web service method, and you might need to access the data again, you should cache the data to improve performance. Caching keeps a copy of data in memory, so that the next time your application needs it, it does not have to wait to retrieve it from disk or across the network. Note Working with Multiple Caches
The .NET Framework has three different, but similar, caches: the ASP.NET cache, the System.Runtime.Caching memory cache, and the AppFabric cache. If you might need to switch between them, you can use CacheAdapter, available at https://bitbucket.org/glav/ cacheadapter.
This objective covers how to: ■■
Understand caching.
■■
Use MemoryCache.
■■
Cache web service results.
Understanding Caching .NET Framework 4 adds the System.Runtime.Caching namespace. If you are familiar with the System.Web.Caching namespace, System.Runtime.Caching is very similar, although the class names have changed. Microsoft moved it out of the System.Web namespace so that non-web developers did not have to create a dependency on ASP.NET classes. To use the namespace from a WPF application, you must target .NET Framework 4 instead of the default .NET Framework 4 Client Profile. In Visual Basic.NET, change the target framework by clicking the Advanced Compile Options button on the Compile tab of your project properties dialog box. In C#, change the target framework from the Application tab of your project properties dialog box. In either language, you will need to use the .NET tab of the Add Reference dialog box to reference the System.Runtime.Caching namespace. The most useful classes in the System.Runtime.Caching namespace are MemoryCache and ObjectCache (the base class). MemoryCache is a dictionary that stores objects of any type in memory. To cache an object, simply add it to the MemoryCache dictionary. If you want to customize caching (for example, to cache information from a database to isolated storage, or to create a centralized cache for servers in a farm), derive a new class from ObjectCache.
Objective 3.3: Design Data Caching
Chapter 3
189
Using MemoryCache MemoryCache stores objects only until they expire or the CLR determines that it needs to free up memory. Therefore, you never can be sure that an object is in the cache. To retrieve an object that might be cached, first you must determine whether it is still in the cache. If it is, you can access it directly from the cache. If it is not in the cache, you will need to retrieve it from the original source and (if you want) store a copy in the cache for later use. More Info MemoryCache
For more information, read “MemoryCache Class” at http://msdn.microsoft.com/library/ system.runtime.caching.memorycache.aspx.
When you add an object to the MemoryCache, you have the option of providing a CacheItemPolicy. You can use the CacheItemPolicy to specify expiration policies. The simplest expiration policies remove the object from the cache a specified amount of time after it was added (an absolute expiration) or last accessed (a sliding expiration). You can use more complex CacheItemPolicy instances to guarantee that the cached data is up to date by automatically removing an item from the cache if the file or database it was read from is updated. To discard cached output based on a dependency, add one of the following change monitors to the CacheItemPolicy.ChangeMonitors collection: ■■
HostFileChangeMonitor Monitors changes to files and folders
■■
SqlChangeMonitor Monitors changes to a database
More Info Caching Policies
For more information, read “CacheItemPolicy Class” at http://msdn.microsoft.com/library/ system.runtime.caching.cacheitempolicy.aspx.
Caching Web Services Besides caching individual objects, you can cache output from a WCF web HTTP service (such as a REST web service). Caching output allows the .NET Framework to respond to a web service request without calling the web service method at all. If the web service recently received the same request, the .NET Framework simply returns the cached output. You can configure web service caching to store cached output for a limited amount of time, to discard cached output when a dependency (such as a table in a database) is updated, and to cache separate responses for different request parameters. These settings are configured in the Web.config file, as the following example illustrates:
190
Chapter 3
Designing the Data Access Layer
After configuring the policy in the Web.config file, use the AspNetCacheProfile attribute to associate the web service method with the profile, as the following example illustrates: Sample of Visual Basic.NET Code _ _ Public Class Service _ _ Public Function GetStudent(name As String) As Student ' ... End Function End Class Sample of C# Code [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode. Allowed)] public class Service { [WebGet(UriTemplate = "{name}")] [AspNetCacheProfile("cache3min")] public Student GetStudent(string name) { // ... } }
More Info Caching WCF Web HTTP Services
For detailed instructions showing how to cache web service responses, visit http://msdn. microsoft.com/library/ee230443.aspx.
Objective Summary ■■
Caching stores a copy of data retrieved from a file or across the network in memory. You can access that data directly from memory later without waiting to retrieve it.
Objective 3.3: Design Data Caching
Chapter 3
191
■■
■■
Use the MemoryCache class to store cached data. The .NET Framework can free up unused objects automatically. You also can associate cached data with the underlying data store, invalidating the cached data if the data store is updated. This ensures the cache remains up to date. To improve web service performance, cache the web service output. Microsoft Internet Information Services (IIS) can return cached output for similar requests without running the web service method at all. Like the MemoryCache object, you can configure the cache to be invalidated when a data source is changed.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are planning the deployment of a new WPF application that reads information
from an XML file. You plan to use MemoryCache to reduce the number of file I/O requests. However, the XML file might be updated at any time, and it is critical that the application never use outdated information. What is the most efficient way to ensure that information stored in an instance of MemoryCache is up to date? A. Derive a custom class from the CacheItemPolicy class. B. Use the HostFileChangeMonitor class. C. Use the ObjectCache class. D. Derive a custom class from the ObjectCache class. 2. You are planning the deployment of a new WCF web service for a university. The
web service will provide public information about upcoming classes, including the number of available seats. The web service retrieves class information from a SQL Server Express edition database. The database is updated each time a student enrolls in the class, and the information the web service provides about a class always must be accurate. You expect the web service to be very busy when students are enrolling, and would like to minimize the processing time required for each request. Which technologies should you recommend? (Choose all that apply.) A. Use output caching. B. Create an instance of the MemoryCache object. C. Specify a file system dependency for the database file. D. Specify a database dependency for the table.
192
Chapter 3
Designing the Data Access Layer
Thought Experiment Designing a Caching Strategy
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You
can find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Fourth Coffee. Fourth Coffee is planning to use the .NET Framework to create Business Logic and Presentation layers over an existing database containing product information. The Business Logic layer will be imple-
mented using a WCF web service, and the Presentation layer will be implemented using a WPF application. Only the web service will have privileges to connect to the database. Several other applications are accessing the database server already, and the server utilization is very high. Management would like to minimize the number of database requests submitted by the new application. The developers have designed a caching strategy with these attributes: ■■
The WPF application creates an instance of MemoryCache when it starts.
■■
Every time the WPF application requires information from the web service, it will check to see if it is already available in the MemoryCache instance. If it is not, the application will retrieve the information from the web service and store a copy in the instance of MemoryCache.
■■
To ensure that the cache is always valid, every object stored in the MemoryCache instance will have a database dependency associated with it.
Answer the following questions about the future performance of the application:
1. Will the caching strategy work? Why or why not? 2. How would you design the application to meet Fourth Coffee’s goals better? 3. Without changing the database privileges, how could the developers configure a database dependency for the WPF clients?
Objective 3.3: Design Data Caching
Chapter 3
193
Objective 3.4: Design Offline Storage and Data Synchronization Mobility is one of the defining characteristics of the modern workforce. Unfortunately, intermittent connectivity is one of the defining characteristics of mobility. Your users often will be separated from your internal network, but they still will require access to their data. Fortunately, the .NET Framework provides access to the Microsoft Sync Framework, which you can use to synchronize selected data from a central database to a local data store. Users can query and update the local data store and then synchronize the changes to the central database. This objective covers offline storage and data synchronization and how to design solutions that use the Sync Framework. More Info Programming Synchronization Tasks
Like all aspects of the 70-518 exam, this objective focuses on high-level design tasks, and does not require you to understand the details of writing code. For information about how to write synchronization code, refer to “Programming Common Client and Server Synchronization Tasks” at http://msdn.microsoft.com/library/bb726010.aspx.
This objective covers how to: ■■
Determine the need for offline data storage.
■■
Use the Sync Framework.
■■
Design synchronization.
Determining the Need for Offline Data Storage Most applications are written optimistically on the assumption that dependencies will remain online, including the network connection, web services, and databases. If any dependency fails, the application also fails, and the user becomes unproductive until the dependency is brought back online. Often, this sort of optimistic design is acceptable. For example, if a failed database server prevents a user in a small business from performing accounting data entry for an hour, the user could simply do another job task that didn’t require the database, and then continue the data entry when administrators fix the database server. Alternatively, an enterprise might have a clustered database server with almost no downtime, using redundant hardware to virtually eliminate the risk of a failed dependency. At other times, applications require a more pessimistic and resilient design. For example, consider a point-of-sale (POS) application at a retail clothing shop with 100 stores around the world. The application needs to process orders from customers and track inventory within 194
Chapter 3
Designing the Data Access Layer
the local store. However, it also needs to connect to a central database at the company’s headquarters to track global sales, order additional supplies, and locate available inventory in nearby stores. Some locations would have unreliable Internet connections and would need to be able to continue processing orders locally even when the connection to the company’s headquarters is unavailable. You could use offline data storage and synchronization to process the transactions locally and then submit them to the headquarters when the Internet connection was back online. Other common scenarios requiring offline data storage and synchronization include applications running on mobile devices that will not always have an Internet connection, applications running at branch offices, and any application that must be more highly available than all its dependencies. You also might use synchronization simply to improve performance across slow networks. Note Occasionally Connected Applications
Be familiar with the acronym OCA, which stands for Occasionally Connected Applications. OCAs store data in a local database and synchronize it periodically, but never assume that the database will be available, and only access the destination database using synchronization.
Using Sync Framework The Sync Framework has built-in providers for the following types of data sources: ■■
Databases
■■
Files
■■
RSS and Atom feeds
In addition, you can create custom providers to synchronize any type of data source. The sections that follow provide more information about each of the built-in providers. More Info The Sync Framework
The Sync Framework is complex and deep. Fortunately, you do not need to have a detailed understanding of how to write code for the Sync Framework to pass the 70-518 certification exam. Instead, you should focus on understanding the high-level concepts, which this chapter attempts to provide. For detailed information, visit the Microsoft Sync Framework Development Center at http://msdn.microsoft.com/sync/.
Accessing Databases Offline Databases are the most common data source requiring synchronization. Fortunately, the .NET Framework includes the Sync Framework (formerly known as ADO.NET Sync Services or Sync Services For ADO.NET), which greatly simplifies working with data offline and then
Objective 3.4: Design Offline Storage and Data Synchronization
Chapter 3
195
synchronizing updates to the database when a connection is available. The Sync Framework supports three different scenarios: ■■
■■
■■
Download only Retrieve data from a database and cache it locally so that it can be accessed if the database is later unavailable Offline data access All the capabilities of the download only scenario, with the ability to submit updates that are synchronized later when the database is available Peer-to-peer data collaboration Synchronize data between clients on a network, such as between multiple cash registers in a retail store
At a high level, offline data access synchronization follows this process: 1. You create a synchronization session. 2. The Sync Framework compares the source (for example, a local copy of the database)
and destination (for example, the remote database server) to identify any offline updates. 3. Changes at the destination are applied to the source. 4. Changes to the source are applied to the destination.
The most important synchronization classes are as follows: ■■
■■
■■
DbServerSyncProvider, SqlSyncProvider, and SqlCeSyncProvider Synchronization providers for databases SqlServerAdapterBuilder and SyncAdapter Contain the configuration settings that the provider needs to communicate with the destination database, including the columns used to track which records have been updated and the tombstone table that specifies records that have been deleted SyncTable Represents a database table and how it will be synchronized
More Info Database Synchronization
For more information, read “Introduction to Sync Framework Database Synchronization” at http://msdn.microsoft.com/sync/bb887608 and visit http://msdn.microsoft.com/library/ bb726002.aspx.
Multiuser applications with support for offline updates have the potential for conflicts. For example, imagine an application that tracks customer databases. In the morning, a s alesperson synchronizes the database to her mobile device and goes to visit a customer. Discovering that the customer has a new address, she updates it in the database. Before she can return to the office to synchronize the update, the customer calls the headquarters and updates the address with slightly different information, and the update is applied directly to the destination database. When the salesperson returns to the office and synchronizes the database, the Sync Framework will detect that the record was changed while it was offline. The application then is responsible for resolving the conflict, a process that typically involves displaying the updated information to the user and allowing him to choose which update to store.
196
Chapter 3
Designing the Data Access Layer
In October 2010, Microsoft released the Sync Framework 4.0. One of the most important features of this update was to support database synchronization using the open-standard OData protocol. By using OData, you can synchronize databases with clients that do not support the Sync Framework, such as clients running Windows Phone 7, Silverlight, Android, or the iPhone iOS. More Info Sync Framework 4.0
To download Sync Framework 4.0, visit http://www.microsoft.com/download/en/details. aspx?id=12012.
Accessing Files Offline The Sync Framework file synchronization provider (also known as Sync Services For File Systems) offers these features: ■■
■■
■■ ■■
Synchronization of entire folders and subfolders, a filtered list of folder contents, or individual files. Synchronization of file and folder contents as well as metadata such as names, time stamps, and attributes (but not security or encryption settings). Change detection based on time stamps or file hashes. Detection of conflicts caused by multiple updates to the same file (which your application must resolve).
■■
Support for cancelling and resuming a synchronization.
■■
One-way and two-way synchronization.
■■
■■
■■
The ability to rename files within a folder without copying the entire file. Files moved between folders will be re-transferred. A preview mode that allows the application to determine what changes will occur so they can be confirmed. Reporting during the synchronization process that allows the application to display progress information to the user.
Files can be stored on a wide variety of devices, including file servers, portable media players, flash drives, and digital cameras. Because these devices might not be able to run executable files, they are considered partial participants. Clients and servers capable of running the Sync Framework are considered full participants. More Info File Synchronization
For more information, read “Introduction to Sync Framework File Synchronization Provider” at http://msdn.microsoft.com/sync/bb887623.
Objective 3.4: Design Offline Storage and Data Synchronization
Chapter 3
197
Accessing RSS and Atom Feeds RSS and Atom feeds traditionally are used to provide updates to websites, such as new articles that are posted on a blog. Feeds are not limited to web documents; they can contain updates about new products, or any other type of database update that can be expressed using XML. Because feeds are able to provide information but not store information, they are considered simple participants. You can use the Sync Framework to publish a feed or retrieve updated information from a feed. The most important synchronization classes are: ■■
FeedProducer Publishes a feed
■■
FeedConsumer Consumes a feed
Designing Synchronization If you might need to synchronize large amounts of updates or the updates might take a long time to transfer over the network, consider prioritizing data exchange. First, transfer the most critical information. Less critical information that will affect the application less if the connection is interrupted can occur afterwards, or when the user has a higher-speed link. To provide prioritization, the Sync Framework allows you to synchronize only specific tables, or to synchronize in only one direction.
Choosing a Synchronization Topology The synchronization topology describes how synchronization occurs between sources and the destination. The simplest topology is known as the hub-and-spoke, shown in Figure 3-1. In this topology, all clients synchronize directly with a single database server. For hub-and-spoke synchronization, use the DbServerSyncProvider provider class to represent the server and the SqlCeClientSyncProvider provider class to represent the SQL Server Compact edition client. More Info Hub-and-spoke Topologies
For implementation information, refer to “Offline Scenarios” at http://msdn.microsoft.com/ library/bb902819.aspx.
The Sync Framework makes it simple to implement more complex topologies, such as a full-mesh topology, as illustrated in Figure 3-2. In a full-mesh topology, clients can synchronize with multiple databases, servers, or each other. For example, imagine that a salesperson synchronized his mobile device at the company’s headquarters and made updates while travelling to a branch office. Upon arrival, he could synchronize his updates to the branch office database, which would pass the updates on to the database and the headquarters. Similarly, he could synchronize updates with other clients, and whichever client synchronized first would apply the updates to the database server.
198
Chapter 3
Designing the Data Access Layer
Client
Client
Database
Client
Client
Figure 3-1 The hub-and-spoke topology
Client
Client
Database
Database
Client
Client
Figure 3-2 The full-mesh topology
Objective 3.4: Design Offline Storage and Data Synchronization
Chapter 3
199
Naturally, you can simplify the full-mesh topology to a mixed topology, supporting peer-to-peer synchronization to a single database or synchronization to multiple databases without supporting peer-to-peer synchronization. Use the SqlSyncProvider, SqlCeSyncProvider, and DbSyncProvider provider classes. More Info Full-mesh and Mixed Topologies
For implementation information, refer to “Collaboration Scenarios” at http://msdn.microsoft. com/library/bb902853.aspx.
Database Design Considerations Designing a database for synchronization requires you to consider several unique factors: ■■
■■
■■
■■
■■
■■
Follow the principle of least privilege. While this is important in any scenario, it is worth mentioning because it can be tempting to grant all users full access to all database tables to ensure restricted privileges never cause a synchronization problem. Use auto-increment (identity) columns as a primary key only when all new records will be created on the server directly. If you create records in a table with an auto-increment primary key on a client, it is possible for another client to create a different record with the same primary key. Instead, use unique natural ID based on the row data, or use globally unique identifiers (GUIDs). As described in Objective 3.2, “Design the Data Object Model,” use versioning techniques to manage your database schema. Database schema is not propagated automatically during synchronization. Validate data on the server database. You cannot trust client databases to validate the data. Take advantage of the database server’s data validation capabilities. Back up the server database regularly. If a client experiences data corruption, synchronization could propagate it to the server. Plan your synchronization to accommodate restoring the database server to an earlier point in time. If you do need to restore the database server, design your application to detect that restoration and respond by reinitializing the client database.
Optimizing Synchronization Performance Synchronization 7can seem much slower than standard database updates because updates often are batched together and large amounts of data might need to be transferred. You can do several things to optimize performance: ■■
■■
200
Design the data structure for synchronization. For file synchronization, use more, smaller files when possible. For databases, minimize the number of rows and tables that need to be synchronized. Minimize the data synchronized by synchronizing only individual rows, rather than entire tables.
Chapter 3
Designing the Data Access Layer
■■
Stagger synchronization so that databases are not all synchronized at the same time.
■■
Specify one-way synchronization instead of two-way synchronization whenever possible.
■■
Plan for updates to occur in batches. Whereas local updates tend to happen one record at a time, synchronized updates might update dozens, hundreds, or thousands of records. Avoid using triggers on the server to respond to individual updates; instead, trigger a response on the server after the synchronization has completed.
Objective Summary ■■
■■
■■
If users need access to data when a connection to the database might not be available, you need to implement an offline data storage and synchronization technique. You can use the Sync Framework to store cached data from a database, file, or feed. You can update databases and files with offline changes later and resolve any conflicts that occur. The two most common synchronization topologies are hub-and-spoke and full-mesh. You can use the Sync Framework to implement either one.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are planning the deployment of a new WPF application that connects to a
database. The application will be used by realtors to enter information about homes that will be listed for sale. While the realtors often will have a network connection to the database, they also need to be able to view and enter information when disconnected from the network. Which technology should you use? A. The Sync Framework B. The System.Runtime.Caching namespace C. The System.Web.Caching namepsace D. WCF Web Services 2. You are planning the deployment of a new WPF application that connects to a
database. The application will be used by taxi drivers to track their fares, mileage, and efficiency. The taxis each have a mobile device capable of running the WPF application, but they also have Wi-Fi access. The taxis will connect automatically to the internal network when they are near any of the three dispatch offices, and they will connect to an ad-hoc wireless network when near another taxi from the same company.
Objective 3.4: Design Offline Storage and Data Synchronization
Chapter 3
201
Each of the three dispatch offices has its own database server. You would like the taxis to be able to upload their data to any of the three dispatch offices. In addition, if two taxis are in the same area, you would like them to exchange their data and then have whichever taxi first reaches the dispatch upload all the data it has. Which technology should you use? A. SQL Server replication B. The Sync Framework C. WCF Data Services D. ADO.NET
Thought Experiment Designing a Banking Application
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You can
find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Woodgrove Bank. Woodgrove Bank provides banking
services for individuals throughout the country. They have offices in many major cities, and each office has its own SQL Server database. All the databases share the same schema, but they have different sets of data: each regional office is responsible for maintaining its local customers’ records. Customers can access their records from other offices, however, because each of the offices is connected. Woodgrove Bank is currently developing a new WPF application that will be used by the bank tellers at each location. Bank tellers will use the application to update customer account information and process financial transactions. The application must always be able to update the local customers’ records, even if the connection to the other banks is offline. The application must never store local copies of information about customers at other locations. When the connection to other banks is online, tellers must be able to update customer records local to other banks. The developers have designed a database with these attributes: ■■
Each office will run a SQL Server database.
■■
The database will have a table named Customer_Index, with records for every customer at any location and the bank that manages each account. This table will be replicated to all regional locations.
■■
The database will have a table named Customer_Details, with records for every local customer, containing name, address, and other account information.
■■
The database will have a table named Financial_Transactions, with records for every credit and debit and the account that the transaction occurs against.
202
Chapter 3
Designing the Data Access Layer
■■
The WPF application will behave as an OCA. It will use a SQL Server Compact edition database on each teller’s computer.
■■
The WPF application will use the Sync Framework to store information locally for use when it cannot connect to a remote database.
Answer the following questions about the future performance of the application:
1. Will the application be able to update local customer records if the connection to other banks is offline? If not, why not?
2. Will the application be able to update remote customer records when the connection to the bank is online? If not, why not?
3. What would you do to simplify the design?
Objective 3.5: Design for a Concurrent Multiuser Environment Anytime more than one user will be accessing an application or database simultaneously, the development task gets much more complex. Supporting multiple users (a requirement for most business applications) requires planning for multiuser conflicts that might occur when two users attempt to update the same data at approximately the same time. You can do this by locking records, but locking records can introduce other problems that your application needs to handle. This objective covers planning for simultaneous access from multiple users, a concept known as concurrency. It also describes transactions that group together multiple operations so that all operations succeed or fail together.
This objective covers how to: ■■
Plan for multiuser conflicts.
■■
Understand deadlock conflicts.
■■
Design concurrency for web services.
■■
Use cross-tier distributed transactions.
Planning for Multiuser Conflicts When you update a record in a multiuser database (or any resource), your application must read the record from the database, present the current information to the user, wait for them to provide the updated information, and then send the updated information to the database.
Objective 3.5: Design for a Concurrent Multiuser Environment
Chapter 3
203
During the time between reading and writing the record, another user might update the same record. Simply overwriting that user’s changes would result in data loss, a problem which you can avoid by using one of these two types of locking: ■■
■■
Pessimistic locking The application locks the record before it reads it, and it keeps the record locked until the user submits the update. Any other users will be prevented from updating the record while the user has it locked. If the user fails to release the lock, the record will remain locked indefinitely or until the database releases the lock. In pessimistic locking, the application must maintain a connection to the database. Optimistic locking The application reads the record and prompts the user for the information to be updated. In optimistic locking, the application waits until the user submits the update before locking the record. Before submitting the update, the application compares the original record that it read to the current record; if it has changed (a state known as “dirty”), the application must present the updated information to the user to combine or discard the changes. The application does not need to maintain a connection to the database in optimistic locking.
EXAM TIP
The easiest way to implement optimistic locking is to add a WHERE clause to your SQL UPDATE command. Within the WHERE clause, compare every column in the table to the original values before the user edited them. This allows you to implement the update using a single command without manually locking the record first. The UPDATE will fail with a DBConcurrencyException if any of the values have changed because the WHERE clause will not find any matching records.
Pessimistic locking completely eliminates the risk of two users attempting to update data simultaneously (known as a concurrency issue), but because records are locked for a longer period, it increases the risk of deadlocks. Optimistic locking reduces the amount of time that records are locked, but it allows the possibility of two users simultaneously editing a record and requires extra code to resolve that conflict when it arises. More Info Implementing Locking
For more information, read “Optimistic Concurrency” at http://msdn.microsoft.com/library/ aa0416cz.aspx.
By default, the Entity Framework saves changes to objects without checking to see if another user has updated them. This can result in updates being lost. For example, imagine that User1 and User2 both read the same record. User1 changes the FirstName field and u pdates the record. Then, User2 changes the LastName field and updates the record. In this scenario, User1’s change to the FirstName field would be lost and there would be no notification. If you want to use optimistic locking, set the concurrency mode to Fixed by defining the following property in the conceptual layer:
204
Chapter 3
Designing the Data Access Layer
If you try to make an update with the ConcurrencyMode set to fixed and the data has been updated since it was read, the Entity Framework will throw an OptimisticConcurrency Exception, and the transaction will be rolled back. More info
optimistic locking and the entity framework
For detailed implementation information, read “Saving Changes and Managing Concurrency” at http://msdn.microsoft.com/library/bb738618.aspx and “DB Concurrency Control with .NET—Overview” at http://petermeinl.wordpress.com/2011/03/05/db-concurrency-controlwith-net/.
The Entity Framework does not support pessimistic locking directly. However, you can create a single database transaction that contains both the query to read the record and then update it. While this section has discussed pessimistic and optimistic locking in the context of a atabase, the same techniques apply to any resource that might be updated by multiple d users, including objects in memory and files on a file system.
Understanding Deadlock Conflicts Deadlocks occur when two threads of an application attempt to access the same resources at the same time. For example, imagine that there are two threads, Thread A and Thread B, and they both need to access both File C and File D. Thread A locks File C for writing. Then, Thread B locks File D for writing. Thread A then attempts to lock File D, discovers that the file is already locked, and waits. Thread B attempts to lock File C, discovers that the file is already locked, and waits. While the file edits normally might take only milliseconds, deadlocks can last until one thread times out or is terminated. While the deadlock scenario might sound overly complex, deadlocks are extremely c ommon with databases that require locking multiple tables and rows as part of a single transaction. Debugging deadlocks can be complex because you have to analyze multiple threads and determine which resources are deadlocked. Typically, deadlocks do not cause threads to halt indefinitely. Instead, the attempt to access a resource will time out at some point, and the CLR will throw an exception. However, frequent deadlocks can cause significant performance problems. More Info Deadlock Avoidance
For more information, read “Advanced Techniques to Avoid and Detect Deadlocks in .NET Apps” at http://msdn.microsoft.com/magazine/cc163618.aspx.
If you host a WCF web service in IIS, deadlocks can occur when IIS application pools run out of threads, a common scenario when multiple users issue long-running requests. In this case, you could update the code to be more thread-efficient or simply increase the number
Objective 3.5: Design for a Concurrent Multiuser Environment
Chapter 3
205
of threads available to the application pool. Increasing the threads in the application pool can reduce performance, however. To increase the number of threads, update the settings in the server’s Machine.config file. On servers running IIS 7.0 or 7.5, you can increase the number of threads using . More info increasing application pool threads
For more information, read “applicationPool Element” at http://msdn.microsoft.com/library/ dd560842.aspx and “processModel Element” at http://msdn.microsoft.com/library/7w2sway1. aspx.
Designing Concurrency for Web Services You can control whether WCF Web Services are single-threaded or multithreaded. If they are multithreaded, you have to plan to write code to perform locking and to work around deadlock conflicts. Two factors determine whether a WCF web service is single-threaded or multithreaded: instancing and concurrency. Instancing (defined by the ServiceBehavior attribute’s Instance ContextMode property) defines the conditions in which WCF creates a new process: ■■
■■
■■
Single WCF uses a single instance for the web service, regardless of the number of calls or users. PerSession WCF uses a separate instance for each user session, which means that each user gets his own instance unless a user creates multiple sessions. PerCall WCF uses a separate instance for each call, which might mean that multiple instances are created for a single user.
For each instance mode, WCF Web Services support three different concurrency modes (defined by the ServiceBehavior attribute’s ConcurrencyMode property for inbound connections, or the CallbackBehavior attribute’s ConcurrencyMode property for callbacks): ■■
■■
■■
Single Each instance context is allowed to have a maximum of one thread processing messages in the instance context at a time. Other threads will be blocked until they time out or the thread completes, but you do not have to write code to deal with threading issues. Multiple Each service instance can have multiple threads processing messages concurrently. You must create a thread-safe service to use the Multiple concurrency mode. Reentrant Each service instance processes one message at a time, but accepts re-entrant operation calls. The service accepts these calls only when it is calling out through a WCF client object. The Reentrant concurrency mode is only relevant for PerSession instancing.
If you set InstanceContextMode to Single and ConcurrencyMode to Single, the web service always will be single-threaded, messages always will be processed in the order that they are 206
Chapter 3
Designing the Data Access Layer
received, and there never will be any risk of concurrency problems. However, there could be performance problems because the web service will wait until one request is complete before processing another request. If you set InstanceContextMode to PerCall, concurrency is not relevant because each message generates a new instance, anyway. More Info Setting Instance and Concurrency Mode
For implementation information, read “Instancing” at http://msdn.microsoft.com/library/ ms752230.aspx and “Concurrency” at http://msdn.microsoft.com/library/ms752260.aspx.
Using Cross-Tier Distributed Transactions Sometimes, you need to guarantee that a group of operations succeed or fail together. For example, consider transferring money between two bank accounts. The transfer involves two operations: ■■
Removing the money from the first account
■■
Adding the money to the second account
If your application performed the first of these two operations and then failed, the money would seem to vanish from the first account. Related operations, such as debits and credits, must occur within a transaction. In a transaction, if any part of the transaction fails, all operations would be rolled back to their original state. If the transfer of money occurred within a transaction, either the money would be transferred successfully or the transfer would fail completely. However, it would be impossible for the money to simply disappear from an account without being added to a second account. Because you should use databases to store all business data, transactions typically occur when updating databases. To group database operations together using the .NET Framework, use the TransactionScope class, as the following code sample demonstrates. You can use TransactionScope to create a distributed transaction that updates multiple databases. For example, you might need to group operations for multiple databases if a user were transferring money from one bank to another. Sample of Visual Basic.NET Code Using scope As New TransactionScope() ' Update first database ' Update second database End Using Sample of C# Code using (TransactionScope scope = new TransactionScope()) { // Update first database // Update second database }
Objective 3.5: Design for a Concurrent Multiuser Environment
Chapter 3
207
Whereas a distributed transaction occurs across two or more databases, a cross-tier transaction occurs across two or more application layers. Application layers were described in Objective 1.1, “Design a Loosely Coupled Layered Architecture.” For example, imagine that a customer calls her bank’s customer service to transfer money between two accounts. The customer service representative is using a WPF application that implements the Presentation layer. The WPF application communicates to a WCF web service that implements the Business Logic layer. In turn, the web service communicates to one or more databases that implement the Data layer. A single transaction would involve all three tiers: the WPF application initiates the transaction, the web service would create the TransactionScope object and update the databases, and then the web service would communicate to the WPF application and the user whether the transaction succeeded. The WPF application would inform the user that the transfer was successful only if the transaction succeeded at all three layers. Only the layer that initiates a transaction can commit it; however, any layer can cancel a transaction. Typically, cross-tier transactions are initiated and committed from the Business Logic layer. The Data layer must also support transactions, as most databases do. The Presentation layer simply provides feedback to the user.
Objective Summary ■■
■■
■■
■■
Any time multiple users might attempt to update the same resource, whether it is a row in a table or a file on the file system, conflicts can occur. Pessimistic locking prevents the possibility of multiple users submitting changes simultaneously; however, it increases the chances of a deadlock. Optimistic locking decreases the chances of a deadlock but requires the developer to write code to handle simultaneous changes. Deadlock conflicts occur when two threads are waiting on each other to free up a resource. Optimistic locking and careful planning can reduce the occurrence of deadlocks. If you accept multiple connections to a web service, you have to plan for m ultithreaded behavior, including locking resources and resolving deadlocks. To create a single-threaded web service, set both InstanceContextMode and ConcurrencyMode to Single. Transactions cause a set of operations to succeed or fail as a group. Use the TransactionScope object to implement transactions to a single database or multiple databases.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are designing a supply chain management application. The application uses
atabases to track inventory at different warehouses. Each warehouse has its own d database. Often, operators need to transfer products from one warehouse to another. To avoid losing inventory, you need to ensure that when you remove an item from one
208
Chapter 3
Designing the Data Access Layer
warehouse, it is added to the inventory at a different warehouse. Which technology should you recommend? A. TransactionScope B. System.Runtime.Caching C. The Sync Framework D. The Entity Framework 2. You are designing a multitiered WCF web service that will be accessed by multiple
users. The developers in charge of creating the web service do not have experience working with multithreaded, multiuser applications. Therefore, you want to avoid concurrency problems completely, even if it means that users must wait for another user to complete an update. The web service’s InstanceContextMode is set to Single. The database might be accessed by other applications. Which choices should you recommend? (Choose two; each answer forms part of the complete solution.) A. Use pessimistic locking. B. Use optimistic locking. C. Set the concurrency mode to Single. D. Set the concurrency mode to Multiple.
Thought Experiment Designing a Web Service to Reserve Flight Seating
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You can
find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Blue Yonder Airlines. Blue Yonder is designing a new multitiered application that allows customers to reserve seats on future flights. The Presentation layer will be provided by a WPF application (for customer service agents) and an ASP.NET web application (for users on the Internet). The Business Logic layer is implemented using a WCF web service. The Data layer is implemented using a SQL Server database. The application design uses pessimistic locking when a user selects a seat. The lock is released when the user completes the purchase of the seat or after five minutes, whichever comes first. If the user takes more than five minutes to purchase a seat, he has to start the transaction over again. The WCF web service has an InstanceContextMode of PerSession and a ConcurrencyMode of Multiple.
Objective 3.5: Design for a Concurrent Multiuser Environment
Chapter 3
209
Answer the following questions about the future performance of the application:
1. How would the application behave differently if they used optimistic locking instead of pessimistic locking?
2. In what circumstances would optimistic locking be better than pessimistic locking? 3. When a user purchases a seat, the application must process his payment and reserve his seat at the same time. With the airline’s current application, there have been several instances where a user purchased a seat but his reservation was never processed. How would you prevent this?
4. The airline expects to have as many as 20 different users accessing the web service simultaneously. Are the recommendations for InstanceContextMode and ConcurrencyMode the best choices?
Objective 3.6: Analyze Data Services for Optimization Typically, data access consumes more time than any other part of your application. How you design your data access can have a dramatic impact on your application’s overall performance. For example, while the Entity Framework is the most convenient way to access a database, performing a query with the Entity Framework takes far longer than using ADO. NET. Similarly, while creating granular web services is a best practice, you can often improve performance by creating Data Transfer Objects (DTOs) that combine the results of multiple granular web services queries. This objective covers several topics related to the optimization of data services, including the performance impact of Object Relational Mapping (ORM), how to optimize round-trips to web services, and how to use lazy and eager loading. More Info Data Caching
Objective 3.6 includes caching of frequently used data. For more information about caching, refer to Objective 3.3, “Design Data Caching.”
This objective covers how to:
210
■■
Understand ORM performance.
■■
Understand lazy and eager loading.
■■
Optimize round-trips.
Chapter 3
Designing the Data Access Layer
Understanding ORM Performance ORM maps tables in a database to .NET Framework custom classes. This makes it much easier for developers to work with the underlying data. However, the process of mapping requests from the object layer to the database and back can dramatically slow performance. In general, the less abstraction, the better the performance. For example, it is much faster to use a DataReader to access a database than it is to use LINQ to SQL. Similarly, it is much faster to query a database using LINQ to SQL than to use the Entity Framework because the Entity Framework provides more abstraction than LINQ to SQL. However, those generalizations do not always hold true. For example, the Entity Framework tends to be between two and five times faster at updating previously queried records than LINQ to SQL. This performance improvement occurs because the Entity Framework tracks changed objects more efficiently. Often, however, the fastest technique is not the best. In most cases, the Entity Framework is the best choice because it simplifies writing and maintaining code, reducing development costs. While using the Entity Framework instead of more primitive technologies might waste a few milliseconds on every query, users might never notice the difference. And even if they did, a fraction of the budget you might save by reducing development time could be used to upgrade your database server hardware, which might more than make up for any performance impact. Assuming code was written efficiently to begin with, it’s typically less expensive to upgrade computer hardware than to pay a developer to optimize code. More Info Performance Tests
Many people have tested the performance of the different .NET Framework data access layers and written about their experience. Perhaps the easiest of these documents to interpret is “Entity Framework and LINQ to SQL Performance” at http://toomanylayers. blogspot.com/2009/01/entity-framework-and-linq-to-sql.html.
Understanding Lazy and Eager Loading When you query a single database table, the database returns all the rows and columns specified in your query. Once you implement an ORM layer such as the Entity Framework, however, determining what data to return can become more complex. For example, consider a simple database with two tables: Customers and Orders. The Customers table has an ID column named CustomerID, and the orders table has a CustomerID column that associates the order with the customer who placed it. Now, imagine that your Entity Framework application runs a LINQ query to retrieve a Customer object. You’d certainly expect to be able to access properties stored in the Customers table, such as Customer.FirstName and Customer.LastName. Indeed, the Entity Framework will always retrieve that information automatically as part of the SQL query that it generates based on your LINQ query.
Objective 3.6: Analyze Data Services for Optimization
Chapter 3
211
However, you also might want to access the Customer.Orders collection, which would contain one object for every order the customer has placed. Depending on whether the LINQ query used lazy or eager loading, the orders might already be available, or the Entity Framework might need to submit a second query to retrieve them. Lazy and eager loading use different algorithms to determine which data to retrieve: ■■
■■
Lazy loading (also known as deferred loading) Does not load data proactively from related tables. If you later attempt to access linked data, such as the Orders collection in the previous example, lazy loading retrieves it for you at the time that you access it. This improves performance if you never access the related table, but it decreases performance if you do. Eager loading Loads data proactively that you specify from related tables. If you later attempt to access linked data, it will be stored in memory already, and the data access layer will not need to generate a second database query. However, if you never access the related table, the application will have wasted time retrieving unnecessary information.
Lazy loading is the default for both LINQ to SQL and Entity Framework v4 (but not Entity Framework v1). Lazy loading simplifies coding because you never have to worry about what related tables you will access when you create a query. However, you can reduce the number of database queries (and thus improve performance) by specifying the related information that you need in your query. To do this, use the Include method. For example, consider the following pseudo-code: // Implicit/Transparent lazy loading customer = dbContext.Customers.First(); // Query Customers table orders = customer.Orders; // Query Orders table // Explicit lazy loading customer = dbContext.Customers.First(); // Query Customers table if (!customer.Orders.IsLoaded) customer.Orders.Load(); // Query Orders table orders = customer.Orders; // Explicit eager loading customer = dbContext.Customers.First().Include("Orders"); // Query Customers and Orders orders = customerAndOrders.Orders // No query necessary
The explicit eager loading example specifies that the query should include information from the related Orders table, and it will take less total time to execute than the lazy loading scenario. You can disable lazy loading entirely (meaning that related data will be unavailable) by setting ContextOptions.LazyLoadingEnabled to False. If you decide to optimize performance by using eager loading explicitly, you can disable lazy loading to verify that your code never requires it. More Info Loading Related Objects
For more information, visit http://msdn.microsoft.com/library/bb896272.aspx.
212
Chapter 3
Designing the Data Access Layer
In summary, lazy loading is convenient for developers, but it does not provide optimal performance.
Optimizing Round-Trips Round-trip time (RTT) is one of the biggest factors in Internet web services performance. In networking, the RTT is twice the network latency between the client and the server, which is equal to the time it takes for network communications to be sent across the network from the client to the server and then back to the client. On a local area network (LAN), the RTT is negligible—often only a few milliseconds. On the Internet, however, it’s not uncommon for RTTs to be more than one-quarter of a second. If the client and server are on different continents or if the client is using a satellite link, RTTs can even approach a full second. Setting up a TCP connection, which is required before clients can submit requests to most web services, requires a full round-trip as well. To improve web service responsiveness across the Internet, you need to minimize the number of round-trips. As covered in Objective 1.5, “Design for Optimal Processing,” you can do this by using chunky, rather than chatty, interactions. One of the ways to implement chunk communications is by creating a Data Transfer Object (DTO). A DTO combines information that might otherwise require multiple requests or responses into a single request or response. Table 3-1 compares how different web services might implement a DTO. Table 3-1 Simplifying Communications with a DTO
Without a DTO
With a DTO
To update a customer record, first search for the customer’s unique ID using the phone number. Then, submit the update using the ID.
Submit an update using the customer’s phone number.
To retrieve a customer’s addresses, first request the shipping address, and then request the billing address.
Request the customer’s shipping and billing addresses simultaneously.
To retrieve a list of products matching a search string, first retrieve a list of unique IDs, and then submit a separate request to retrieve the product description for each ID.
Submit a search string and receive an array of roduct information as a response. p
Methods can return only a single object. Therefore, creating a DTO that contains multiple objects or collections of objects as properties provides a workaround that in effect allows you to transfer multiple objects. In practice, you create a DTO by creating a custom class with properties for every object that needs to be transferred. More Info Creating DTOs
For more information, read “Data Transfer Object” at http://msdn.microsoft.com/library/ ff649585.aspx.
Objective 3.6: Analyze Data Services for Optimization
Chapter 3
213
DTOs improve performance only for high-latency, high-bandwidth connections. For low-latency, high-bandwidth connections, such as LANs, the performance difference might not be noticeable. For low-latency, low-bandwidth connections, such as dial-up links, the extra information communicated as part of a DTO might actually reduce performance.
Objective Summary ■■
■■
■■
The more your data access layer abstracts your application from the database, the slower queries will be. Therefore, while the Entity Framework provides a convenient development environment, you can get better performance with a DataReader object. The Entity Framework provides very fast updates, however. Both LINQ to SQL and the Entity Framework provide lazy loading by default. Lazy loading automatically queries the database when the application attempts to access a related table. You can improve performance by using eager loading, which specifies which related data to load when you initially query the database. RTT is a significant factor in web service performance. If web service clients must submit multiple successive requests to perform a single operation, you can improve performance across the Internet by using DTOs, which combine the results from multiple web service queries.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are designing a new WPF application for a university. The application uses the
ntity Framework to access a database. You need to retrieve a list of specific classes E and the professors for each class, but they are stored in separate, related tables. How can you retrieve the information as efficiently as possible? A. Query the Classes table. Using the queried classes, call the Include method to refer
to the Professors table. B. Query the Classes table. In a separate statement, use Load to refer to the
Professors table. C. Query the Classes table and use Include to refer to the Professors table in the same
query. D. Query the Classes table without referring to the Professors table. 2. You are designing a new WPF application for a university. The application uses the Entity
Framework to access a database. You need to retrieve a list of specific classes. If the user selects a class, you need to display the class schedule, which is stored in a separate, related table. Users view a class schedule only about 10 percent of the time. How can you retrieve the information while minimizing the load on the database? (Choose all that apply.) A. Query the Classes table. Using the queried classes, call the Include method to refer
to the Schedule table. 214
Chapter 3
Designing the Data Access Layer
B. Query the Classes table. When the user clicks a class, use Load to refer to the
Schedule table. C. Query the Classes table and use Include to refer to the Schedule table in the same
query. D. Query the Classes table without referring to the Schedule table. 3. You are designing a new WCF web service. You need to query a very large amount of
data from a single table in a database. You need to optimize the performance. Which technology should you use? A. Use a DataReader to query the table. B. Use the Entity Framework to query the table. C. Use LINQ to SQL to query the table. D. Use the Sync Framework to query the table.
Thought Experiment Optimizing Data Access Performance
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You can find answers
to these questions in the “Answers” section at the end of this chapter. You are a consultant for Consolidated Messenger. Recently, Consolidated Messenger began planning a new MVC web service that accesses a database. The web service will allow customers to send messages to each other, as well as providing access to public profile information about different users. The new web service will replace an existing web service that has serious performance problems. Therefore, Consolidated Messenger is very interested in making the web service and data access layer as efficient as possible. The developers have designed the web service with these attributes: ■■
It will use the Entity Framework to connect to the database.
■■
Developers will use the Entity Framework’s lazy loading feature.
■■
Web service design will be very granular, returning small, specific pieces of information, such as the ID of a queried user.
Answer the following questions about the future performance of the application:
1. Is the Entity Framework the right choice if we want to optimize performance? If not, what would you recommend instead?
2. Should we use lazy loading? Why or why not? 3. The developers are concerned that the granular web service design will result in unnecessary queries. How could they address this concern?
Objective 3.6: Analyze Data Services for Optimization
Chapter 3
215
Chapter Summary ■■
■■
■■
■■
■■
■■
216
ADO.NET provided earlier versions of the .NET Framework direct access to databases. Although ADO.NET still is supported, it typically should not be used for new applications. However, ADO.NET can still provide some performance benefits. WCF Data Services are the best way to expose a database using a web service. To avoid compatibility problems, you should use Visual Studio 2010 to integrate your database schema into your application’s change management strategy. If you might experience unexpected schema changes, separate the schema mapping from your compiled assembly. When the database schema changes, then you can update the mapping file after compiling your application. Use the MemoryCache class to store cached data, and create a dependency to invalidate the cached data automatically if the data store is updated. To improve web service performance, cache the web service output. IIS can return cached output for similar requests without running the web service method at all. If users need access to data when a connection to the database might not be available, you need to implement an offline data storage and synchronization technique. You can use the Sync Framework to store cached data from a database, file, or feed, and later update the database or file with offline changes. Pessimistic locking prevents the possibility of multiple users submitting changes simultaneously; however, it increases the chances of a deadlock. Optimistic locking decreases the chances of a deadlock, but it requires the developer to write code to handle simultaneous changes. If you accept multiple connections to a web service, you have to plan for multithreaded behavior, including locking resources and resolving deadlocks. To create a single-threaded web service, set both InstanceContextMode and ConcurrencyMode to Single. Use the TransactionScope object to implement transactions to a single or multiple databases. The Entity Framework provides a convenient development environment, but you can get better performance with DataReader. Both LINQ to SQL and the Entity Framework provide lazy loading by default. Lazy loading automatically queries the database when the application attempts to access a related table. You can improve performance by using eager loading, which specifies which related data to load when you initially query the database. If web service clients must submit multiple successive requests to perform a single operation, you can improve performance across the Internet by using DTOs, which combine the results from multiple web service queries.
Chapter 3
Designing the Data Access Layer
Answers This section contains the answers to the Object Reviews and the Thought Experiments.
Objective 3.1: Review 1. Correct Answer: B A. Incorrect: A WCF data service would be an ideal solution; however, it supports
only OData and REST. WCF Data Services do not support .NET-only protocols. B. Correct: WCF Web Services provide a great deal of flexibility, and they support
using multiple binding types. C. Incorrect: SQL Server Express edition does not support the different protocol
requirements. In addition, you should avoid exposing database servers to the public Internet because doing so presents a significant security risk. D. Incorrect: An Entity Data Model is useful for accessing a database directly from
an application. In fact, you might choose to generate an Entity Data Model in the application. However, you then will need to create a WCF web service to allow other clients to access the data across the network. 2. Correct Answer: D A. Incorrect: You could connect to the database using ADO.NET; however, it
would require a significant amount of extra programming and would not be object-oriented or strongly typed. B. Incorrect: WCF Data Services are useful when you need to create a data access
layer for applications connecting across the Internet. In this scenario, the database is on the local network, allowing you to take advantage of the more robust Entity Framework. C. Incorrect: Although you could access data using a WCF web service, it would be very
inefficient because you would need to code your own data access layer manually. D. Correct: The Entity Framework supports strongly typed, object-oriented classes
that represent tables in an underlying database. Visual Studio can generate classes automatically based on an existing database, allowing for rapid application development. 3. Correct Answer: B A. Incorrect: JavaScript clients cannot connect to a database using ADO.NET. B. Correct: WCF Data Services are useful when you need to create a data access layer
for applications connecting across the Internet. You can create a data access layer with WCF Data Services in only a few minutes.
Answers
Chapter 3
217
C. Incorrect: Although you could create a WCF web service that provided access to
an underlying database, it would require far more programming than using WCF Data Services. D. Incorrect: The Entity Framework supports strongly typed, object-oriented classes
that represent tables in an underlying database. However, JavaScript clients cannot access a database by using the Entity Framework.
Objective 3.1: Thought Experiment 1. Yes. 2. Yes. 3. Developers will need to use platforms that support SQL Server connections. Although
that’s straightforward with a .NET Framework application, it is difficult or impossible to do using JavaScript. Another challenge would be the network protocol itself: although the City Power & Light firewall has been configured to forward TCP port 1433, clients might be behind firewalls that allow only HTTP communications. 4. Yes. This architecture exposes the database directly to requests from the Internet.
Database administrators would need to be very confident that the database had been configured properly to protect the privacy and integrity of the database. Ideally, all databases would have that level of protection. Realistically, many developers rely on the applications that access the database to validate data and authorize requests. 5. WCF Data Services allow requests over HTTP, which allows requests across all common
firewalls. In addition, a wider variety of clients (including JavaScript clients) can connect easily to WCF Data Services.
Objective 3.2: Review 1. Correct Answers: B and D A. Incorrect: While the Entity Framework uses XML files, they do not have an .xml
extension. LINQ to SQL can use an external XML file, however. B. Correct: The .ssdl file is the Store Schema Definition Language file, and it records
the database schema. C. Incorrect: The .csdl file is the Conceptual Schema Definition Language, and it
efines your application’s conceptual model, also known as the object model. In d this scenario, you do not need to update the object model. D. Correct: The .msl file is the Mapping Schema Language file, and it maps the
database to your object model.
218
Chapter 3
Designing the Data Access Layer
2. Correct Answer: C A. Incorrect: While this approach would work, no part of the code could be reused
when you replace the XML file with a database connection. B. Incorrect: While this approach would work, no part of the code could be reused
when you replace the XML file with a database connection. In addition, regular expressions tend to make code more difficult to read. C. Correct: Using DataContractSerializer, you can map an XML file to your object
model, and then query the XML file with LINQ. This approach closely resembles the best practices for querying databases, simplifying your eventual migration to a database connection. In addition, LINQ is very readable. D. Incorrect: While this approach would work, no part of the code could be reused
when you replace the XML file with a database connection. In addition, regular expressions tend to make code more difficult to read.
Objective 3.2: Thought Experiment 1. The biggest potential problem is the lack of a schema change management strategy.
By deploying schema changes directly to the production server without testing the WCF web service with the new schema first, you could cause compatibility problems. 2. A better approach would be to deploy the schema changes to a database server in a
development environment, update the WCF web service to work with the new database, and then deploy both the new web service and the new database schema simultaneously. 3. Yes, it can abstract the data model to a separate mapping file and then update the
mapping file.
Objective 3.3: Review 1. Correct Answer: B A. Incorrect: You could derive a custom class from the CacheItemPolicy to accom-
plish this. However, it is far simpler to use the built-in HostFileChangeMonitor class. B. Correct: You can add an instance of the HostFileChangeMonitor to the
CacheItemPolicy.ChangeMonitors collection to cause an item added to the cache to expire when a file is updated. C. Incorrect: ObjectCache is the base class for MemoryCache. Using it instead of
MemoryCache would not provide any benefits. D. Incorrect: You do not need to create a custom caching class. Instead, you can
simply add an instance of HostFileChangeMonitor to the CacheItemPolicy .ChangeMonitors collection.
Answers
Chapter 3
219
2. Correct Answers: A and D A. Correct: Output caching is the most efficient way to cache a web service because
a cache hit will bypass processing the web service method completely. B. Incorrect: You could improve performance by using an instance of the
MemoryCache object. However, output caching would allow the .NET Framework to bypass running the web service method completely, improving performance even more. While there is no reason not to use both output caching and the MemoryCache object, nothing described in the scenario would benefit from using MemoryCache in addition to output caching. C. Incorrect: Although you are using a file-based database, specifying a file system
dependency would cause the cache to be invalidated unnecessarily if some other aspect of the database were updated. D. Correct: Specify a database dependency to invalidate the cache when a table is
updated.
Objective 3.3: Thought Experiment 1. No. While a WPF application can store objects in an instance of MemoryCache with a
database dependency, in this scenario, the WPF application does not have p rivileges to connect to the database. Therefore, it would not be able to create a database dependency. 2. A better approach would be to implement caching at the web service. Not only
does the web service have database privileges, allowing it to create the database dependency, but the web service would be able to cache queries from multiple different WPF clients. In addition, developers could use output caching. 3. They could create a custom change monitor that connected to the web service
to determine if a database table had been updated. However, because it requires communicating with the web service anyway, there would be no performance benefit, and the additional request might even reduce performance.
Objective 3.4: Review 1. Correct Answer: A A. Correct: The Sync Framework is perfect for this application because it simplifies
storing a local copy of a database and submitting the updates when the user is online. B. Incorrect: While you could use caching to store a copy of database records locally,
memory caching works only while the program is running (and thus it would not be effective if the realtor closed the application and reopened it), and caching does not support updates.
220
Chapter 3
Designing the Data Access Layer
C. Incorrect: While you could use caching to store a copy of database records locally,
memory caching works only while the program is running (and thus it would not be effective if the realtor closed the application and reopened it), and caching does not support updates. D. Incorrect: WCF Web Services do not provide support for offline updates. 2. Correct Answer: B A. Incorrect: SQL Server replication is powerful, but it is supported only by the full
versions of SQL Server, which typically are not used for storing data on individual devices because of the licensing costs. B. Correct: The Sync Framework is capable of the full-mesh replication topology
described by the scenario. C. Incorrect: WCF Data Services provides a useful way to interact with a database
across the Internet. However, it does not provide replication capabilities. D. Incorrect: ADO.NET provides access to databases. However, it does not have built-
in intelligent replication capabilities.
Objective 3.4: Thought Experiment 1. Yes. 2. Yes. 3. You could remove the Sync Framework completely from the design. Instead, simply
have the WPF clients connect to the SQL Server database in their location. If a particular customer is at a remote location, have the WPF client attempt to connect directly to the remote database. If the remote database cannot be reached, the teller will be unable to work with the customer’s records. However, the Sync Framework could not overcome this limitation anyway because the scenario prohibits the application from storing customer information from other banks.
Objective 3.5: Review 1. Correct Answer: A A. Correct: You can create distributed transactions across multiple databases by us-
ing the TransactionScope class. B. Incorrect: The System.Runtime.Caching class is useful for keeping local copies of
data from different databases. However, it cannot create a transaction. C. Incorrect: The Sync Framework can be used to copy updates between different
databases. However, TransactionScope is a better choice for ensuring updates to multiple databases commit together.
Answers
Chapter 3
221
D. Incorrect: The Entity Framework does provide support for distributed transactions.
However, you still must use the TransactionScope class to create the transaction. 2. Correct Answers: A and C A. Correct: Pessimistic locking locks resources anytime a user might make an
pdate. This can increase the likelihood of deadlock scenarios, but it eliminates the u possibility of multiple users updating a record simultaneously. B. Incorrect: If you were to use optimistic locking, your developers would need to
write code to resolve concurrent updates. Therefore, you should use pessimistic locking instead. C. Correct: Setting the concurrency mode to Single will cause a WCF web service
with a Single InstanceContextMode to run with a single thread. This avoids multithreaded issues within your web service. Because other applications are accessing the database, you still could have conflicts with record locking. D. Incorrect: Setting the concurrency mode to Multiple will cause the WCF web
service to be multithreaded, potentially running methods for multiple users simultaneously. This introduces the potential for concurrency issues. A better choice is to use the Single concurrency mode.
Objective 3.5: Thought Experiment 1. If they used optimistic locking, two users might begin to reserve the same seat. When
the second user attempts to pay for the seat, she would discover that the seat was not available and would need to start over. With pessimistic locking, the second user would not be able to begin the seat reservation process if another user had already begun. 2. If many users begin to reserve seats and never check out, optimistic locking would
be better. If most users who begin the seat reservation process complete payment, pessimistic locking would be better. 3. Process the financial transaction and the seat reservation as a single transaction. 4. Yes; if multiple users will be accessing the web service simultaneously, supporting
multithreading will provide the best performance.
Objective 3.6: Review 1. Correct Answer: C A. Incorrect: You would use Include only as part of the query. To retrieve related data
after running the query, use Load. B. Incorrect: While this approach would work, performance would be better if you
were to use eager loading to specify the Professors table as part of the initial query.
222
Chapter 3
Designing the Data Access Layer
C. Correct: By using Include to reference the Professors table, the Entity Framework
will eagerly load related professors. This optimizes performance. D. Incorrect: While lazy loading would allow this approach to work transparently,
performance would be better if you were to use eager loading to specify the Professors table as part of the initial query. 2. Correct Answers: B and D A. Incorrect: You would use Include only as part of the query. To retrieve related data
after running the query, use Load. B. Correct: This approach uses lazy loading to load the class schedule information.
It will generate a separate query when the user clicks a class; however, that will be more efficient than always retrieving the class schedule information. C. Incorrect: By using Include to refer to the Schedule table, the Entity Framework
will eagerly load related class schedules. However, your application will not use this unless the user clicks the schedule. Therefore, lazy loading would be more efficient. D. Correct: By using lazy loading, you can simply refer to the Schedule table when
you need it, and the Entity Framework will retrieve the required information automatically. It will generate a separate query when the user clicks a class; however, that will be more efficient than always retrieving the class schedule information. 3. Correct Answer: A A. Correct: Because of its simplicity, DataReader provides the best performance for
queries. B. Incorrect: The Entity Framework provides complex ORM that simplifies
development. However, it significantly decreases query performance. C. Incorrect: LINQ to SQL provides ORM that simplifies development. However, it
significantly decreases query performance. D. Incorrect: The Sync Framework simplifies storing a local cache of database
information. It is not designed as a data access layer, however.
Objective 3.6: Thought Experiment 1. No. The Entity Framework simplifies development, and that can reduce the company’s
costs significantly. However, using lower-level data access technologies such as ADO.NET or even LINQ to SQL can improve performance. 2. No. To improve performance, you should disable lazy loading and have developers
eagerly load any related data that they require. 3. You could create ORMs that encapsulate the data that otherwise would require
multiple queries to transfer.
Answers
Chapter 3
223
C h apter 4
Planning a Solution Deployment A
t a high level, an application’s life cycle follows the process shown in Figure 4-1.
Design
Management
Development
Deployment
Figure 4-1 The application life cycle
While developers spend most of their time in the development stage, and systems administrators spend most of their time in the management phase, the deployment stage is unique because it is often shared between both developers and systems administrators. When planning a solution deployment strategy, you need to design an approach that allows each group to perform their assigned roles while minimizing the effort required of both. You also need to plan for updates of the application that will occur during future iterations of the application’s life cycle.
225
Objectives in this chapter: ■■
Objective 4.1: Define a client deployment strategy
■■
Objective 4.2: Plan a database deployment
■■
Objective 4.3: Design a solution update strategy
■■
Objective 4.4: Plan for n-tier deployment
Objective 4.1: Define a Client Deployment Strategy Applications must be installed before users can run them from their own computers. Creating a setup project is simple for the developer, but the actual deployment and maintenance of an application can be very time-consuming for systems administrators. Choosing the right deployment strategy can greatly reduce the overall cost of a new application.
This objective covers how to: ■■
Distinguish between ClickOnce, Windows Installer, and XCopy deployments.
■■
Choose an installation method.
■■
Deploy Component Object Model (COM) objects.
Understanding Installation Methods You can install client applications using ClickOnce, Windows Installer, and XCopy. The sections that follow provide more design-level information about ClickOnce, Windows Installer, and XCopy deployments. ClickOnce and Windows Installer were covered by the 70-511 MCPD certification exam, so they will not be covered in detail here.
ClickOnce With ClickOnce, it is as easy for users to install and run a Windows Forms or Windows Presentation Foundation (WPF) application as it is to click a link in their browser. Basically, you copy application files to a web server, file server, or removable media, and users click a link to install the application on their own computers or run the application directly from the network location. ClickOnce provides the power of a WPF application with the simple deployment of a web application. ClickOnce supports two deployment methods: ■■
226
Install from a website, network share, or removable media When the user clicks a link on a webpage or double-clicks a file on a shared folder, the application installs itself on the user’s computer. The installation can add a shortcut to the Start menu and can be uninstalled using Add Or Remove Programs on the Control Panel.
Chapter 4
Planning a Solution Deployment
■■
Run from a website or network share When the user clicks a link on a webpage or double-clicks a file on a shared folder, the application seems to start from that location. Technically, the files are being copied to the local application cache and run. The application files stay on the server, allowing you to maintain the application by updating a single, central set of files.
ClickOnce provides several benefits over a traditional Windows Installer deployment: ■■
■■ ■■
■■
Minimal changes are required to clients, and users do not require administrative privileges. You can deploy an application by sending an email or link to users. Users install an application only when they need it. With Active Directory software distribution, administrators tend to install applications on users’ computers whether or not they ever need it. Applications can update themselves automatically. If users run the application directly from a website or network share, they always run the newest version of the application. For more information, refer to Objective 4.3, “Design a Solution Update Strategy.”
More Info Active Directory Software Distribution
For more information, read “How to use Group Policy to remotely install software in Windows Server 2003 and in Windows Server 2008” at http://support.microsoft.com/kb/816102.
Other than requiring the Microsoft .NET Framework, ClickOnce has one very important prerequisite: the user must open the link with Windows Internet Explorer or another browser with the ClickOnce add-on configured. Because many organizations prefer to use a browser other than Internet Explorer, or individual users change their default browser, this requirement is the single biggest challenge with ClickOnce deployment. More Info Browser Add-ons
For more information about the Firefox add-on, read “Firefox Add-ons to Support .NET Application Deployment” at http://msdn.microsoft.com/library/cc716877.aspx. For a discussion on how to use ClickOnce with Firefox on Windows 7, visit http://social.msdn. microsoft.com/Forums/en/wpf/thread/747c6b78-a821-4498-a37f-1b79197de803. For Google Chrome, install the “ClickOnce for Google Chrome” extension from https://chrome. google.com/webstore/detail/eeifaoomkminpbeebjdmdojbhmagnncl.
To provide offline access to an application and reduce bandwidth usage, the .NET F ramework can cache a local copy of the application and run the local copy when the server is unavailable or the local copy of the application is current.
Objective 4.1: Define a Client Deployment Strategy
Chapter 4
227
To improve client security, ClickOnce applications run with partial trust by default. If your application needs additional privileges, you can perform a Trusted Application Deployment. To perform a Trusted Application Deployment, sign the assembly (as required for all ClickOnce deployments) and then configure clients to trust your certificate as a trusted publisher. This requires you to install two certificates on clients: ■■
■■
In each client’s Trusted Publisher certificate store, install the certificate used to sign the assembly. In each client’s Trusted Root Certification Authority certificate store, install the Certification Authority (CA) certificate. If you purchase a certificate from a CA that Windows trusts by default, or if your certificate is issued by a trusted enterprise CA, you can skip this step.
After you have configured your certificate as a trusted publisher, you can deploy any number of ClickOnce applications signed with the same certificate. More Info Partial Trust and Trusted Application Deployment
For more information about partial trust, refer to Objective 1.3, “Design a Loosely Coupled Layered Architecture,” in Chapter 1, “Designing the Layers of a Solution.” For more information about Trusted Application Deployment, visit http://msdn.microsoft.com/ library/01daf08f.aspx.
In an Active Directory domain environment, you can install certificates using Group Policy Objects (GPOs). Alternatively, you can script the installation using the CertMgr.exe tool (included with all recent versions of Windows) or by using the System.Security.Cryptography namespace. More Info Deploying Certificates
For detailed instructions about deploying certificates with GPOs, visit http://technet. microsoft.com/library/cc770315.aspx. For detailed instructions about using the Certmgr.exe tool, visit http://msdn.microsoft.com/library/e78byta0.aspx. For detailed instructions about programmatically importing certificates, visit http://msdn.microsoft.com/library/te7383x5. aspx.
Windows Installer Windows Installer is a feature of Microsoft Windows that can automatically install W indows Installer packages (.msi files). You can add a Microsoft Visual Studio Setup Project to your solution to add your application files and any configuration settings automatically to a Windows Installer package.
228
Chapter 4
Planning a Solution Deployment
When you build a Setup Project, it automatically creates an .msi file and a Setup.exe. sers can run either to manually install the application, and administrators can distribute U the .msi file in a variety of ways, such as by using Active Directory software distribution in an enterprise environment. With Active Directory software distribution, a systems administrator stores a copy of a Windows Installer package on a file server (known as the distribution point). The systems administrator then links to that Windows Installer package with a GPO and applies that GPO to a set of users (using the User Configuration\Software Settings\Software Installation node) or computers (using the Computer Configuration\Software Settings\Software Installation node). When Windows starts, it installs any Windows Installer packages configured in the computer’s GPO. When the user logs on, Windows downloads user settings, and installs any Windows Installer packages configured in the user’s GPO. Because Windows trusts the Active Directory domain and the GPO settings, Windows uses administrative privileges to install the application, even if the user does not have those privileges. With Active Directory software distribution provides three options for deploying applications: ■■
■■
■■
Publish the application to users Does not install the application. The next time the user logs on, this setting makes the application available for the user to install from Control Panel. Administrators would need to send a notice to users to inform them that they can install the application if desired. This allows users to determine whether they need the application rather than installing it on every computer, which can reduce licensing costs. Assign the application to users Adds shortcuts to the application a utomatically the next time the user logs on. The first time the user attempts to access the application, Windows installs the package. This reduces the number of users installing an application, but curious users still might install an application they do not need. This technique also serves to limit the number of simultaneous requests sent to the file server hosting the Windows Installer package. Assign the application to computers Installs the application automatically the next time Windows starts. The application will be available to all users on the computer. After installation, any shortcuts that the Setup project adds to the Start menu or Desktop will be visible to users.
After installation, administrators can uninstall the application manually using the Control Panel or uninstall the application automatically using Active Directory software distribution by removing it from the GPO and choosing Immediately Uninstall The Software From Users And Computers. Windows Installer has one major drawback when compared to ClickOnce: there are no automatic updates. You can package and deploy updates just as you would deploy any Windows Installer file, but Windows Installer does not detect and install new updates automatically. Instead, you would have to write your own code to detect and install new updates.
Objective 4.1: Define a Client Deployment Strategy
Chapter 4
229
Real World
M
ost organizations using Active Directory software distribution to deploy Windows Installer packages. This sounds great, but in the real world, it
doesn’t always work smoothly. First, if the package is large, you might saturate network connections in the morning when large numbers of users start the computer or log on. Second, administrators don’t have precise control over exactly when an application is installed. Especially if a user does not connect the computer to the Active Directory domain (for example, a user who is travelling), they might not update their GPO settings for weeks or months at a time, meaning that the application never gets installed. So, in the real world, Windows Installer packages work great for tightly managed computers. For computers that aren’t managed tightly, ClickOnce might be a better choice.
XCopy The term XCopy deployment refers to simply copying all a web application’s files to a web server. XCopy is a tool built into Windows that copies files from either a command prompt or a script. While XCopy is one of the best ways to reliably copy files, you could use any file copy tool to deploy an application, including Windows Explorer, File Transfer Protocol (FTP), and RoboCopy. Because you are simply copying files, an XCopy deployment does not configure the computer in any way. Therefore, when using XCopy deployment, you must do the following actions manually: ■■ ■■
Create any shortcuts. Configure any dependencies (such as installing the latest version of the .NET Framework or adding shared assemblies).
■■
Alter the .config file, if required for that specific computer.
■■
Change connection strings to connect to production databases.
■■
Make any other changes to prepare the application for a production environment.
XCopy is most useful when deploying a web service to a large number of servers. Once you have prepared the application to be run in a production environment, you could write a script using XCopy that copies the application to all the servers at once. If you intend to use XCopy to deploy an application to clients, create a domain logon script. Otherwise, it will be difficult to find a time when a computer is turned on and connected to your internal network.
230
Chapter 4
Planning a Solution Deployment
As an alternative to XCopy, consider the RoboCopy tool, included with Windows Vista, Windows 7, and Windows Server 2008. While functionally similar, you can use the /Purge parameter to remove files from the destination that no longer exist at the source. Without that feature, old files that you are no longer maintaining can remain indefinitely on your production computers, perhaps introducing security vulnerabilities. For web services, you also can perform an XCopy-style deployment using the Publish Web tool and selecting File System from the Publish Method list. XCopy web service deployments are not precompiled. When you copy files to a web server, the web server automatically compiles them the first time a user requests a page—a process that usually takes less than a second. While the compile time is not typically a problem, relying on the web server to compile the web service means that it is possible for compilation to fail because of a missing file or mistyped word.
Choosing an Installation Method Choose ClickOnce deployment when: ■■
■■ ■■
The application can run with partial trust or you can install a trusted publisher certificate. You want to distribute an application using a webpage, an email, or removable media. You want to update the application regularly but do not have privileges to deploy updates to clients.
Choose Windows Installer deployment when: ■■
Deploying applications to unknown users on the Internet.
■■
Users have privileges to install Windows Installer packages.
■■
■■
Client computers participate in a software deployment infrastructure, such as Active Directory. Deploying the application requires configuring the client (for example, by performing any of these changes): ■■
Installing other software components (especially COM components, drivers, or shared files)
■■
Adding an assembly to the Global Assembly Cache (GAC)
■■
Installing the application for multiple users
■■
Configuring the application to start automatically
Choose XCopy deployment when: ■■ ■■
■■
The application is self-contained and does not need to update the client. The application does not need to configure the client, such as installing required components or adding a shortcut to the Start menu. The application has been configured already and only needs to be updated.
Objective 4.1: Define a Client Deployment Strategy
Chapter 4
231
Regardless of the deployment method, clients must have the correct version of the .NET Framework installed. Typically, you will deploy this to computers in an enterprise using their update deployment infrastructure, such as Windows Server Update Services (WSUS). However, you also can install it the first time a user attempts to run an application. Both ClickOnce and Windows Installer deployments can determine automatically whether the required version of the .NET Framework is installed, and if it is not, they can install it before proceeding with the application installation.
Deploying the .NET Framework Before a client can run your new application, it must have a compatible version of the .NET Framework installed. .NET Framework 4 can be installed only on these operating systems: ■■
The Windows 7 family
■■
The Windows Server 2008 R2 family
■■
Windows Server 2008 Server Core R2 with Service Pack 1 or later
■■
The Windows Vista family
■■
The Windows Server 2008 family
■■
Microsoft Windows XP Home or Windows XP Professional, both with SP3 or later
■■
The Windows Server 2003 family with SP2 or later
In enterprise environments, systems administrators typically will deploy the latest version of the .NET Framework to clients using Windows Update or Active Directory s oftware distribution (by deploying the .NET Framework redistributable stand-alone installer). Therefore, by deploying the correct version of the .NET Framework prior to any applications that require it, you can eliminate any problems with prerequisites. In environments where you need to deploy your application to a computer that is not part of an Active Directory domain, your installer will need to verify that the .NET Framework is present, and if it is not, provide some way to install it. This process is known as bootstrapping. You can choose from three different approaches: ■■
■■
■■
232
Prompt the user to install the .NET Framework themselves. You can provide a link to a webpage or include the .NET Framework redistributable with your installation media. Install the .NET Framework 4 Client Profile automatically. The Client Profile includes only a subset of the entire .NET Framework 4; however, it is typically sufficient to run WPF and Windows Forms applications. The .NET Framework 4 Client Profile is 41 MB in size. Install the full .NET Framework 4 automatically. The .NET Framework 4 full installer is 48 MB in size and includes components to run server applications, web applications, and some less frequently used technologies, such as System.Data.OracleClient and the Windows Workflow Foundation (WF).
Chapter 4
Planning a Solution Deployment
The .NET Framework is available as either a web installer or a stand-alone installer: ■■
■■
Web installer Starts a small setup file (less than 1 MB) that connects to Microsoft and downloads the .NET Framework files from across the Internet. Obviously, this requires the user to have an Internet connection during setup. Stand-alone installer Contains all the files required to install the .NET Framework, which is 41 MB or 48 MB in size, depending on whether you are installing the .NET Framework 4 Client Profile or the full redistributable. Users do not need an Internet connection; however, you must provide them a copy of the stand-alone installer.
By default, Visual Studio 2010 Setup projects are configured with the .NET Framework 4 Client Profile as a prerequisite. If the .NET Framework is not installed at setup time, the setup process will download it from Microsoft’s website. Figure 4-2 shows the Prerequisites dialog box, which you can access by viewing the Setup project’s properties and clicking Prerequisites.
Figure 4-2 Configuring a Setup project to require the .NET Framework
As you can see from Figure 4-2, you can use the same dialog box to install the .NET Framework directly from your installation location (by selecting Download Prerequisites From The Same Location As My Application) or you can reduce Internet usage by installing it from a shared folder on your internal network (by selecting Download Prerequisites From The Following Location). You also can install other common prerequisites from the web or a location that you specify, including Microsoft SQL Server 2008 Express or SQL Server Compact edition. You can download the different versions of the .NET Framework 4 redistributables here: ■■
.NET Framework 4 Client Profile web installer: http://www.microsoft.com/download/ en/details.aspx?id=17113
Objective 4.1: Define a Client Deployment Strategy
Chapter 4
233
■■
■■
■■
.NET Framework 4 Client Profile stand-alone installer: http://www.microsoft.com/ download/en/details.aspx?id=24872 .NET Framework 4 full web installer: http://www.microsoft.com/download/en/details. aspx?id=17851 .NET Framework 4 full stand-alone installer: http://www.microsoft.com/download/en/ details.aspx?id=17718
Deploying COM Objects If your application accesses a COM object, you will need to register the COM object when you install your application. For this reason, deploying a COM object cannot be done easily with ClickOnce or XCopy deployments. From a command prompt, you can register a COM object using RegSvr32.exe. RegSvr32 is included with all recent versions of Windows, so you can assume that the command-line tool will be available. From a Setup project, you can call RegSvr32 and specify the path to the COM object that you need to register. The simplest way to register a COM object is to use a Setup project. Add the file (typically a .exe or .dll file) to the setup project and view its properties. Edit the value of the Register property, setting it to one of these values: ■■
■■
■■
vsdraCOM Windows Installer will register the COM object. The deployment project will update the Class table, ProgID table, and other tables in the Registry Tables group of the corresponding .msi file. This is the recommended way to register a COM module. vsdraCOMRelativePath Windows Installer will register the COM object as an isolated COM object, which prevents it from being accessed by different applications. vsdraCOMSelfReg Windows Installer calls the DllRegisterServer function of that module at the time that you install the module, and the DllUnregisterServer function at the time that you uninstall the module. This option is available only with COM objects that support self-registration, and it is not recommended. Instead, choose vsdraCOM or vsdraCOMRelativePath.
If you are deploying a .NET assembly that will be accessed as a COM object, deploy it with a Setup Project or the Assembly Registration Tool (RegAsm.exe), as described in Objective 1.4, “Design for Interoperability with external systems,” in Chapter 1. More Info Working with COM Components
For more information, refer to Objective 1.4.
234
Chapter 4
Planning a Solution Deployment
Objective Summary ■■
■■
■■
■■
ClickOnce deployments store an assembly on a server or removable media, and clients can run the assembly directly from that location. ClickOnce applications run with partial trust by default, and if your application requires higher privileges, you will need to install a publisher certificate on every client. In addition, if clients use a browser other than Internet Explorer, you will need to configure an add-on (if one is available). Windows Installer deployments distribute an .msi package to clients and can make complex configuration changes to the client. You must perform a Windows Installer deployment if your application requires complex configuration changes, such as installing a COM object. XCopy deployments simply copy files to a computer, without performing any configuration. If your application uses a COM object, you must register it during the setup process. You can deploy COM objects using the RegSvr32.exe tool or by using a Setup project and editing the file’s properties.
Objective Review Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter. 1. You are planning the deployment of a new WPF application. You want to publish the
application to a website and have users run the application directly from the linked location without needing to distribute updates directly to clients. Some users will have administrative privileges on their own computers, while others have limited privileges. You have no direct access to users’ computers. Which technology should you recommend? A. ClickOnce B. XCopy C. Windows Installer D. RegSrv32.exe 2. You are planning the deployment of a new WPF application. The application requires a
new shortcut in the Start menu and the use of a COM object that will not be installed on some clients. Which technology should you recommend to install the application? A. ClickOnce B. XCopy C. Windows Installer D. RegSrv32.exe
Objective 4.1: Define a Client Deployment Strategy
Chapter 4
235
3. You are planning to use ClickOnce to deploy a new WPF application using a link on a
webpage. You need to design the deployment so that a User Account Control (UAC) prompt does not appear during installation. However, the application might request elevated privileges when the user runs it. How should you install the trusted publisher certificate? A. Install the signing certificate in the Trusted Publisher’s certificate store on the client. B. Install the signing certificate in the Trusted Publisher’s certificate store on the web server. C. Install the CA’s certificate in the client’s Trusted Root Certificate Authority
certificate store. D. Install the CA’s certificate in the Trusted Root Certificate Authority certificate store
on the web server.
Thought Experiment Deploying a New Three-Tier Application
I
n the following thought experiment, apply what you’ve learned about this objective to predict how a theoretical application architecture will perform. You can
find answers to these questions in the “Answers” section at the end of this chapter. You are a consultant for Alpine Ski House, a resort that can host about 2,000 guests. Alpine Ski House is planning the following three-tier application that will allow guests to get information about the resort, to check in, and to contact staff: ■■
The Presentation layer will be implemented using a WPF application and an ASP.NET web application. The WPF application will be run from the 200 clients in the organization’s Active Directory domain. The web application will be run directly by visitors from their own computers and mobile devices.
■■
The Business Logic layer will be implemented using a Windows Communication Foundation (WCF) web service.
■■
The Data layer will be implemented using a SQL Server database.
The developers have designed a deployment strategy with these attributes: ■■
The WPF application will be deployed using ClickOnce. A link to the application will be placed on the intranet’s website.
■■
The ASP.NET web application and the WCF web service will be deployed using XCopy.
Answer the following questions about the future performance of the application:
1. Will ClickOnce deployment work for the WPF application? If you’re not sure, what else would you need to know to answer this question?
2. The WCF web service will run as a service and be self-hosted. Will XCopy deployment work for it? If not, why?
236
Chapter 4
Planning a Solution Deployment
Objective 4.2: Plan a Database Deployment When you design a data-driven application, you must plan for the deployment of the database. Even if the application connects to an existing database, you are likely to modify the schema of the database during the course of development, and those incremental changes will need to be deployed from your development environment and into production. This objective requires you to understand the different techniques for deploying a database and the factors you will need to consider when planning that deployment.
This objective covers how to: ■■
Run SQL scripts.
■■
Use the Vsdbcmd.exe tool.
■■
Use data-tier projects and .dacpac files.
■■
Use SQL Server database projects.
■■
Deploy databases directly from Server Explorer.
■■
Deploy databases with a WCF web service.
■■
Understand deployment conflicts.
Understanding Database Deployment Files Though developers rarely deploy a database manually, it is important to understand the database deployment technologies because they are the underlying technologies used during Visual Studio deployments. The three most common database deployment file types are: ■■
■■
■■
.sql scripts A script (resembling a batch file) that contains SQL commands to create a structure and (if you want) add data to it. .dbschema A file that describes a database schema. Typically, these deployments also include a .dbmanifest file and SQL scripts. .dacpac An increasingly popular format for deploying or updating a database. In addition to schema and SQL scripts, .dacpac files describe requirements for the database, such as a minimum version number.
The sections that follow describe these technologies, as well as how to use Visual Studio to create them.
Using SQL Scripts The most common ways to configure and populate a database is by using SQL scripts. In fact, even if you use one of the other deployment methods, Visual Studio will generate SQL scripts to handle part of the deployment process.
Objective 4.2: Plan a Database Deployment
Chapter 4
237
SQL scripts use the same language used to query and update databases to add tables, views, and stored procedures. For example, the following SQL command creates a table in the current database: CREATE TABLE people ( people_id smallint IDENTITY(1,1) PRIMARY KEY CLUSTERED, name varchar(50) NOT NULL DEFAULT ‘John Doe’, age tinyint NOT NULL CHECK (age