Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) ®
™
Delivery Guide Course Number: 2349B
Part Number: X08-76381 Released: 02/2002
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, places or events is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001−2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Course Number: 2349B Part Number: X08-76381 Released: 02/2002
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
iii
Contents Introduction Introduction..............................................................................................................1 Course Materials ......................................................................................................2 Prerequisites.............................................................................................................3 Course Outline .........................................................................................................4 Microsoft Certified Professional Program ...............................................................9 Facilities.................................................................................................................11
Module 1: Overview of the Microsoft .NET Framework Overview..................................................................................................................1 Overview of the Microsoft .NET Framework..........................................................2 Overview of Namespaces ......................................................................................13 Review ...................................................................................................................17
Module 2: Introduction to a Managed Execution Environment Overview..................................................................................................................1 Writing a .NET Application.....................................................................................2 Compiling and Running a .NET Application.........................................................11 Lab 2: Building a Simple .NET Application..........................................................29 Review ...................................................................................................................32
Module 3: Working with Components Overview..................................................................................................................1 An Introduction to Key .NET Framework Development Technologies ..................2 Creating a Simple .NET Framework Component ....................................................4 Lab 3.1: Creating a .NET Framework Component ................................................11 Creating a Simple Console Client..........................................................................14 Lab 3.2: Creating a Simple Console-Based Client.................................................19 Demonstration: Creating a Windows Forms Client ...............................................22 Creating an ASP.NET Client .................................................................................27 Lab 3.3: Calling a Component Through an ASP.NET Page..................................36 Review ...................................................................................................................40
Module 4: Deployment and Versioning Overview..................................................................................................................1 Introduction to Application Deployment .................................................................2 Application Deployment Scenarios .........................................................................7 Related Topics and Tools.......................................................................................31 Lab 4: Packaging and Deployment ........................................................................37 Review ...................................................................................................................42
Module 5: Common Type System Overview..................................................................................................................1 An Introduction to the Common Type System ........................................................2 Elements of the Common Type System...................................................................8 Object-Oriented Characteristics.............................................................................25 Lab 5: Building Simple Types ...............................................................................39 Review ...................................................................................................................44
iv
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
Module 6: Working with Types Overview................................................................................................................. 1 System.Object Class Functionality ......................................................................... 2 Specialized Constructors....................................................................................... 12 Type Operations.................................................................................................... 18 Interfaces............................................................................................................... 28 Managing External Types ..................................................................................... 34 Lab 6: Working with Types .................................................................................. 38 Review .................................................................................................................. 43
Module 7: Strings, Arrays, and Collections Overview................................................................................................................. 1 Strings ..................................................................................................................... 2 Terminology – Collections.................................................................................... 20 .NET Framework Arrays....................................................................................... 21 .NET Framework Collections ............................................................................... 39 Lab 7: Working with Strings, Enumerators, and Collections................................ 57 Review .................................................................................................................. 63
Module 8: Delegates and Events Overview................................................................................................................. 1 Delegates................................................................................................................. 2 Multicast Delegates............................................................................................... 12 Events.................................................................................................................... 21 When to Use Delegates, Events, and Interfaces.................................................... 31 Lab 8: Creating a Simple Chat Server................................................................... 32 Review .................................................................................................................. 42
Module 9: Memory and Resource Management Overview................................................................................................................. 1 Memory Management Basics.................................................................................. 2 Non-Memory Resource Management ................................................................... 12 Implicit Resource Management ............................................................................ 13 Explicit Resource Management ............................................................................ 26 Optimizing Garbage Collection ............................................................................ 36 Lab 9: Memory and Resource Management ......................................................... 49 Review .................................................................................................................. 56
Module 10: Data Streams and Files Overview................................................................................................................. 1 Streams.................................................................................................................... 2 Readers and Writers ................................................................................................ 5 Basic File I/O .......................................................................................................... 8 Lab 10: Files ......................................................................................................... 21 Review .................................................................................................................. 26
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
v
Module 11: Internet Access Overview..................................................................................................................1 Internet Application Scenarios.................................................................................2 The WebRequest and WebResponse Model............................................................3 Application Protocols.............................................................................................16 Handling Errors......................................................................................................25 Security ..................................................................................................................28 Best Practices.........................................................................................................35 Lab 11: Creating a DateTime Client/Server Application.......................................36 Review ...................................................................................................................41 Course Evaluation..................................................................................................43
Module 12: Serialization Overview..................................................................................................................1 Serialization Scenarios.............................................................................................2 Serialization Attributes ............................................................................................4 Object Graph............................................................................................................5 Serialization Process ................................................................................................7 Serialization Example ..............................................................................................9 Deserialization Example ........................................................................................10 Custom Serialization..............................................................................................12 Custom Serialization Example...............................................................................14 Security Issues .......................................................................................................17 Lab 12: Serialization ..............................................................................................18 Review ...................................................................................................................27
Module 13: Remoting and XML Web Services Overview..................................................................................................................1 Remoting..................................................................................................................2 Remoting Configuration Files................................................................................19 Demonstration: Remoting ......................................................................................22 Lab 13.1: Building an Order-Processing Application by Using Remoted Servers 28 XML Web Services................................................................................................36 Lab 13.2: Using an XML Web Service..................................................................48 Review ...................................................................................................................54 Course Evaluation..................................................................................................56
vi
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
Optional Modules Module 14 (Optional): Threading and Asynchronous Programming Overview................................................................................................................. 1 Introduction to Threading ....................................................................................... 2 Using Threads in .NET ........................................................................................... 9 Thread Safety ........................................................................................................ 29 Special Thread Topics........................................................................................... 51 Asynchronous Programming in .NET................................................................... 74 Lab 14: Working With Multithreaded Applications ............................................. 92 Review ................................................................................................................ 108
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code Overview................................................................................................................. 1 Integration Services................................................................................................. 2 Platform Invoke ...................................................................................................... 7 Lab 15.1: Calling Win32 APIs.............................................................................. 16 Calling COM Objects from Managed Code.......................................................... 20 Lab 15.2: Calling COM Objects ........................................................................... 39 Calling .NET Objects from COM Objects............................................................ 43 Review .................................................................................................................. 55
Module 16 (Optional): Using Microsoft ADO.NET to Access Data Overview................................................................................................................. 1 Overview of ADO.NET .......................................................................................... 2 Connecting to a Data Source................................................................................. 10 Accessing Data with DataSets .............................................................................. 12 Using Stored Procedures....................................................................................... 26 Lab 16: Using ADO.NET to Access Data ............................................................ 34 Accessing Data with DataReaders ........................................................................ 42 Binding to XML Data ........................................................................................... 50 Review .................................................................................................................. 56
Module 17 (Optional): Attributes Overview................................................................................................................. 1 Overview of Attributes............................................................................................ 2 Defining Custom Attributes .................................................................................. 13 Retrieving Attribute Values .................................................................................. 22 Demonstration: Custom Attributes ....................................................................... 26 Lab 17: Defining and Using Attributes................................................................. 27 Review .................................................................................................................. 36
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
vii
About This Course This section provides you with a brief description of the course, audience, suggested prerequisites, course objectives, and information about course customization.
Description The goal of this five day course is to help application developers understand the Microsoft® .NET Framework. In addition to offering an overview of the .NET Framework and an introduction to key concepts and terminology, the course provides a series of labs, which introduce and explain .NET Framework features that are used to code, debug, tune, and deploy applications.
Audience This course is intended for experienced, professional software developers who work in independent software vendors (ISVs) or work on corporate enterprise development teams. Most students will be Microsoft Visual C++® (or C++) and Java developers.
Student Prerequisites This course requires that students meet the following prerequisites: • Students should be experienced professional developers and have a basic understanding of the C# language. Students can meet the C# language prerequisite by taking Course 2124, Introduction to C# Programming for the Microsoft .NET Platform.
Course Objectives After completing this course, the student will be able to: !
List the major elements of the .NET Framework and explain how they fit into the .NET platform.
!
Explain the main concepts behind the common language runtime and use the features of the .NET Framework to create a simple application.
!
Create and use components in Microsoft Windows® Forms-based and ASP.NET-based applications.
!
Use the deployment and versioning features of the .NET runtime to deploy multiple versions of a component.
!
Create, use, and extend types by understanding the Common Type System architecture.
!
Create classes and interfaces that are functionally efficient and appropriate for specific programming scenarios.
!
Use the .NET Framework class library to efficiently create and manage strings, arrays, collections, and enumerators.
viii
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET) !
Use delegates and events to have an event sender object signal the occurrence of an action to an event receiver object.
!
Describe and control how memory and other resources are managed in the .NET Framework.
!
Read from and write to data streams and files.
!
Use the basic request/response model to send and receive data over the Internet.
!
Serialize and deserialize an object graph.
!
Create distributed applications through XML Web services and Object Remoting.
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
Course Timing This section provides estimated course timings for all of the modules, labs, and breaks in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET). Your timing may vary.
Day 1 Start
End
Module
9:00
9:30
Introduction
9:30
10:00
Module 1: Overview of the Microsoft .NET Framework
10:00
10:15
Break
10:15
11:00
Module 2: Introduction to a Managed Execution Environment
11:00
11:15
Lab 2: Building a Simple .NET Application
11:15
11:45
Module 3: Working with Components
11:45
12:00
Lab 3.1: Creating a .NET Framework Component
12:00
1:00
Lunch
1:00
1:15
Module 3: Working with Components (continued)
1:15
1:30
Lab 3.2: Creating a Simple Console-Based Client
1:30
2:00
Module 3: Working with Components (continued)
2:00
2:30
Lab 3.3: Calling a Component Through an ASP .NET Page
2:30
2:45
Break
2:45
4:15
Module 4: Deployment and Versioning
Start
End
Module
9:00
9:50
Lab 4: Packaging and Deployment
9:50
10:00
Break
10:00
11:30
Module 5: Common Type System
11:30
12:30
Lunch
12:30
1:15
Lab 5: Building Simple Types
1:15
2:30
Module 6: Working with Types
2:30
2:45
Break
2:45
3:30
Lab 6: Working with Types
3:30
4:00
Module 7: Strings, Arrays, and Collections
Day 2
ix
x
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
Day 3 Start
End
Module
9:00
10:30
Module 7: Strings, Arrays, and Collections (continued)
10:30
10:45
Break
10:45
11:45
Lab 7: Working with Strings, Enumerators, and Collections
11:45
12:45
Lunch
12:45
2:00
Module 8: Delegates and Events
2:00
2:15
Break
2:15
3:30
Lab 8: Creating a Simple Chat Server
3:30
4:00
Module 9: Memory and Resource Management
Start
End
Module
9:00
10:30
Module 9: Memory and Resource Management (continued)
10:30
10:45
Break
10:45
11:45
Lab 9: Memory and Resource Management
11:45
12:45
Lunch
12:45
1:30
Module 10: Data Streams and Files
1:30
2:15
Lab 10: Files
2:15
2:30
Break
2:30
3:30
Module 11: Internet Access
3:30
4:15
Lab 11: Creating a DateTime Client/Server Application
Start
End
Module
9:00
9:30
Module 12: Serialization
9:30
10:15
Lab 12: Serialization
10:15
10:30
Break
10:30
11:30
Module 13: Remoting and Web Services
11:30
12:30
Lunch
12:30
1:20
Lab 13.1: Building an Order-Processing Application by Using Remoted Servers
1:20
2:20
Module 13: Remoting and Web Services (continued)
2:20
2:35
Break
2:35
3:30
Lab 13.2: Using an XML Web Service
Day 4
Day 5
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
xi
Trainer Materials Compact Disc Contents The Trainer Materials compact disc contains the following files and folders: !
Autorun.exe. When the compact disc is inserted into the compact disc drive, or when you double-click the Autorun.exe file, this file opens the compact disc and allows you to browse the Student Materials or Trainer Materials compact disc.
!
Autorun.inf. When the compact disc is inserted into the compact disc drive, this file opens Autorun.exe.
!
Default.htm. This file opens the Trainer Materials Web page.
!
Readme.txt. This file explains how to install the software for viewing the Trainer Materials compact disc and its contents and how to open the Trainer Materials Web page.
!
2349B_ms.doc. This file is the Manual Classroom Setup Guide. It contains the steps for manually setting up the classroom computers.
!
2349B_sg.doc. This file is the Automated Classroom Setup Guide. It contains a description of classroom requirements, classroom configuration, instructions for using the automated classroom setup scripts, and the Classroom Setup Checklist.
!
Powerpnt. This folder contains the Microsoft PowerPoint® slides that are used in this course.
!
Pptview. This folder contains the Microsoft PowerPoint Viewer, which is used to display the PowerPoint slides.
!
Setup. This folder contains the files that install the course and related software to computers in a classroom setting.
!
StudentCD. This folder contains the Web page that provides students with links to resources pertaining to this course, including additional reading, review and lab answers, lab files, multimedia presentations, and courserelated Web sites.
!
Tools. This folder contains files and utilities used to complete the setup of the instructor computer.
!
Webfiles. This folder contains the files that are required to view the course Web page. To open the Web page, open Windows Explorer, and in the root directory of the compact disc, double-click Default.htm or Autorun.exe.
xii
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
Student Materials Compact Disc Contents The Student Materials compact disc contains the following files and folders: !
Autorun.exe. When the compact disc is inserted into the CD-ROM drive, or when you double-click the Autorun.exe file, this file opens the compact disc and allows you to browse the Student Materials compact disc.
!
Autorun.inf. When the compact disc is inserted into the compact disc drive, this file opens Autorun.exe.
!
Default.htm. This file opens the Student Materials Web page. It provides students with resources pertaining to this course, including additional reading, review and lab answers, lab files, multimedia presentations, and course-related Web sites.
!
Readme.txt. This file explains how to install the software for viewing the Student Materials compact disc and its contents and how to open the Student Materials Web page.
!
2349B_ms.doc. This file is the Manual Classroom Setup Guide. It contains a description of classroom requirements, classroom setup instructions, and the classroom configuration.
!
Democode. This folder contains demonstration code.
!
Flash. This folder contains the installer for the Macromedia Flash 5.0 browser plug-in.
!
Fonts. This folder contains fonts that are required to view the PowerPoint presentation and Web-based materials.
!
Labs. This folder contains files that are used in the hands-on labs. These files may be used to prepare the student computers for the hands-on labs.
!
Media. This folder contains files that are used in multimedia presentations for this course.
!
Mplayer. This folder contains the setup file to install Microsoft Windows Media™ Player.
!
Webfiles. This folder contains the files that are required to view the course Web page. To open the Web page, open Windows Explorer, and in the root directory of the compact disc, double-click Default.htm or Autorun.exe.
!
Wordview. This folder contains the Word Viewer that is used to view any Word document (.doc) files that are included on the compact disc.
Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET)
xiii
Document Conventions The following conventions are used in course materials to distinguish elements of the text. Convention
Use
"
Indicates an introductory page. This symbol appears next to a topic heading when additional information on the topic is covered on the page or pages that follow it.
bold
Represents commands, command options, and syntax that must be typed exactly as shown. It also indicates commands on menus and buttons, dialog box titles and options, and icon and menu names.
italic
In syntax statements or descriptive text, indicates argument names or placeholders for variable information. Italic is also used for introducing new terms, for book and course titles, and for emphasis in the text.
Title Capitals
Indicate domain names, user names, computer names, directory names, and folder and file names, except when specifically referring to case-sensitive names. Unless otherwise indicated, you can use lowercase letters when you type a directory name or file name in a dialog box or at a command prompt.
ALL CAPITALS
Indicate the names of keys, key sequences, and key combinations — for example, ALT+SPACEBAR.
monospace
Represents code samples or examples of screen text.
[]
In syntax statements, enclose optional items. For example, [filename] in command syntax indicates that you can choose to type a file name with the command. Type only the information within the brackets, not the brackets themselves.
{}
In syntax statements, enclose required items. Type only the information within the braces, not the braces themselves.
|
In syntax statements, separates an either/or choice.
!
Indicates a procedure with sequential steps.
...
In syntax statements, specifies that the preceding item may be repeated.
. . .
Represents an omitted portion of a code sample.
THIS PAGE INTENTIONALLY LEFT BLANK
Introduction
Contents Introduction
1
Course Materials
2
Prerequisites
3
Course Outline
4
Microsoft Certified Professional Program
9
Facilities
11
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Introduction
iii
Instructor Notes Presentation: 30 Minutes
The Introduction module provides students with an overview of the course content, materials, and logistics for Course 2349B, Programming with the Microsoft® .NET Framework (Microsoft Visual C#™ .NET).
Course Materials and Preparation Required Materials To teach this course, you need the following materials: !
Delivery Guide
!
Trainer Materials compact disc
Preparation Tasks To prepare for this course, you must complete the Course Preparation Checklist that is included with the trainer course materials.
Module Strategy Use the following strategy to present this module: !
Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) Show the slide that displays the course number and course title.
!
Introduction Welcome students to the course and introduce yourself. Provide a brief overview of your background to establish credibility. Have students introduce themselves and provide their background, product experience, and expectations of the course. Record student expectations on a white board or flip chart that you can reference later in class.
!
Course Materials Explain the purpose of all materials used in this course.
!
Prerequisites Provide the students with the list of prerequisites that they should have met before taking this course. This is an opportunity for you to identify students who may not have the appropriate background or experience to attend this course.
iv
Introduction !
Course Outline Provide an overview of each module and what students will learn. Explain how this course will meet students’ expectations by relating the information covered in individual modules to their expectations. Providing reasonably complete coverage of the Microsoft .NET Framework within a five day class is a challenging undertaking. The thirteen modules that encompass Course 2349B, Programming with the Microsoft.NET Framework (Microsoft Visual C# .NET), will provide most students with a common baseline for working with the .NET Framework. Note For more information about customizing this course see the Optional Course Presentation Strategies section in this module.
!
Setup Provide the students with any necessary setup information for the course.
!
Microsoft Certified Professional Program Inform students about the Microsoft Certified Professional (MCP) program and the various certification options.
!
Facilities Explain the facility information for the training site.
Introduction
v
Optional Course Presentation Strategies Because of the complexity and amount of material that makes up the .NET Framework, additional resources are included to provide instructors with some flexibility in the course delivery.
Course Customization The thirteen modules that make up the five day Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET), will provide most students with a common baseline for working with the .NET Framework. In addition to the first thirteen modules that make up the five days of material, the course contains four optional modules, thus providing you with the opportunity for customization. You are not required to cover the optional modules as part of Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET). The decision about whether to cover the optional modules has been left entirely to you, the instructor, as you will need to consider the circumstances of each class separately, including student levels and preferences, and any requirements of an individual Microsoft Certified Technical Education Center (Microsoft CTEC). However, as part of the course materials, your students will receive all seventeen modules. While Modules 14 through 17 are considered optional, you should be prepared to discuss what you plan to cover during the course introduction. If you only intend to cover the first thirteen modules that make up the official five days of course delivery of Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET), then state that clearly and suggest that the optional modules may be used as additional selfstudy. Alternatively, you may want to discuss covering one or more of the optional modules if there is sufficient interest in those topics. You will then need to consider with your students what module(s) you may safely leave out in order to accommodate the optional module(s). For example, an alternative approach for more advanced students who already have a basic understanding of .NET Framework applications and the Microsoft Visual C#™ .NET object model would be to omit Modules 2 through 6 and teach Modules 7 through 17.
vi
Introduction
Course Flow As a general guideline, modules may be grouped accordingly, as shown in the following table. Module
Course Flow
Module 1: Overview of the Microsoft .NET Framework
This short module is designed to provide a high level overview of the .NET Framework.
Module 2: Introduction to a Managed Execution Environment
Modules 2 through 4 cover core .NET Framework concepts, including managed execution, assemblies, and deployment.
Module 3: Working with Components Module 4: Deployment and Versioning Module 5: Common Type System Module 6: Working with Types Module 7: Strings, Arrays, and Collections Module 8: Delegates and Events Module 9: Memory and Resource Management
Modules 5 and 6 cover core .NET Framework object oriented topics. Modules 7 through 13 cover more advanced material than the preceding modules and may be considered as standalone material. Modules 10 through 12 have some relevance to Module 13: Remoting and XML Web Services.
Module 10: Data Streams and Files Module 11: Internet Access Module 12: Serialization Module 13: Remoting and XML Web Services Module 14: Threading and Asynchronous Programming Module 15: Interoperating Between Managed and Unmanaged Code Module 16: Using Microsoft ADO.NET to Access Data Module 17: Attributes
Modules 14 through 17 may be considered as optional, standalone, and generally more advanced than modules 1 through 6.
Introduction
vii
Course Timing This section provides estimated course timings for all of the modules, labs, and breaks in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET). The following schedule options are provided as a guide to help with ideas about how to organize your class if you decide to customize the course.
Option 1 The following schedule is an estimate of the course timing if you choose to teach Modules 1 through 13. This is the basic approach for students needing additional instruction regarding Visual C# .NET and the .NET Framework approach to assemblies, packaging and the object model. If you do not intend to customize Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET), this is the approach you should use. Your timing may vary.
Day 1 Start
End
Module
9:00
9:30
Introduction
9:30
10:00
Module 1: Overview of the Microsoft .NET Framework
10:00
10:15
Break
10:15
11:00
Module 2: Introduction to a Managed Execution Environment
11:00
11:15
Lab 2: Building a Simple .NET Application
11:15
11:45
Module 3: Working with Components
11:45
12:00
Lab 3.1: Creating a .NET Framework Component
12:00
1:00
Lunch
1:00
1:15
Module 3: Working with Components (continued)
1:15
1:30
Lab 3.2: Creating a Simple Console-Based Client
1:30
2:00
Module 3: Working with Components (continued)
2:00
2:30
Lab 3.3: Calling a Component Through an ASP .NET Page
2:30
2:45
Break
2:45
4:15
Module 4: Deployment and Versioning
Start
End
Module
9:00
9:50
Lab 4: Packaging and Deployment
9:50
10:00
Break
10:00
11:30
Module 5: Common Type System
11:30
12:30
Lunch
12:30
1:15
Lab 5: Building Simple Types
1:15
2:30
Module 6: Working with Types
2:30
2:45
Break
2:45
3:30
Lab 6: Working with Types
3:30
4:00
Module 7: Strings, Arrays, and Collections
Day 2
viii
Introduction
Day 3 Start
End
Module
9:00
10:30
Module 7: Strings, Arrays, and Collections (continued)
10:30
10:45
Break
10:45
11:45
Lab 7: Working with Strings, Enumerators, and Collections
11:45
12:45
Lunch
12:45
2:00
Module 8: Delegates and Events
2:00
2:15
Break
2:15
3:30
Lab 8: Creating a Simple Chat Server
3:30
4:00
Module 9: Memory and Resource Management
Start
End
Module
9:00
10:30
Module 9: Memory and Resource Management (continued)
10:30
10:45
Break
10:45
11:45
Lab 9: Memory and Resource Management
11:45
12:45
Lunch
12:45
1:30
Module 10: Data Streams and Files
1:30
2:15
Lab 10: Files
2:15
2:30
Break
2:30
3:30
Module 11: Internet Access
3:30
4:15
Lab 11: Creating a DateTime Client/Server Application
Start
End
Module
9:00
9:30
Module 12: Serialization
9:30
10:15
Lab 12: Serialization
10:15
10:30
Break
10:30
11:30
Module 13: Remoting and Web Services
11:30
12:30
Lunch
12:30
1:20
Lab 13.1: Building an Order-Processing Application by Using Remoted Servers
1:20
2:20
Module 13: Remoting and Web Services (continued)
2:20
2:35
Break
2:35
3:30
Lab 13.2: Using an XML Web Service
Day 4
Day 5
Introduction
Option 2 The following schedule is an estimate of the course timing if you choose to teach Module 1 followed by Modules 7 through 17. This approach would serve more advanced students who already have a solid understanding of Visual C# .NET and the .NET Framework approach to assemblies, packaging and the object model. Your timing may vary.
Day 1 Start
End
Module
9:00
9:30
Introduction
9:30
10:00
Module 1: Overview of the Microsoft .NET Framework
10:00
10:15
Break
10:15
11:45
Module 7: Strings, Arrays, and Collections
11:45
12:45
Lunch
12:45
1:15
Module 7: Strings, Arrays, and Collections (continued)
1:15
2:15
Lab 7: Working with Strings, Enumerators, and Collections
2:15
2:30
Break
2:30
3:55
Module 8: Delegates and Events
3:55
4:15
Break
4:15
5:15
Lab 8: Creating a Simple Chat Server
Start
End
Module
9:00
10:30
Module 9: Memory and Resource Management
10:30
10:45
Break
10:45
11:20
Module 9: Memory and Resource Management (continued)
11:20
11:50
Lab 9: Memory and Resource Management
11:50
1:00
Lunch
1:00
1:30
Lab 9: Memory and Resource Management (continued)
1:30
2:15
Module 10: Data Streams and Files
2:15
3:00
Lab 10: Files
3:00
3:15
Break
3:15
4:15
Module 11: Internet Access
4:15
5:00
Lab 11: Creating a DateTime Client/Server Application
Day 2
ix
x
Introduction
Day 3 Start
End
Module
9:00
9:30
Module 12: Serialization
9:30
10:15
Lab 12: Serialization
10:15
10:30
Break
10:30
11:30
Module 13: Remoting and XML Web Services
11:30
12:30
Lunch
12:30
1:20
Lab 13.1: Building an Order-Processing Application by Using Remoted Servers
1:20
2:20
Module 13: Remoting and XML Web Services (continued)
2:20
2:35
Break
2:35
3:30
Lab 13.2: Using an XML Web Service
Start
End
Module
9:00
10:15
Module 14: Threading and Asynchronous Programming
10:15
10:30
Break
10:30
11:45
Module 14: Threading and Asynchronous Programming (continued)
11:45
12:45
Lunch
12:45
1:45
Lab 14: Working with Multithreaded Applications
1:45
2:00
Break
2:00
2:45
Module 15: Interoperating Between Managed and Unmanaged Code
2:45
3:15
Lab 15.1: Calling Win32 APIs
3:15
3:30
Break
3:30
4:15
Module 15: Interoperating Between Managed and Unmanaged Code (continued)
4:15
4:45
Lab 15.2: Calling COM Objects
Start
End
Module
9:00
10:30
Module 16: Using Microsoft ADO.NET to Access Data
10:30
10:45
Break
10:45
11:15
Module 16: Using Microsoft ADO.NET to Access Data (continued)
11:15
12:15
Lab 16: Using ADO.NET to Access Data
12:15
1:15
Lunch
1:15
2:15
Module 17: Attributes
2:15
2:30
Break
2:30
3:15
Lab 17: Defining and Using Attributes
Day 4
Day 5
Introduction
xi
Other Options The standard approach could be further customized by substituting one or more of Modules 7 through 13 with Modules 14 through 17. In addition, a more advanced option of delivery would be to extend the course by offering all seventeen modules. However, the decision to offer this delivery option would need to be reached in accordance with the policies and business requirements of individual Microsoft Certified Technical Education Centers (Microsoft CTECs). The following table lists the times for individual labs and modules. Lecture Time (only)
Lab Time (only)
Lecture and Lab Time (combined)
Module 1: Overview of the Microsoft .NET Framework
30 minutes
None
30 minutes
Module 2: Introduction to a Managed Execution Environment
45 minutes
20 minutes
65 minutes
Module 3: Working with Components
70 minutes
60 minutes
130 minutes
Module 4: Deployment and Versioning
90 minutes
50 minutes
140 minutes
Module 5: Common Type System
90 minutes
45 minutes
130 minutes
Module 6: Working with Types
75 minutes
45 minutes
125 minutes
Module 7: Strings, Arrays, and Collections
120 minutes
60 minutes
180 minutes
Module 8: Delegates and Events
75 minutes
75 minutes
150 minutes
Module 9: Memory and Resource Management
120 minutes
60 minutes
180 minutes
Module 10: Data Streams and Files
45 minutes
45 minutes
90 minutes
Module 11: Internet Access
60 minutes
45 minutes
105 minutes
Module 12: Serialization
30 minutes
45 minutes
75 minutes
Module 13: Remoting and XML Web Services
120 minutes
105 minutes
225 minutes
Module 14: Threading and Asynchronous Programming
150 minutes
60 minutes
210 minutes
Module 15: Interoperating Between Managed and Unmanaged Code
90 minutes
60 minutes
150 minutes
Module 16: Using Microsoft ADO.NET to Access Data
120 minutes
60 minutes
180 minutes
Module 17: Attributes
60 minutes
45 minutes
105 minutes
Module Title
Introduction
1
Introduction Topic Objective
To introduce yourself, establish credibility, meet students, and set student expectations for the course.
Lead-in
Good morning. Welcome to Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET). My name is...
!
Name
!
Company Affiliation
!
Title/Function
!
Job Responsibility
!
Programming Experience
!
.NET Framework Experience
!
Expectations for the Course
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Introduce yourself. Provide a brief overview of your background to establish credibility as a .NET Framework instructor. Ask students to introduce themselves, addressing the bulleted items on the slide.
Delivery Tip
As students introduce themselves, use a white board or flip chart to record their expectations of the course.
2
Introduction
Course Materials Topic Objective
To identify and describe the course materials.
Lead-in
We have provided everything you need for this course. You will find the following materials at your desk...
!
Name Card
!
Student Workbook
!
Student Materials Compact Disc
!
Course Evaluation
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The following materials are included with your kit:
Describe the contents of the student workbook and the Student Materials compact disc.
!
Name card. Write your name on both sides of the name card.
!
Have students write their names on both sides of the name card.
Student workbook. The student workbook contains the material covered in class, in addition to the hands-on lab exercises.
!
Student Materials compact disc. The Student Materials compact disc contains the Web page that provides you with links to resources pertaining to this course, including additional readings, review and lab answers, lab files, multimedia presentations, and course-related Web sites.
Tell students where they can send comments with feedback on the course.
Note To open the Web page, insert the Student Materials compact disc into the CD-ROM drive, and then in the root directory of the compact disc, double-click Autorun.exe or Default.htm.
Delivery Tip
Demonstrate how to open the Web page provided on the Student Materials compact disc. On the Trainer Materials compact disc, double-click Autorun.exe or Default.htm in the StudentCD folder.
!
Course evaluation. To provide feedback on the course, training facility, and instructor, you will have the opportunity to complete an online evaluation near the end of the course. To provide additional comments or inquire about the Microsoft Certified Professional program, send e-mail to
[email protected].
Introduction
3
Prerequisites Topic Objective
To present and describe the prerequisites for this course.
Lead-in
The following prerequisite knowledge is needed for this course.
!
Proficiency in the C++ or Java Programming Languages
!
A basic understanding of the C# Language
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This course requires that you meet the following prerequisites: !
Proficiency in the C++ or Java programming languages
!
A basic understanding of the C# language Students can meet the C# language prerequisite by taking Course 2124, Introduction to C# Programming for the Microsoft .NET Platform.
4
Introduction
Course Outline Topic Objective
To provide an overview of each module and what students will learn.
Lead-in
!
Module 1: Overview of the Microsoft .NET Framework
!
Module 2: Introduction to a Managed Execution Environment
!
Module 3: Working with Components
!
Module 4: Deployment and Versioning
!
Module 5: Common Type System
!
Module 6: Working with Types
In this course, we will cover...
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Briefly describe each module. As you describe each module, acknowledge any information that will meet the student expectations that you recorded earlier.
Module 1, “Overview of the Microsoft .NET Framework,” defines terminology specific to the Microsoft® .NET Framework and describes its key features and benefits. This module also discusses the namespaces taught in this course. There will be minimal lecture and no lab. After completing this module, you will be able to list the major elements of the .NET Framework. Module 2, “Introduction to a Managed Execution Environment,” introduces the concept of managed execution and shows developers how to quickly build applications that take advantage of the new .NET Framework common language runtime environment. After completing this module, you will be able to explain the main concepts behind the common language runtime and use the features of the .NET Framework to create a simple application. Module 3, “Working with Components,” discusses how to create a small, componentized application where modules can easily be written in either Microsoft Visual C#™ or Microsoft Visual Basic®. The steps necessary to construct, compile, and run each program are covered in detail. This module also explains how to build the client application by using the Microsoft Windows® Forms library and ASP.NET Web Forms. After completing this module, you will be able to create and use components in Windows Formsbased and ASP.NET-based applications. Module 4, “Deployment and Versioning,” explains how to use deployment and versioning features of the .NET Framework common language runtime to build and deploy applications that are fully managed and protected. After completing this module, you will be able to use the deployment and versioning features of the .NET Framework common language runtime to deploy multiple versions of a component.
Introduction
Module 5, “Common Type System,” introduces the Common Type System. The module discusses how to differentiate between value types and reference types and examines how classes, interfaces, properties, methods, events, and values are represented in the .NET Framework. After completing this module, you will be able to create, use, and extend types by understanding the Common Type System architecture. Module 6, “Working with Types,” discusses the use of attributes to control visibility and inheritance on types and explains how to work with various type operations, such as boxing and unboxing, and type operators. In addition, this module discusses how to build an interface that supports methods and properties and how to make interface designs more efficient. Finally, this module highlights features that are designed to help you work with unmanaged types, such as COM types. After completing this module, you will be able to create classes and interfaces that are functionally efficient and appropriate for specific programming scenarios.
5
6
Introduction
Course Outline (continued) !
Module 7: Strings, Arrays, and Collections
!
Module 8: Delegates and Events
!
Module 9: Memory and Resource Management
!
Module 10: Data Streams and Files
!
Module 11: Internet Access
!
Module 12: Serialization
!
Module 13: Remoting and XML Web Services
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Module 7, “Strings, Arrays, and Collections,” describes some of the key classes in the .NET Framework class library. This module explains how to work with strings, arrays, collections, and enumerators. After completing this module, you will be able to use the .NET Framework class library to efficiently create and manage strings, arrays, collections, and enumerators. Module 8, “Delegates and Events,” explains how the .NET Framework uses delegates in callback and event-handling scenarios. After completing this module, you will be able to use delegates and events to have an event sender object signal the occurrence of an “action” to an event receiver object. Module 9, “Memory and Resource Management,” discusses how the .NET Framework automatically handles the allocation and release of an object’s memory resources through garbage collection. After completing this module, you will be able to describe and control how memory and other resources are managed in the .NET Framework. Module 10, “Data Streams and Files,” introduces the System.IO namespace and discusses the types that it contains that allow synchronous and asynchronous reading from and writing to data streams and files. This module discusses synchronous operations only, as asynchronous operations are beyond the scope of this course. After completing this module, you will be able to read from and write to data streams and files. Module 11, “Internet Access,” discusses the use of the System.Net classes to communicate with other applications by using common protocols, such as HTTP, Transmission Control Protocol (TCP), User Datagram Protocol (UDP), and Socket Internet protocols. After completing this module, you will be able to use the basic request/response model to send and receive data over the Internet.
Introduction
7
Module 12, “Serialization,” explains how to use serialization to convert a graph of objects into a linear sequence of bytes, which can then be sent to a remote computer and deserialized, thereby making a clone in the remote memory of the original graph of objects. After completing this module, you will be able to serialize and deserialize an object graph. Module 13, “Remoting and XML Web Services,” explains how .NET Remoting supports communication between objects in different application domains, in different processes, and on different computers. The module describes how the common language runtime remoting infrastructure provides a rich set of classes that enable you to ignore most of the complexities of deploying and managing remote objects. After completing this module, you will be able to create distributed applications by means of XML Web services and Object Remoting.
8
Introduction
Course Outline (continued) !
Module 14 (Optional): Threading and Asynchronous Programming
!
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
!
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
!
Module 17 (Optional): Attributes
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The following modules are optional and can also be used as further reading about .NET Framework topics. Module 14, “Threading and Asynchronous Programming,” covers the support that the Microsoft .NET Framework provides for working with multithreaded applications and asynchronous programming. After completing this module, in addition to creating and managing threads, you will be able to handle thread synchronization to maintain application responsiveness and avoid potential data corruption and other problems. Module 15, “Interoperating between Managed and Unmanaged Code,” introduces the services that are provided by the .NET Framework to allow managed code to interoperate with unmanaged code. After completing this module, you will be able to use Platform Invoke to call unmanaged functions that are implemented in a DLL. You will also be able to call COM objects from managed code. Module 16, “Using Microsoft ADO.NET to Access Data,” presents ADO.NET as an evolution of the ADO data access model, with a rich suite of data handling and data binding functions for manipulating all types of data. After completing this module, you will use the rapid application design facilities of Microsoft Visual Studio® .NET to create an XML Web service whose methods can be used to read and update a database by using ADO.NET. You will also Use Visual Studio.NET rapid application design facilitates to create a Windows Forms application that displays and enables a user to update a DataGrid control that is bound to a DataSet. The DataSet is obtained and updated by using an XML Web service. Module 17, “Attributes,” provides details about how to use attributes in code. It describes the predefined attributes that are provided by the Microsoft .NET Framework and provides some simple examples of how to use some common attributes. After completing this module, you will be able to use custom predefined attributes, create simple custom attributes, and query attribute information at run time.
Introduction
9
Microsoft Certified Professional Program Topic Objective
To provide students with information about the Microsoft Certified Professional Program.
Lead-in
The Microsoft Certified Professional Program includes these certifications…
http://www.microsoft.com/traincert/ *****************************ILLEGAL FOR NON-TRAINER USE****************************** The Microsoft Certified Professional program is a leading certification program that validates your experience and skills to keep you competitive in today’s changing business environment. The following table describes each certification in more detail. Certification
Description
MCSA on Microsoft Windows 2000
The Microsoft Certified Systems Administrator (MCSA) certification is designed for professionals who implement, manage, and troubleshoot existing network and system environments based on Microsoft Windows 2000 platforms, including the Windows .NET Server family. Implementation responsibilities include installing and configuring parts of the systems. Management responsibilities include administering and supporting the systems.
MCSE on Microsoft Windows 2000
The Microsoft Certified Systems Engineer (MCSE) credential is the premier certification for professionals who analyze the business requirements and design and implement the infrastructure for business solutions based on the Microsoft Windows 2000 platform and Microsoft server software, including the Windows .NET Server family. Implementation responsibilities include installing, configuring, and troubleshooting network systems.
MCSD
The Microsoft Certified Solution Developer (MCSD) credential is the premier certification for professionals who design and develop leading-edge business solutions with Microsoft development tools, technologies, platforms, and the Microsoft Windows DNA architecture. The types of applications MCSDs can develop include desktop applications and multi-user, Web-based, N-tier, and transaction-based applications. The credential covers job tasks ranging from analyzing business requirements to maintaining solutions.
10
Introduction
(continued) Certification
Description
MCDBA on Microsoft SQL Server 2000
The Microsoft Certified Database Administrator (MCDBA) credential is the premier certification for professionals who implement and administer Microsoft SQL Server™ databases. The certification is appropriate for individuals who derive physical database designs, develop logical data models, create physical databases, create data services by using Transact-SQL, manage and maintain databases, configure and manage security, monitor and optimize databases, and install and configure SQL Server.
MCP
The Microsoft Certified Professional (MCP) credential is for individuals who have the skills to successfully implement a Microsoft product or technology as part of a business solution in an organization. Hands-on experience with the product is necessary to successfully achieve certification.
MCT
Microsoft Certified Trainers (MCTs) demonstrate the instructional and technical skills that qualify them to deliver Microsoft Official Curriculum through Microsoft Certified Technical Education Centers (Microsoft CTECs).
Certification Requirements The certification requirements differ for each certification category and are specific to the products and job functions addressed by the certification. To become a Microsoft Certified Professional, you must pass rigorous certification exams that provide a valid and reliable measure of technical proficiency and expertise. For More Information See the Microsoft Training and Certification Web site at http://www.microsoft.com/traincert. You can also send e-mail to
[email protected] if you have specific certification questions.
Acquiring the Skills Tested by an MCP Exam Microsoft Official Curriculum (MOC) and MSDN® Training Curriculum can help you develop the skills that you need to do your job. They also complement the experience that you gain while working with Microsoft products and technologies. However, no one-to-one correlation exists between MOC and MSDN Training courses and MCP exams. Microsoft does not expect or intend for the courses to be the sole preparation method for passing MCP exams. Practical product knowledge and experience is also necessary to pass the MCP exams. To help prepare for the MCP exams, use the preparation guides that are available for each exam. Each Exam Preparation Guide contains exam-specific information, such as a list of the topics on which you will be tested. These guides are available on the Microsoft Training and Certification Web site at http://www.microsoft.com/traincert/.
Introduction
11
Facilities Topic Objective
Class Hours
To inform students of class logistics and rules for the training site.
Lead-in
Before we start, let’s go over the class logistics.
Building Hours
Phones
Parking
Messages
Rest Rooms
Smoking
Meals
Recycling
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Explain the class hours, extended building hours for labs, parking, rest room location, meals, phones, message posting, and where smoking is or isn’t allowed. Let students know if your facility has Internet access that is available for them to use during class breaks. Also make sure that the students are aware of the recycling program if one is available.
THIS PAGE INTENTIONALLY LEFT BLANK
Module 1: Overview of the Microsoft .NET Framework Contents Overview Overview of the Microsoft .NET Framework
1 2
Overview of Namespaces
13
Review
17
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 1: Overview of the Microsoft .NET Framework
iii
Instructor Notes Presentation: 30 Minutes Lab: 00 Minutes
This module provides students with an overview of the Microsoft® .NET Framework. It defines some of the terminology that is specific to the .NET Framework and describes the key features and benefits of the .NET Framework. The module starts with an overview of the .NET Framework, and then introduces the namespaces in the .NET Framework. It explains which modules teach which namespaces, and which namespaces are not covered in this course. Do not spend too much time on this module. This module is designed to provide only an overview, so do not go into too much detail. This module contains no labs. After completing this module, students will be able to: !
Describe the .NET Framework and its components.
!
Explain the relationship between the .NET Framework class library and namespaces.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft PowerPoint® file 2349B_01.ppt.
Preparation Tasks To prepare for this module, you should read all of the materials for this module.
iv
Module 1: Overview of the Microsoft .NET Framework
Module Strategy Use the following strategy to present this module: !
The .NET Framework Explain each part of the .NET Framework. One important goal of this slide is to explain what this course covers. This course primarily teaches the common language runtime and the .NET Framework class library. This course uses Microsoft Visual C#™ to teach the .NET Framework. Only minimal coverage of XML Web services, user interfaces, ADO.NET, and ASP.NET is provided. Other courses will cover these technologies in more detail. In addition, ADO.NET is covered more fully in Module 16, “Using Microsoft ADO.NET to Access Data,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
!
Common Language Runtime This is a build slide. Explain each of the following topics as they appear. Definitions of these topics are found in the content. • Class Loader • Microsoft Intermediate Language (MSIL) to Native compilers, Code Manager, and Garbage Collection • Security Engine, Debugger, Type Checker, Exception Manager, Thread Support, and COM Marshaler • .NET Framework Class Library Support
!
The .NET Framework Class Library Explain the benefits of the .NET Framework class library. Explain that the common type system is covered in more detail in Module 5, “Common Type System,” and Module 6, “Working with Types,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET). Specific classes are covered in Module 7, “Strings, Arrays, and Collections,” and other modules as appropriate.
!
ADO.NET: Data and XML This is a build slide. Explain the following topics as they appear. • System.Data Explain how this namespace works primarily with data, such as data from databases. • System.Xml Explain how this namespace works primarily with XML and Extensible Stylesheet Language (XSL).
Module 1: Overview of the Microsoft .NET Framework !
v
What Is an XML Web Service? This slide is the key slide for explaining XML Web services. Be sure that everyone understands the role of Web Services Description Language (WSDL), Universal Description, Discovery, and Integration (UDDI), SOAP, XML, and HTTP. Also explain that the .NET software development kit (SDK) and Microsoft Visual Studio® .NET provide tools to simplify the creation of XML Web services. Note that XML Web services are appropriate for internal applications in addition to external applications. An organization is likely to run multiple platforms; XML Web services are a good way of working across platforms because they rely on XML and SOAP. Any platform that supports XML and SOAP can use or expose XML Web services.
!
Web Forms and XML Web Services Explain how ASP.NET classes make it easier to work with user data on Web pages.
!
Namespaces Briefly explain the purpose of each namespace. The key point to explain is that there are a lot of data types and functionality in the .NET Framework. Namespaces arrange the types in a hierarchy that make it easier to work with the types.
!
Namespaces Used in this Course Briefly explain which modules teach which namespaces. Explain that not all of the namespaces are taught in their entirety. For example, the System.Reflection namespace is mentioned in Module 4, “Deployment and Versioning,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET), only in terms of versioning. System.Reflection is also mentioned in Module 17, “Attributes,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET). Also, explain that the ADO.NET namespace is covered more fully in Module 16, “Using Microsoft ADO.NET to Access Data,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET). The security namespace is not taught in this course as security is covered extensively in Course 2350A, Securing and Deploying Microsoft .NET Assemblies.
Module 1: Overview of the Microsoft .NET Framework
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
!
Overview of the Microsoft .NET Framework
!
Overview of Namespaces
In this module, you will be introduced to the .NET Framework. You will then learn about the namespaces and which modules teach certain namespaces.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The Microsoft® .NET Framework provides tools and technologies that you need to build distributed Web applications. In this module, you will learn the architecture of the .NET Framework. You will also learn how the .NET Framework class library is divided into namespaces, and which namespaces are taught in this course. After completing this module, you will be able to: !
Describe the .NET Framework and its components.
!
Explain the relationship between the .NET Framework class library and namespaces.
2
Module 1: Overview of the Microsoft .NET Framework
" Overview of the Microsoft .NET Framework Topic Objective
To provide an overview of the .NET Framework topics.
Lead-in
In this section, you will learn about the Microsoft .NET Framework.
!
The .NET Framework
!
Common Language Runtime
!
The .NET Framework Class Library
!
ADO.NET: Data and XML
!
What is an XML Web Service?
!
Web Forms and Services
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn about the .NET Framework. The .NET Framework is a set of technologies that form an integral part of the Microsoft .NET platform. It provides the basic building blocks for developing Web applications and XML Web services. This section includes the following topics: !
The .NET Framework
!
Common Language Runtime
!
The .NET Framework Class Library
!
ADO.NET: Data and XML
!
What is an XML Web Service?
!
Web Forms and Services
Module 1: Overview of the Microsoft .NET Framework
3
The .NET Framework Topic Objective
To understand the architecture of the .NET Framework.
Lead-in
VB
C++
C#
Perl
Web Services
Python
…
User Interface ASP.NET
The .NET Framework is an architecture consisting of a runtime, the class library, and language support.
ADO.NET: Data and XML .NET Framework Class Library Common Language Runtime Message Queuing
COM+ (Transactions, Partitions, Object Pooling)
IIS
WMI
Win32
*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework The .NET Framework provides the necessary compile time and runtime foundation to build and run .NET applications. The .NET Framework is the primary focus of this course.
Platform Substrate The .NET Framework must run on an operating system. Currently, the .NET Framework is built to work on the Microsoft Win32® operating systems. In the future, the .NET Framework will be extended to run on other platforms, such as Microsoft Windows® CE.
Application Services When running on Microsoft Windows 2000, application services, such as COM+, Message Queuing, Windows Internet Information Server (IIS), and Windows Management Instrumentation (WMI), are available to the developer. The .NET Framework exposes application services through classes in the .NET Framework class library.
Common Language Runtime The common language runtime simplifies application development, provides a robust and secure execution environment, supports multiple languages, and simplifies application deployment and management. The common language runtime environment is also referred to as a managed environment, in which common services, such as garbage collection and security, are automatically provided.
4
Module 1: Overview of the Microsoft .NET Framework
The .NET Framework Class Library The .NET Framework class library exposes features of the runtime and provides other high-level services that every developer needs. The classes simplify development of .NET applications. Developers can extend them by creating their own libraries of classes.
ADO.NET ADO.NET is the next generation of Microsoft ActiveX® Data Object (ADO) technology. ADO.NET provides improved support for the disconnected programming model. It also provides rich XML support.
ASP.NET Microsoft ASP.NET is a programming framework that is built on the common language runtime. ASP.NET can be used on a server to build powerful Web applications. ASP.NET Web Forms provide an easy and powerful way to build dynamic Web user interfaces (UI).
XML Web Services The .NET Framework provides tools and classes for building, testing, and distributing XML Web services.
User Interfaces The .NET Framework supports three types of user interfaces: !
Web Forms, which work through ASP.NET
!
Windows Forms, which run on Win32 clients
!
Console applications, which for simplicity, are used for most of the labs in this course
Module 1: Overview of the Microsoft .NET Framework
5
Languages Any language that conforms to the Common Language Specification (CLS) can run on the common language runtime. In the .NET Framework, Microsoft provides Microsoft Visual Basic®, Microsoft Visual C++®, Microsoft Visual C#™, and Microsoft JScript® support. Third parties can provide additional languages. Note C# has been submitted for standardization to ECMA, a vendor-neutral international standards organization committed to driving industry-wide adoption of information and communications technologies. This standardization will make it possible for any company which wishes to implement C# programming tools on any platform to do so. Microsoft has also submitted a subset of the Microsoft .NET Framework, called the Common Language Infrastructure (CLI), to ECMA. This will make it possible for other vendors to implement the CLI on a variety of platforms, so that software written using the basic architectural model presented by the .NET Framework can be created using a variety of tools on a variety of platforms.
Building Components in the .NET Framework In the .NET Framework, components are built on a common foundation. You no longer need to write the code to allow objects to interact directly with each other. In addition, you no longer need to write component wrappers in the .NET environment, because components do not use wrappers. The .NET Framework can interpret the constructs that developers are accustomed to using in objectoriented languages. The .NET Framework fully supports class, inheritance, methods, properties, events, polymorphism, constructors, and other objectoriented constructs.
6
Module 1: Overview of the Microsoft .NET Framework
Common Language Runtime Topic Objective
.NET Framework Class Library Support
To highlight some of the key components in the common language runtime.
Lead-in
This topic will give you an overview of the components of the common language runtime. I will briefly describe each component. As a C# developer, you will never see these discrete pieces, but this discussion will give you a better understanding of the richness of the runtime.
Thread Support
COM Marshaler
Type Checker
Exception Manager
Security Engine
Debugger
MSIL to Native Compilers
Code Manager
Garbage Collection
Class Loader
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The common language runtime simplifies application development, provides a robust and secure execution environment, supports multiple languages, and simplifies application deployment and management. The common language runtime environment is also referred to as a managed environment, one in which common services, such as garbage collection and security, are automatically provided. The common language runtime features are described in the following table. Component
Description
Class loader
Manages metadata, and the loading and layout of classes.
Microsoft Intermediate Language (MSIL) to native compiler
Converts MSIL to native code on a just-in-time basis.
Code manager
Manages code execution.
Garbage collection
Provides automatic lifetime management of all of objects in the .NET Framework, garbage collection is a multiprocessor, scalable garbage collector.
Security engine
Provides evidence-based security, based on user identity and the origin of the code.
Debugger
Enables the developer to debug an application and trace the execution of code.
Type checker
Does not allow unsafe casts or uninitialized variables. MSIL can be verified to guarantee type safety.
Exception manager
Provides structured exception handling, which is integrated with Windows Structured Exception Handling (SEH). Error reporting has been improved.
Thread support
Provides classes and interfaces that enable multithreaded programming.
COM marshaler
Provides marshaling to and from COM.
.NET Framework class library support
Integrates code with the runtime that supports the .NET Framework class library.
Module 1: Overview of the Microsoft .NET Framework
7
The .NET Framework Class Library Topic Objective
To provide an overview of the .NET Framework class library and the most common namespace: System.
Lead-in
In this topic, you will learn how .NET Framework class library exposes features of the runtime and provides other high-level services.
!
!
Spans All Programming Languages #
Enables cross-language inheritance and debugging
#
Integrates well with tools
Is Object-Oriented and Consistent #
Enhances developer productivity by reducing the number of APIs to learn
!
Has a Built-In Common Type System
!
Is Extensible
!
Is Secure
#
#
Makes it easy to add or modify framework features Allows creation of secure applications
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework class library exposes features of the runtime and provides other high-level services that every developer needs. Because there are hundreds of classes in the .NET Framework class library, classes are grouped into namespaces. The first part of the full name, which is located before the rightmost dot, is the namespace name. The last part of the name, which is located after the dot, is the type name. For example, System.Collections.ArrayList represents the ArrayList class, which belongs to the System.Collections namespace. The types in System.Collections namespace can be used to manipulate collections of objects.
Spans All Programming Languages The .NET Framework class library is language-independent so it enables crosslanguage inheritance and debugging. The .NET Framework class library also integrates fully with Microsoft Visual Studio® .NET, making it easy to develop applications with the library.
Is Object-Oriented and Consistent Unlike flat APIs that are numerous and unorganized, the .NET Framework class library is organized into namespaces and classes. This object-oriented approach groups related functionality and data together and enables the developer to work with the library in a more natural way.
8
Module 1: Overview of the Microsoft .NET Framework
Has a Built-In Common Type System The .NET Framework class library is type-safe. Type safety is ensured through the common type system, which is part of the common language runtime.
Is Extensible You can extend the library by creating your own classes and compiling them into libraries. If designed properly, your class library will also be objectoriented and language-independent.
Is Secure The .NET Framework class library provides rich security for your applications. You can use code access security and role-based security, and configure your own security policies. Furthermore, there are numerous security tools to assist in certificate creation, permission viewing, and so on.
Module 1: Overview of the Microsoft .NET Framework
9
ADO.NET: Data and XML Topic Objective
To explain the data and XML support in the runtime.
ADO.NET: Data and XML
Lead-in
The .NET Framework provides a new set of ADO.NET classes to handle data.
System.Data
System.Xml
OleDb
SqlClient
XSL
Common
SQLTypes
XPath
Serialization
*****************************ILLEGAL FOR NON-TRAINER USE****************************** ADO.NET, the next generation of ADO technology provides improved support for disconnected programming. It also provides rich XML support in the System.Xml namespace.
System.Data Namespace The System.Data namespace consists of classes that constitute the ADO.NET object model. At a high level, the ADO.NET object model is divided into two layers: the connected layer and the disconnected layer. The System.Data namespace includes the DataSet class, which represents multiple tables and their relations. These data sets are completely self-contained data structures that can be populated from a variety of data sources. One data source could be XML; another data source could be an OLE DB; and a third data source could be the direct adapter for Microsoft SQL Server™.
System.Xml Namespace The System.Xml namespace provides support for XML. It includes an XML parser and a writer, which are W3C-compliant. The Extensible Stylesheet Language for Transformation (XSLT) is provided by the System.Xml.Xsl namespace. The implementation of XPath, a comprehensive language for document addressing, enables data graph navigation in XML. The System.Xml.Serialization namespace provides the entire core infrastructure for XML Web services, including such features as moving back and forth from objects to an XML representation.
10
Module 1: Overview of the Microsoft .NET Framework
What Is an XML Web Service? Topic Objective
Open Internet Protocols
To define an XML Web service.
Lead-in
XML Web services are the fundamental building blocks of the .NET platform.
XML Web service A programmable application component accessible via standard Web protocols
!
Provide a Directory of Services on the Internet
!
XML Web services are defined in terms of the formats and ordering of messages
!
XML Web services consumers can send and receive messages using XML
!
Built using open Internet protocols
UDDI
Universal Description, Discovery, and Integration
WSDL
Web Services Description Language
SOAP XML & HTTP
*****************************ILLEGAL FOR NON-TRAINER USE****************************** XML Web services are an integral part of the .NET platform. They are the fundamental mechanism for exposing and consuming data and functionality across Web applications, both inside organizations and across organizations. XML Web services are units of application logic that provide data and services to other applications. Applications access XML Web services by means of industry standard Web protocols and data formats, such as HTTP, XML, and Simple Object Access Protocol (SOAP), regardless of how each XML Web service is implemented.
XML and HTTP XML Web services are built by using XML and HTTP. Because they are built with XML and HTTP, XML Web services operate without firewall restrictions. Also, because XML and HTTP are industry standards, any platform supporting XML and HTTP can work with XML Web services.
SOAP SOAP defines how messages are formatted, sent, and received when working with XML Web services. SOAP is also an industry standard that is built on XML and HTTP. Any platform that supports the SOAP standard can support XML Web services.
Module 1: Overview of the Microsoft .NET Framework
11
Web Services Description Language The Web Services Description Language (WSDL) is an XML format for describing the network services that are offered by the server. You use WSDL to create a file that identifies the services that are provided by the server and the set of operations within each service that the server supports. For each of the operations, the WSDL file also describes the format that the client must follow when requesting an operation.
Universal Description, Discovery, and Integration Universal Description, Discovery, and Integration (UDDI) is an industry standard for registering and searching for XML Web services. By using UDDI, developers can discover and use XML Web services that are available publicly over the Internet. For more information on UDDI, see Web Service Discovery in Module 13, “Remoting and XML Web Services,” in Course 2349B, Programming with the Microsoft.NET Framework (Microsoft Visual C# .NET), and the UDDI Web site at http://www.uddi.org.
12
Module 1: Overview of the Microsoft .NET Framework
Web Forms and Services Topic Objective
To show where Web Forms and XML Web services are found in the ASP.NET programming model.
ASP.NET System.Web
Lead-in
Services
The Internet is quickly evolving from today’s Web sites that simply deliver UI pages to browsers to a next generation of programmable Web sites that link organizations, applications, services, and devices directly.
UI
Description
HtmlControls
Discovery
WebControls
Protocols Caching
Security
Configuration
SessionState
*****************************ILLEGAL FOR NON-TRAINER USE****************************** ASP.NET is a programming framework built on the common language runtime that can be used on a server to build powerful Web applications. ASP.NET Web Forms provide an easy and powerful way to build dynamic Web UI pages. ASP.NET XML Web services provide the building blocks for constructing distributed Web-based applications. XML Web services are based on open Internet standards, such as HTTP and XML. The common language runtime provides built-in support for creating and exposing XML Web services by using a programming abstraction that is consistent and familiar to both ASP Web Forms and Visual Basic developers. The resulting model is both scalable and extensible. This model is based on open Internet standards, such as HTTP, XML, SOAP, and WSDL, so it can be accessed and interpreted by any client or Internet-enabled device. Some of the more common ASP.NET classes are described in this topic as follows:
System.Web In the System.Web namespace, there are lower-level services, such as caching, security, and configuration, which are shared between XML Web services and Web UIs.
System.Web.Services The System.Web.Services namespace has classes that handle XML Web services, such as protocols and discovery.
Controls There are two types of controls: HTML controls and Web controls. The System.Web.UI.HtmlControls namespace gives you direct mapping of HTML tags, such as input. The System.Web.UI.WebControls namespace enables you to structure controls with templates, such as grid controls.
Module 1: Overview of the Microsoft .NET Framework
13
" Overview of Namespaces Topic Objective
To provide an overview of the namespaces in the .NET Framework.
Lead-in
In this section, you will learn about the namespaces in the .NET Framework.
!
Namespaces
!
Namespaces Used in this Course
!
Namespaces Covered in Optional Modules
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn about the namespaces in the Microsoft .NET Framework. You will also learn about which namespaces are taught in this course. This section includes the following topics: !
Namespaces
!
Namespaces Used in this Course
!
Namespaces Covered in Optional Modules
14
Module 1: Overview of the Microsoft .NET Framework
Namespaces Topic Objective
To understand how namespaces provide an easy-to-use hierarchy of types and functionality.
Lead-in
The .NET Framework includes a large set of class library assemblies, which contain hundreds of types. These assemblies provide access to system functionality in your development process.
System Collections
IO
Security
Configuration
Net
ServiceProcess
Diagnostics
Reflection
Text
Globalization
Resources
Threading
Runtime
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework includes a large set of class library assemblies, which contain hundreds of types. These assemblies provide access to system functionality in your development process.
The Purpose of Namespaces Because the .NET Framework class library includes definitions for so many types, the library is organized in a hierarchical namespace structure. Namespaces use a dot-syntax naming scheme to group logically related classes together so that they can be easily searched and referenced. For example, the System.Data namespace contains the classes that constitute the ADO.NET architecture. The System.Xml namespace is the overall namespace for the XML classes that provide standards-based support for processing XML.
The System Namespace The System namespace is the root namespace for types in the .NET Framework. The System namespace contains the base type Object, from which all other types are derived. The System namespace also contains types for exception handling, garbage collection, console I/O, various tool types, format data types, random number generators, and math functions.
Module 1: Overview of the Microsoft .NET Framework
15
Namespaces Used in this Course Topic Objective
To explain which namespaces are taught in this course, and which namespaces are not taught.
Lead-in
This course covers many of the System namespaces. Not all namespaces are covered, for example the System.Security namespaces.
Module 2 #
System.Console
Module 3
Module 10 #
System.IO
Module 11
#
System.Windows.Forms
#
System.Net
#
System.Drawing
#
System.Net.Sockets
Module 4 #
System.Reflection
Module 7 #
System.Text
#
System.Collections
Module 12 #
System.Runtime.Serialization
Module 13 #
System.Runtime.Remoting.Channels
#
System.Web.Services
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This course covers many of the namespaces in the Microsoft .NET Framework. Module 2 teaches the System.Console namespace for printing output to the console. Module 3 teaches the System.Windows.Forms and System.Drawing namespaces for building a form with buttons that interacts with the user. Module 4 teaches the System.Reflection namespace for storing version and key file information in an assembly. Module 7 teaches the System.Text namespace for advanced string management, and System.Collections for maintaining collections of data. Module 10 teaches the System.IO namespace for reading and writing to files. Module 11 teaches the System.Net and System.Net.Sockets namespaces for transmitting data over the network. Module 12 teaches the System.Runtime.Serialization namespace for persisting objects to storage. Module 13 teaches the System.Runtime.Remoting.Channels and System.Web.Services namespaces for invoking remote objects, and building XML Web services.
16
Module 1: Overview of the Microsoft .NET Framework
Namespaces Covered in Optional Modules Topic Objective
To complete the namespace information that was presented in the preceding slide.
Lead-in
Here are some more namespaces that are covered in optional Modules 14 through 17 of this course.
Module 14 #
System.Threading
Module 16 #
System.Data
Module 17 #
System.Reflection
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Modules 14 through 17 are optional modules. Module 14 teaches the System.Threading namespace for enabling multithreaded programming. Module 16 teaches the System.Data namespace, which provides the base objects and types for the ADO.NET programming model. ADO.NET also provides rich XML support in the System.Xml namespace. Finally, Module 17 teaches the System.Reflection namespace, which contains classes that you can use for examining metadata.
Namespaces Not Covered This course does not teach the System.Security namespace. For more information about System.Security and related security namespaces, see Course 2350A, Securing and Deploying Microsoft .NET Assemblies.
Module 1: Overview of the Microsoft .NET Framework
17
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
!
Overview of the Microsoft .NET Framework
!
Overview of Namespaces
The review questions cover some of the key concepts taught in the module.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. List the components of the .NET Framework. The common language runtime, .NET Framework class library, data and XML, XML Web services and Web Forms, and Windows Forms.
2. What is the purpose of the common language runtime? It provides an environment in which you can execute code.
3. What is the purpose of the common language specification? It defines a set of features that all .NET languages should support.
4. What is an XML Web service? An XML Web service is a programmable Web component that can be shared among applications on the Internet or an intranet.
5. What is a managed environment? A managed environment is an environment that provides services, such as garbage collection, security, and other related features.
THIS PAGE INTENTIONALLY LEFT BLANK
Module 2: Introduction to a Managed Execution Environment Contents Overview Writing a .NET Application Compiling and Running a .NET Application
1 2 11
Lab 2: Building a Simple .NET Application
29
Review
32
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 2: Introduction to a Managed Execution Environment
Instructor Notes Presentation: 45 Minutes Lab: 20 Minutes
After completing this module, students will be able to: !
Create simple console applications in C#.
!
Explain how code is compiled and executed in a managed execution environment.
!
Explain the concept of garbage collection.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the following materials: !
Microsoft® PowerPoint® file 2349B_02.ppt
!
Sample managed module HelloDemoCS.exe
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Practice the demonstrations.
!
Review the animation.
!
Complete the lab.
iii
iv
Module 2: Introduction to a Managed Execution Environment
Demonstrations This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for the student notes.
Hello World This demonstration shows how to build a simple application in C#. In the following procedures, use Notepad to create the simple Hello World application, and build and run the HelloDemoCS.exe application from the command line.
! To create the source code in C# 1. Open Notepad and type the following code: // Allow easy reference to System namespace classes using System; // Create class to hold program entry point class MainApp { public static void Main() { // Write text to the console Console.WriteLine(“Hello World using C#!”); } }
2. Save the file as HelloDemoCS.cs. Important To use Microsoft Visual Studio® .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.
Module 2: Introduction to a Managed Execution Environment
! To compile the source code and build an executable program • From a Visual Studio .NET Command Prompt window, type the following syntax: csc HelloDemoCS.cs
Running the resulting executable file will generate the following output: Hello World using C#!
Viewing Assembly Metadata by Using the MSIL Disassembler This demonstration shows how to use the Microsoft Intermediate Language (MSIL) Disassembler (Ildasm.exe) to view an assembly’s metadata. The code for this demonstration is contained in one project and is located in \Democode\Mod02. To demonstrate how to use the MSIL Disassembler to view the contents of the HelloDemoCS.exe assembly, follow the directions in Demonstration: Using the MSIL Disassembler in this module.
v
vi
Module 2: Introduction to a Managed Execution Environment
Multimedia This section lists the multimedia items that are part of this module. Instructions for launching and playing the multimedia are included with the relevant slides.
Application Loading and Single-File Assembly Execution This animation will show students how a single-file private assembly is loaded and executed.
Module Strategy Use the following strategy to present this module: !
Writing a .NET Application Stress the importance of understanding the process of compiling and running Microsoft .NET Framework applications, by using the simple Hello World application. Focus primarily on the compilation and execution processes.
!
Compiling and Running a .NET Application This section introduces basic concepts of a managed execution environment and presents new terminology. Many of these concepts are covered in greater detail in subsequent modules in this course, in subsequent courses, and in the .NET Framework software development kit (SDK) documentation. Emphasize that you are primarily introducing new concepts and terminology. Be prepared to postpone answering questions that pertain to information that is covered in later modules. Encourage students to start reading the .NET Framework SDK documentation.
Module 2: Introduction to a Managed Execution Environment
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
!
Writing a .NET Application
!
Compiling and Running a .NET Application
This module introduces the concept of managed execution and shows you how to quickly build applications that use the Microsoft .NET Framework common language runtime environment.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This module introduces the concept of managed execution and shows you how to quickly build applications that use the Microsoft® .NET Framework common language runtime environment. A simple Hello World version of a console application illustrates most of the concepts that are introduced in this module. Because this course is an introduction to programming in the .NET Framework, you should spend some time reading the .NET Framework software development kit (SDK) documentation. In fact, the labs, demonstrations, and material for this module and other modules in this course are based on several tutorials in the .NET Framework SDK. After completing this module, you will be able to: !
Create simple console applications in C#.
!
Explain how code is compiled and executed in a managed execution environment.
!
Explain the concept of garbage collection.
2
Module 2: Introduction to a Managed Execution Environment
" Writing a .NET Application Topic Objective
To introduce the topics in the section.
Lead-in
Because all supported languages use the Common Type System and the .NET Framework class library, and run in the common language runtime, programs in the supported languages are similar.
!
Using a Namespace
!
Defining a Namespace and a Class
!
Entry Points, Scope, and Declarations
!
Console Input and Output
!
Case Sensitivity
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Because all supported languages use the Common Type System and the .NET Framework base class library, and run in the common language runtime, programs in the supported languages are similar. The most significant difference in programming with the supported languages is syntax. Delivery Tip
Stress the importance of understanding the process of compiling and running the .NET applications. Using Visual Studio .NET at this time may obscure the underlying processes.
Note In this module, and in Modules 3 and 4, Notepad is used as the source code editor, instead of the Microsoft Visual Studio® .NET development environment. The examples in these modules are simple enough to be compiled and built directly from a command prompt window. Working in Notepad will allow you to focus on the compilation and execution processes.
Module 2: Introduction to a Managed Execution Environment
3
Demonstration: Hello World Topic Objective
To demonstrate how to build a simple application in C#.
Lead-in
In this demonstration, you will learn how to build a simple application in C#.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this demonstration, you will learn how to build a simple application in C#. Delivery Tip
As this is a short, simple demonstration, you may want to let students try it themselves.
! To create the source code in C# 1. Open Notepad and type the following code: // Allow easy reference to System namespace classes using System; // Create class to hold program entry point class MainApp { public static void Main() { // Write text to the console Console.WriteLine(“Hello World using C#!”); } }
2. Save the file as HelloDemoCS.cs
4
Module 2: Introduction to a Managed Execution Environment
! To compile the source code and build an executable program Important To use Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt. • From a Visual Studio .NET Command Prompt window, type the following syntax: csc HelloDemoCS.cs
Running the resulting executable will generate the following output: Hello World using C#!
Module 2: Introduction to a Managed Execution Environment
5
Using a Namespace Topic Objective
To describe how to use namespaces in the .NET Framework.
!
Classes Can Be Fully Referenced // // declares declares aa FileStream FileStream object object System.IO.FileStream System.IO.FileStream aFileStream; aFileStream;
Lead-in
You can fully reference classes in which an instance of System.IO.FileStream is declared by using C#:
!
Or the Namespace of a Class Can Be Referenced #
No need to fully qualify contained class names using using System.IO; System.IO; ... ... FileStream FileStream aFileStream; aFileStream;
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can fully reference classes, as in the following example, in which an instance of System.IO.FileStream is declared by using C#: System.IO.Filestream aFileStream;
However, it is more convenient to reference the required namespaces in your program. Using the namespace effectively disposes of the need to qualify all class library references, as in the following example: using System.IO; ... FileStream aFileStream;
For example, in order to have convenient access to System objects, you must use the System namespace.
6
Module 2: Introduction to a Managed Execution Environment
Defining a Namespace and a Class Topic Objective
To describe how to define namespaces and classes in C#.
Lead-in
C# supports the creation of custom namespaces and classes within those namespaces.
!
C# Supports Creation of Custom Namespaces and Classes Within Those Namespaces namespace namespace CompCS CompCS {{ public public class class StringComponent StringComponent {{ ... ... }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** C# supports the creation of custom namespaces and classes within those namespaces. Tip The following is the general rule for naming namespaces: CompanyName.TechnologyName For example: Microsoft.Office This is merely a guideline. Third-party companies can choose other names.
Namespaces in C# In C#, you use the namespace statement to define a new namespace, which encapsulates the classes that you create, as in the following example: namespace CompCS { public class StringComponent { ... } }
Note that a namespace may be nested in other namespaces, and a single namespace may be defined in multiple files. A single source code file may also define multiple namespaces.
Module 2: Introduction to a Managed Execution Environment
7
Entry Points, Scope, and Declarations Topic Objective
To describe how to create program entry points in C#.
!
In C#, the External Entry Point for a Program Is in a Class
!
class class MainApp MainApp {{ public public static static void void Main() Main() {. . .} {. . .} }} C# Supports the Use of a Period As a Scope Resolution Operator
Lead-in
Every executable program must contain an external entry point, where the application begins its execution.
Console.WriteLine Console.WriteLine ("First ("First String"); String"); !
In C#, Objects Must Be Declared Before They Can Be Used and Are Instantiated Using the New Keyword Lib.Comp Lib.Comp myComp myComp == new new Lib.Comp(); Lib.Comp();
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Every executable program must contain an external entry point, where the application begins its execution. In C#, all code must be contained in methods of a class.
Entry Points in C# To accommodate the entry point code in C#, you must first specify the class, as in the following example: class MainApp
{...}
Next, you specify the entry point for your program. The compiler requires this entry point to be a public static method called Main, as in the following example: public static void Main () {...}
Scope C# uses the period as a scope resolution operator. For example, you use the syntax Console.WriteLine when referencing the WriteLine method of the Console object.
Declaring and Instantiating Variables In C#, you must declare a variable before it can be used. To instantiate the object, use the new keyword. The following example in C# shows how to declare an object of type Comp, in namespace Lib, with the name myComp: Lib.Comp myComp = new Lib.Comp();
8
Module 2: Introduction to a Managed Execution Environment
Console Input and Output Topic Objective
To describe how to use Console class methods in C#.
!
Lead-in
Console Class Methods #
You can use the runtime Console class of the System namespace for input and output to the console of any string or numeric value by using the Read, ReadLine, Write, and WriteLine methods.
Read, ReadLine, Write, and WriteLine
Console.WriteLine("Hello Console.WriteLine("Hello World World using using C#!"); C#!");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can use the common language runtime Console class of the System namespace for input and output to the console of any string or numeric value by using the Read, ReadLine, Write, and WriteLine methods. The following example shows a C# program that outputs a string to the console: using System; class MainApp { public static void Main() { // Write text to the console Console.WriteLine(“Hello World using C#!”); } }
Module 2: Introduction to a Managed Execution Environment
9
Case Sensitivity Topic Objective
To describe case sensitivity issues in programming languages.
!
Do Not Use Names That Require Case Sensitivity #
Components should be fully usable from both casesensitive and case-insensitive languages
#
Case should not be used to distinguish between identifiers within a single name scope
Lead-in
C++ and C# are casesensitive, but Visual Basic is not case-sensitive. !
Avoid the Following class class class class
customer customer Customer Customer
{...} {...} {...} {...}
void void foo(int foo(int X, X, int int x) x)
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Microsoft Visual C++® and C# are case-sensitive, but Microsoft Visual Basic® is not case-sensitive. To ensure that a program is compliant with the Common Language Specification (CLS), however, you must take special care with public names. You cannot use case to distinguish between identifiers within a single name scope, such as types within assemblies and members within types. The following examples show situations to avoid: !
Do not have two classes or namespaces whose names differ only by case. class customer { ... } class Customer { ... }
!
Do not have a function with two parameters whose names differ only by case. void foo(int X, int x)
This constraint enables Visual Basic (and potentially other case-insensitive languages) to produce and use components that have been created in other casesensitive languages. This constraint does not apply to your definitions of private classes, private methods on public classes, or local variables.
10
Module 2: Introduction to a Managed Execution Environment
Note To fully interact with other objects regardless of the language they were implemented in, objects must expose to callers only those features that are common to all the languages they must interoperate with. For this reason, a set of language features has been defined, called the Common Language Specification (CLS), which includes common language features that are needed by many applications. The CLS rules define a subset of the common type system; that is, all the rules that apply to the common type system apply to the CLS, except where stricter rules are defined in the CLS. If your component uses only CLS features in the API that it exposes to other code (including derived classes), the component is guaranteed to be accessible from any programming language that supports the CLS. Components that adhere to the CLS rules and use only the features included in the CLS are said to be CLS-compliant components.
Module 2: Introduction to a Managed Execution Environment
11
" Compiling and Running a .NET Application Topic Objective
To introduce the topics in the section.
!
Compiler Options
Lead-in
!
The Process of Managed Execution
!
Metadata
!
Microsoft Intermediate Language
!
Assemblies
!
Common Language Runtime Tools
!
Just-In-Time Compilation
!
Application Domains
!
Garbage Collection
Most aspects of programming in the .NET Framework are the same for all compatible languages; each supported language compiler produces selfdescribing, managed Microsoft intermediate language (MSIL) code.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Delivery Tip
Emphasize that you are primarily introducing new concepts and terminology. Be prepared to postpone answering questions that pertain to later modules. Encourage students to start reading the .NET Framework SDK documentation.
Key Points
String literals in an application are stored and transported as clear text. Therefore, you should avoid putting sensitive information such as passwords in string literals.
Most aspects of programming in the .NET Framework are the same for all compatible languages; each supported language compiler produces selfdescribing, managed Microsoft intermediate language (MSIL) code. All managed code runs using the common language runtime, which provides cross-language integration, automatic memory management, cross-language exception handling, enhanced security, and a consistent and simplified programming model. This section introduces basic concepts of a managed execution environment and presents new terminology. Many of these concepts are covered in greater detail in subsequent modules in this course, in subsequent courses, and in the .NET Framework SDK documentation.
12
Module 2: Introduction to a Managed Execution Environment
Compiler Options Topic Objective
To introduce compiler options in C#.
!
Compile Directly from a Command Prompt Window
Lead-in
The .NET Framework includes a command line compiler for C#.
>csc >csc HelloDemoCS.cs HelloDemoCS.cs !
Use /t to indicate target >csc >csc /t:exe /t:exe HelloDemoCS.cs HelloDemoCS.cs
!
Use /reference to reference assemblies
>csc >csc /t:exe /t:exe /reference:assemb1.dll /reference:assemb1.dll HelloDemoCS.cs HelloDemoCS.cs
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework includes a command line compiler for C#. The file name of the compiler is Csc.exe.
Compiling in C# To compile the source code for the Hello World application presented in the Hello World demonstration at the beginning of this module, type the following: csc HelloDemoCS.cs
This syntax invokes the C# compiler. In this example, you only need to specify the name of the file to be compiled. The compiler generates the program executable, HelloDemoCS.exe.
Module 2: Introduction to a Managed Execution Environment
13
Command Line Options In C#, you can obtain the complete list of command line options by using the /? switch as follows: csc /?
Common options include the /out switch, which specifies the name of the output file, and the /target switch, which specifies the target type. By default, the name of the output file is the name of the input file with an .exe extension. The default for the target type is an executable program. The following example shows the use of both the /out and /t switches in C#: csc /out:HelloDemoCS.exe /t:exe HelloDemoCS.cs
The /t switch is equivalent to the /target switch. For more information about compiler options, see the .NET Framework SDK documentation.
Using the /reference Compilation Option When referring to other assemblies, you must use the /reference compilation switch. The /reference compilation option allows the compiler to make information in the specified libraries available to the source that is currently being compiled. The following example shows how to build an executable program from the command line by using the /reference compilation option. csc /r:assemb1.dll,assemb2.dll /out:output.exe input.cs
The /r switch is equivalent to the /reference compilation switch.
14
Module 2: Introduction to a Managed Execution Environment
The Process of Managed Execution Topic Objective
EXE/DLL EXE/DLL (MSIL (MSILand and metadata) metadata)
To introduce fundamental concepts of compiling and executing code in a managed execution environment. In the .NET Framework, the common language runtime provides the infrastructure for a managed execution environment.
Source Source Code Code
Class Class Loader Loader
Class Class Libraries Libraries (MSIL (MSILand and metadata) metadata)
Lead-in
Compiler Compiler
JIT JIT Compiler Compiler with with optional optional verification verification Trusted, pre-JITed code only
Managed Native Code
Call to an uncompiled method
Execution Execution Security Security Checks Checks
Runtime Engine
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In the .NET Framework, the common language runtime provides the infrastructure for a managed execution environment. This topic introduces fundamental concepts of compiling and executing code in a managed execution environment and identifies new terminology.
Compiling Source Code When you develop an application in the .NET Framework, you can write the source code in any programming language as long as the compiler that you use to compile the code targets the common language runtime. Compilation of the source code produces a managed module. The managed module is contained within a physical file also known as a portable executable (PE) file. The file may contain the following items: !
Microsoft Intermediate Language (MSIL) The compiler translates the source code into MSIL, a CPU-independent set of instructions that can be efficiently converted to native code.
!
Type Metadata This information fully describes types, members, and other references, and is used by the common language runtime at run time.
!
A Set of Resources For example, .bmp or .jpg files.
Module 2: Introduction to a Managed Execution Environment
15
If the C# compiler’s target option is either exe or library, then the compiler produces a managed module that is an assembly. Assemblies are a fundamental part of programming with the .NET Framework. Assemblies are the fundamental units of sharing, deployment, security, and versioning in the common language runtime. The .NET common language runtime only executes MSIL code that is contained in an assembly. If the C# compiler’s target option is module, then the compiler produces a managed module that is not an assembly, it does not contain a manifest and cannot be executed by the common language runtime. A managed module can be added to an assembly by the C# compiler, or by using the .NET’s Assembly Generation Tool, Al.exe. Subsequent topics in this module cover MSIL, metadata, and assemblies in more detail.
Executing Code When a user executes a managed application, the operating system loader loads the common language runtime, which then begins executing the module’s managed MSIL code. Because current host CPUs cannot execute the MSIL instructions directly, the common language runtime must first convert the MSIL instructions into native code. The common language runtime does not convert all of the module’s MSIL code into CPU instructions at load time. Instead, it converts the instructions when functions are called. The MSIL is compiled only when needed. The component of the common language runtime that performs this function is called the justin-time (JIT) compiler. JIT compilation conserves memory and saves time during application initialization. For more information about the JIT compiler, see Just-In-Time Compilation in this module.
Application Domain Operating systems and runtime environments typically provide some form of isolation between applications. This isolation is necessary to ensure that code running in one application cannot adversely affect other, unrelated applications. Application domains provide a secure and versatile unit of processing that the common language runtime can use to provide isolation between applications. Application domains are typically created by runtime hosts, which are responsible for bootstrapping the common language runtime before an application is run.
16
Module 2: Introduction to a Managed Execution Environment
Metadata Topic Objective
To explain how metadata is used in the common language runtime.
Lead-in
Every compiler that targets the common language runtime is required to emit full metadata into every managed module.
!
Declarative Information Emitted at Compile Time
!
Included with All .NET Framework Files and Assemblies
!
Metadata Allows the Runtime to: #
Load and locate code
#
Enforce code security
#
Generate native code at runtime
#
Provide reflection
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Every compiler that targets the common language runtime is required to emit full metadata into every managed module. This topic explains how the common language runtime uses metadata.
Definition of Metadata Metadata is a set of data tables, which fully describe every element that is defined in a module. This information can include data types and members with their declarations and implementations, and references to other types and members. Metadata provides the common language runtime with all the information that is required for software component interaction. It replaces older technologies, such as Interface Definition Language (IDL) files, type libraries, and external registration. Metadata is always embedded in the .exe or .dll file containing the MSIL code. Therefore, it is impossible to separate metadata from the MSIL code.
Module 2: Introduction to a Managed Execution Environment
17
Uses for Metadata Metadata has many uses, but the following uses are most important: !
Locating and loading classes Because metadata and MSIL are included in the same file, all type information in that file is available to the common language runtime at compile time. There is no need for header files because all types in a particular assembly are described by the assembly’s manifest.
!
Enforcing security The metadata may or may not contain the permissions required for the code to run. The security system uses permissions to prevent code from accessing resources that it does not have authority to access.
Other uses for metadata include: !
Resolving method calls.
!
Setting up runtime context boundaries.
!
Providing reflection capability.
For more information about metadata, see “Metadata and Self-Describing Components” in the .NET Framework SDK documentation. For more information about verification of type safety, see Module 4, “Deployment and Versioning,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET).
18
Module 2: Introduction to a Managed Execution Environment
Microsoft Intermediate Language Topic Objective
To introduce MSIL and JIT compilation.
Lead-in
Microsoft intermediate language, sometimes called managed code, is the set of instructions that the compiler produces as it compiles source code.
!
Produced by Each Supported Language Compiler
!
Converted to Native Language by the Common Language Runtime's JIT Compilers
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Microsoft intermediate language (MSIL), sometimes called managed code, is the set of instructions that the compiler produces as it compiles source code. This topic explains MSIL and the general process for converting MSIL to native code.
Compiled MSIL Regardless of their logical arrangement, most assemblies contain code in the form of MSIL. MSIL is a CPU-independent machine language created by Microsoft in consultation with third-party compiler vendors. However, MSIL is a much higher-level language than most CPU machine languages. MSIL contains instructions for many common operations, including instructions for creating and initializing objects, and for calling methods on objects. In addition, it includes instructions for arithmetic and logical operations, control flow, direct memory access, and exception handling.
Conversion to Native Code Before MSIL code can be executed, it must be converted to CPU-specific or native code by a JIT compiler. The common language runtime provides an architecture-specific JIT compiler for each CPU architecture. These architecture-specific JIT compilers allow you to write managed code that can be JIT compiled and executed on any supported architecture. Note Any managed code that calls platform-specific native APIs or libraries can only run on a specific operating system. For more information about MSIL, see the .NET Framework SDK documentation.
Module 2: Introduction to a Managed Execution Environment
19
Assemblies Topic Objective
This topic introduces the concept of an assembly and the role of the assembly manifest.
ManagedModule Module Managed (MSILand andMetadata) Metadata) (MSIL ManagedModule Module Managed (MSILand andMetadata) Metadata) (MSIL
Assembly Assembly
Lead-in
The common language runtime uses an assembly as the functional unit of sharing and reuse.
Manifest Manifest
.html .gif
Resource Files
Multiple Managed Modules and Resource Files Are Compiled to Produce an Assembly
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The common language runtime uses an assembly as the functional unit of sharing and reuse.
Definition of an Assembly An assembly is a unit of class deployment, analogous to a logical .dll. Each assembly consists of all the physical files that make up the functional unit: any managed modules, and resource or data files. Conceptually, assemblies provide a way to consider a group of files as a single entity. You must use assemblies to build an application, but you can choose how to package those assemblies for deployment. An assembly provides the common language runtime with the information that it needs to understand types and their implementations. As such, an assembly is used to locate and bind to referenced types at run time.
20
Module 2: Introduction to a Managed Execution Environment
The Assembly Manifest An assembly contains a block of data known as a manifest, which is a table in which each entry is the name of a file that is part of the assembly. The manifest includes the metadata that is needed to specify the version requirements, security identity, and the information that is used to define the scope of the assembly and resolve references to resources and classes. Because the metadata makes an assembly self-describing, the common language runtime has the information it requires for each assembly to execute. All applications that are executed by the common language runtime must be composed of an assembly or assemblies. All files that make up an assembly must be listed in the assembly’s manifest. The manifest can be stored in singlefile assemblies or multi-file assemblies: !
Single-file assemblies When an assembly has only one associated file, the manifest is integrated into a single PE file.
!
Multi-file assemblies When an assembly has more than one associated file, the manifest can be a standalone file, or it can be incorporated into one of the PE files in the assembly.
For more information about assemblies, see “Assemblies” in the .NET Framework SDK documentation.
Module 2: Introduction to a Managed Execution Environment
21
Common Language Runtime Tools Topic Objective
This topic describes how the MSIL Assembler and MSIL Disassembler work.
!
Lead-in
The common language runtime provides two tools that you can use together to test and debug MSIL code.
Runtime Utilities for Working with MSIL #
MSIL Assembler (ilasm.exe) produces a final executable binary
#
MSIL Disassembler (ildasm.exe) inspects metadata and code of a managed binary
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The common language runtime provides two tools that you can use to test and debug MSIL code. !
The MSIL Assembler The MSIL Assembler (Ilasm.exe) takes MSIL as text input and generates a PE file, which contains the binary representation of the MSIL code and required metadata. The basic syntax is as follows: ilasm [options] filename [options]
!
The MSIL Disassembler You can use the MSIL Disassembler (Ildasm.exe) to examine the metadata and disassembled code of any managed module. You will use this tool to examine MSIL code in Lab 2, Building a Simple .NET Application.
22
Module 2: Introduction to a Managed Execution Environment
Demonstration: Using the MSIL Disassembler Topic Objective
To demonstrate how the MSIL Disassembler works.
Lead-in
This demonstration shows how to use the MSIL Disassembler to view an assembly’s metadata.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This demonstration shows how to use the MSIL Disassembler to view an assembly’s metadata.
Viewing Assembly Metadata by Using the MSIL Disassembler In the following procedures, you will see how to use the MSIL Disassembler (Ildasm.exe) to view the contents of the HelloDemoCS.exe assembly.
! To run the MSIL Disassembler on the HelloDemoCS.exe assembly • At a Visual Studio .NET Command Prompt window, change the directory to \Democode\Mod02 where the sample file HelloDemoCS.exe has been copied, and type the following command: ildasm HelloDemoCS.exe
Module 2: Introduction to a Managed Execution Environment
After you expand the MainApp icon, the MSIL Disassembler graphical user interface (GUI) displays information about the file HelloDemoCS.exe, as shown in the following illustration:
! To display the contents of the manifest 1. Double-click MANIFEST. The MANIFEST window appears, as follows:
2. Close the Manifest window, and then close ILDASM.
23
24
Module 2: Introduction to a Managed Execution Environment
Just-In-Time Compilation Topic Objective
To describe JIT compilation and introduce JIT compiler options.
Lead-in
MSIL code must be converted into native code before it can execute. Because an intermediate step is involved, the common language runtime optimizes the compilation process for efficiency.
!
Process for Code Execution #
MSIL converted to native code as needed
#
Resulting native code stored for subsequent calls
#
JIT compiler supplies the CPU-specific conversion
*****************************ILLEGAL FOR NON-TRAINER USE****************************** As previously stated in this module, MSIL code must be converted into native code before it can execute. Because an intermediate step is involved, the common language runtime optimizes the compilation process for efficiency.
The Code Execution Process The common language runtime compiles MSIL as needed. This just-in-time, or JIT, compiling saves time and memory. The basic process is as follows: 1. When the common language runtime loads a class type, it attaches stub code to each method. 2. For subsequent method calls, the stub directs program execution to the common language runtime component that is responsible for compiling a method’s MSIL into native code. This component of the common language runtime is frequently referred to as the JIT compiler. 3. The JIT compiler compiles the MSIL and the method’s stub is substituted with the address of the compiled code. Future calls to that method will not involve the JIT compiler because the compiled native code will simply execute.
Module 2: Introduction to a Managed Execution Environment
25
Application Domains Topic Objective
To describe how application domains provide application isolation.
Lead-in
Historically, process boundaries have been used to isolate applications running on the same computer.
!
Historically, Process Boundaries Used to Isolate Applications
!
In the Common Language Runtime, Application Domains Provide Isolation Between Applications
!
#
The ability to verify code as type-safe enables isolation at a much lower performance cost
#
Several application domains can run in a single process
Faults in One Application Cannot Affect Other Applications
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Historically, process boundaries have been used to isolate applications running on the same computer. Each application is loaded into a separate process, which isolates the application from other applications running on the same computer. The applications are isolated because memory addresses are process-relative; a memory pointer passed from one process to another cannot be used in any meaningful way in the target process. In addition, you cannot make direct calls between two processes. Instead, you must use proxies, which provide a level of indirection. Managed code must be passed through a verification process before it can be run (unless the administrator has granted permission to skip the verification). The verification process determines whether the code can attempt to access invalid memory addresses or perform some other action that could cause the process in which it is running to fail to operate properly. Code that passes the verification test is said to be type-safe. The ability to verify code as type-safe enables the common language runtime to provide as great a level of isolation as the process boundary, at a much lower performance cost. Application domains provide a secure and versatile unit of processing that the common language runtime can use to provide isolation between applications. You can run several application domains in a single process with the same level of isolation that would exist in separate processes, but without incurring the additional overhead of making cross-process calls or switching between processes. The ability to run multiple applications within a single process dramatically increases server scalability.
26
Module 2: Introduction to a Managed Execution Environment
Isolating applications is also important for application security. For example, you can run controls from several Web applications in a single browser process in such a way that the controls cannot access each other's data and resources. The isolation provided by application domains has the following benefits: !
Faults in one application cannot affect other applications. Because type-safe code cannot cause memory faults, using application domains ensures that code running in one domain cannot affect other applications in the process.
!
Individual applications can be stopped without stopping the entire process. Using application domains enables you to unload the code running in a single application.
Note You cannot unload individual assemblies or types. Only a complete domain can be unloaded. Code running in one application cannot directly access code or resources from another application. The common language runtime enforces this isolation by preventing direct calls between objects in different application domains. Objects that pass between domains are either copied or accessed by proxy. If the object is copied, the call to the object is local. That is, both the caller and the object being referenced are in the same application domain. If the object is accessed through a proxy, the call to the object is remote. In this case, the caller and the object being referenced are in different application domains. Crossdomain calls use the same remote call infrastructure as calls between two processes or between two computers.
Module 2: Introduction to a Managed Execution Environment
27
Multimedia: Application Loading and Single-File Assembly Execution Topic Objective
To describe how assembly code is executed.
Lead-in
In this demonstration, you will see how a single-file private assembly is loaded and executed.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
To start the animation, click the button in the lower-left corner of the slide. The animation plays automatically. To pause or rewind the animation, click the controls in the lower-left of the screen.
Applications that are implemented by using MSIL code require the common language runtime to be installed on the user’s computer in order to run. The common language runtime manages the execution of code. For example, when an application that is implemented as a single-file private assembly is run, the following tasks are performed: !
The Microsoft Windows® loader loads the PE file. The PE file contains a call to a function found in the file MSCorEE.dll.
!
Windows loads the file MSCorEE.dll and transfers control to it to initialize the common language runtime.
!
The common language runtime parses the metadata of the application assembly, and then the JIT compiler compiles the code.
!
The common language runtime then locates the PE file’s managed entrypoint (the Main method in the case of a C# program), and transfers control to this entry point.
If the application calls a private assembly: !
The common language runtime uses probing to locate the referenced assembly, beginning in the application’s root directory and then traversing the subfolders until the assembly is located. • If the assembly is not found, a TypeLoadException error occurs. • If the assembly is found, it is loaded by the common language runtime.
The common language runtime loader parses the manifest in the referenced assembly. The JIT compiler then compiles the required code in the assembly and passes control to the called function.
28
Module 2: Introduction to a Managed Execution Environment
Garbage Collection Topic Objective
To introduce memory and resource management in the .NET Framework.
!
Garbage Collection Provides Automatic Object Memory Management in the .NET Framework
!
You No Longer Need to Track and Free Object Memory
Lead-in
Every program uses resources, such as files, screen space, network connections, and database resources.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This topic introduces memory management in the .NET Framework. For more information about memory and resource management, see Module 9, “Memory and Resource Management,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
Current Memory Management Model When you create an object programmatically, you generally follow these steps: 1. Allocate memory for the object 2. Initialize the memory 3. Use the object 4. Perform cleanup on the object 5. Free the object’s memory If you forget to free memory when it is no longer required or try to use memory after it has been freed, you can generate programming errors. The tracking and correction of such errors are complicated tasks because the consequences of the errors are unpredictable.
Memory Management in the .NET Framework The common language runtime uses a heap called the managed heap to allocate memory for all objects. This managed heap is similar to a C-Runtime heap, however you never free objects from the managed heap. In the common language runtime, garbage collection is used to manage memory deallocation. The garbage collection process frees objects when they are no longer needed by the application. For more information about garbage collection, see Module 9, “Memory and Resource Management,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
Module 2: Introduction to a Managed Execution Environment
29
Lab 2: Building a Simple .NET Application Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn how to write, compile, and run a simple application in C# and how to use the MSIL Disassembler to examine an assembly.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: !
Write, compile, and run a simple application in C#.
!
Use the MSIL Disassembler to examine an assembly.
Lab Setup Only solution files are associated with this lab. The solution files for this lab are in the folder \Labs\Lab02\Solution.
Estimated time to complete this lab: 20 minutes
30
Module 2: Introduction to a Managed Execution Environment
Exercise 1 Creating the Program in C# In this exercise, you will create the source code for a small console application that takes user input and writes a string to the console by using C#. The lab uses the classic Hello World application to allow you to focus on basic concepts in a managed execution environment. To help you concentrate on the syntactical aspects of this lab, you will use Notepad to create and edit the source files. From a command prompt window, you will then compile the application and test the resulting executable program.
! Write the source code 1. Open Notepad and create a class in C# called MainApp. 2. Import the System namespace. 3. Define the program entry point. The entry point takes no arguments and does not return a value. 4. Create methods that accomplish the following: a. Print the following text to the console: “Type your name and press Enter”. b. Read in the resulting user input. c. Print the text “Hello” and append to the text the value that was read in. 5. Save the file as HelloLabCS.cs in the \Labs\Lab02 folder.
! Build and test the program Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt. 1. From a Visual Studio .NET Command Prompt window, type the syntax to build an executable program from HelloLabCS.cs. 2. Run the resulting executable program. Your C# program should generate the following output: Type your name and press Enter
When you press ENTER, the program outputs the text “Hello ” and whatever text you typed as input.
Module 2: Introduction to a Managed Execution Environment
31
Exercise 2 Using the MSIL Disassembler In this exercise, you will use the MSIL Disassembler to open a single assembly and familiarize yourself with the assembly manifest. In subsequent labs, you will explore assemblies in greater detail.
! Examine the metadata for the Hello World applications 1. Open a Visual Studio .NET Command Prompt window. 2. From the Visual Studio .NET Command Prompt window, type: >ildasm /source
3. Open HelloLabCS.exe and double-click Manifest. 4. Note the following: a. The externally referenced library named mscorlib b. The assembly name HelloLabCS c. Version information (for the HelloLabCS assembly and mscorlib) 5. Close the Manifest window, double-click MainApp, double-click Main, and view the MSIL and source code.
32
Module 2: Introduction to a Managed Execution Environment
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
!
Writing a .NET Application
!
Compiling and Running a .NET Application
The review questions cover some of the key concepts taught in the module.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. Name the root namespace for types in the .NET Framework. The System namespace is the root namespace for types in the .NET Framework.
2. What class and methods can your application use to input and output to the console? You can use the common language runtime’s Console class of the System namespace for input and output to the console of any string or numeric value by using the Read, ReadLine, Write, and WriteLine methods.
3. When compiling code that makes references to classes in assemblies other than mscorlib.dll what must you do? You must use the /reference compilation switch. The /reference compilation option allows the compiler to make information in the specified libraries available to the source that is currently being compiled. The /r switch is equivalent to /reference.
4. What is the code produced by a .NET compiler called? Microsoft intermediate language (MSIL), sometimes called managed code.
Module 2: Introduction to a Managed Execution Environment
5. What .NET component compiles MSIL into CPU specific native code? The just-in-time (JIT) compiler.
6. What feature of .NET ensures that object memory is freed? The garbage collection process.
33
THIS PAGE INTENTIONALLY LEFT BLANK
Module 3: Working with Components Contents Overview
1
An Introduction to Key .NET Framework Development Technologies
2
Creating a Simple .NET Framework Component
4
Lab 3.1: Creating a .NET Framework Component
11
Creating a Simple Console Client
14
Lab 3.2: Creating a Simple Console-Based Client 19 Demonstration: Creating a Windows Forms Client
22
Creating an ASP.NET Client
27
Lab 3.3: Calling a Component Through an ASP.NET Page
36
Review
40
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 3: Working with Components
Instructor Notes Presentation: 70 Minutes Lab: 60 Minutes
After completing this module, students will be able to: !
Create a simple Microsoft® .NET Framework component in C#.
!
Implement structured exception handling.
!
Create a simple .NET Framework console application that calls a component.
!
Create a .NET Framework client application by using the Windows Forms library.
!
Create an ASP.NET page that uses the previously developed .NET Framework component to create an ASP.NET application.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft PowerPoint® file 2349B_03.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Practice the demonstration.
!
Review the animation.
!
Complete the lab.
iii
iv
Module 3: Working with Components
Demonstrations This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for the student notes.
Creating a Windows Forms Client This demonstration shows how to build a simple Windows Forms client in Microsoft Visual Studio® .NET. If time permits, you can show the demonstration by using Microsoft Visual Basic®. The solution code for this demonstration is located in \DemoCode\Mod03\C# and \DemoCode\Mod03\VB. In this demonstration, point out how the Windows Form is created in the Main method. Also, point out how the Windows Forms Designer adds code to create and initialize the controls. Consider stepping through the code to explain the code to students.
! To create a Windows Form 1. Open Visual Studio .NET and create a Microsoft Visual C#™ project from the Windows Application template. Name the project WinForm_Client. 2. Add two buttons and a listbox to the form. Set the following properties. a. Set the button1 Text property to &Execute. b. Set the button2 Text property to &Close. c. Set the Form1 Text property to Client.
! To add references 1. On the Project menu, click Add Reference. 2. In the Add Reference dialog box, click the Browse button. 3. Select the CompCS.dll and CompVB.dll assemblies as references. These assemblies are located in \DemoCode\Mod03\C#. Then click the Open button. 4. Click OK. 5. Add references to the assemblies in the form code. Use aliases to avoid name conflicts. using CSStringComp = CompCS.StringComponent; using VBStringComp = CompVB.StringComponent;
Module 3: Working with Components
! To add button event handlers 1. In the form, double-click the Execute button to create a button-click event handler. 2. Add the following code to the event handler to create an instance of the C# component and Visual Basic component, and add their strings to the listbox. //Local Variables CSStringComp myCompCS = new CSStringComp(); VBStringComp myCompVB = new VBStringComp(); int stringCount=0; //Display results from C# Component for (stringCount=0; stringCount<myCompCS.Count;! stringCount++) { listBox1.Items.Add(myCompCS.GetString(stringCount)); } //Display results from Visual Basic Component for (stringCount=0; stringCount<myCompVB.Count;! stringCount++) { listBox1.Items.Add(myCompVB.GetString(stringCount)); }
3. In the form, double-click the Close button to create a button-click event handler. 4. In the Close button event handler, call the Close method to close the form. 5. Compile and run the application. When you click the Execute button, the list box should be filled with four C# strings and four Visual Basic strings.
Testing the ASP.NET Client To test the ASP.NET page, the following software must be installed on the test computer: !
Microsoft Internet Information Services (IIS)
!
The .NET Framework common language runtime
!
The .aspx file that contains the client code
!
All compiled components that are required by the client application
You must configure a virtual directory that points to the directory that contains the .aspx file. You can use the New Directory Wizard in the IIS snap-in to do this.
v
vi
Module 3: Working with Components
! To configure a virtual directory 1. Click the Start menu, click Control Panel, click Performance and Maintenance, click Administrative Tools, and then click Internet Information Services. 2. Expand the computer icon, expand the Web Sites folder, and then expand and select Default Web Site. 3. On the Action menu, point to New, and then click Virtual Directory. The Virtual Directory Creation Wizard is launched. 4. Click Next to continue. 5. In the Alias text box, type Test and click Next. 6. Browse to the \Labs\Lab03.3\SOLUTION\ASP.NET_Client folder that contains the file ClientASP.NET.aspx, click OK, and then click Next. 7. On the Access Permissions page, accept the default selections, click Next, and then click Finish. The Test folder is added to the Default Web Site. Ensure that the compiled component DLLs, CompCS.dll and CompVB.dll, are in a \Bin subdirectory under the starting point for the application virtual directory.
Module 3: Working with Components
vii
! To test the ASP.NET Client • Open Microsoft Internet Explorer, and type the following text in the Address bar: http://localhost/Test/ClientASP.NET.aspx When you press ENTER, the following display, or one similar to it, appears.
Multimedia This section lists the multimedia items that are part of this module. Instructions for launching and playing the multimedia are included with the relevant slides.
ASP.NET Execution Model This animation will show students how ASP.NET pages are processed on the server.
viii
Module 3: Working with Components
Module Strategy Use the following strategy to present this module: !
An Introduction to Key .NET Framework Technologies Briefly introduce Windows Forms, Web Forms, and XML Web services. Explain that you will show an example of a Windows Form in this module. Tell students that they will learn about XML Web services in Module 13, “Remoting and XML Web Services,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
!
Creating a Simple .NET Framework Component Show the basic approach to creating reusable classes by using C# and Visual Basic. Spend some time on the new structured exception handling techniques. Use the command line to compile the component. Break the lecture at this point and instruct students to do Lab 3.1, Creating a .NET Framework Component.
!
Creating a Simple Console Client Show how to write a simple console application that calls the .NET Framework runtime-compatible component that was created in Creating a Simple .NET Framework Component. Be sure to fully explain how to use a namespace alias to remove ambiguity to type references. Instruct students to do Lab 3.2, Creating a Simple Console-Based Client.
!
Creating an ASP.NET Client Emphasize the .NET Framework’s capability to execute the component code that was created in Creating a Simple .NET Framework Component in a variety of environments. Contrast the use of the component by the standalone client applications with that of an IIS ASP.NET page. Be sure to present the ASP.NET Execution Model animation to help students understand how ASP.NET pages are processed on the server. Instructions for running the animation are included in the Instructor Notes in the margins. The main objective of this section is to show students how to use an ASP.NET page to obtain the string data from the .NET Framework component, to format that data in HTML, and to return this HTML to a Web browser for display. Be sure to demonstrate how to test the ASP.NET page that was created in Creating an ASP.NET Client in this module. Students must understand how to configure a virtual directory that points to the directory that contains the .aspx file. Instruct students to do Lab 3.3, Calling a Component Through an ASP.NET Page.
Module 3: Working with Components
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
!
An Introduction to Key .NET Framework Development Technologies
!
Creating a Simple .NET Framework Component
!
Creating a Simple Console Client
!
Creating an ASP.NET Client
Lead-in
In this module, you will learn how to create a small client/server application.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** For Your Information
Tell students that the code examples used in this module are from the tutorial, “Introduction to Developing with the .NET Framework.” This tutorial is found in the .NET Framework Software Development Kit (SDK).
In this module, you will learn how to create a small client/server application in C#. The steps that are necessary to construct, compile, and run each program are covered in detail. You will also learn how to build the client application by using the Windows Forms library and an ASP.NET page. As in Module 2, “Introduction to a Managed Execution Environment” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET), the content of this module is based on the “Introduction to Developing with the .NET Framework” tutorial in the .NET Framework Software Development Kit (SDK). After completing this module, you will be able to: !
Create a simple Microsoft® .NET Framework component in C#.
!
Implement structured exception handling.
!
Create a simple .NET Framework console application that calls a component.
!
Create a .NET Framework client application by using the Windows Forms library.
!
Create an ASP.NET page that uses the previously developed .NET Framework component to create an ASP.NET application.
2
Module 3: Working with Components
An Introduction to Key .NET Framework Development Technologies Topic Objective
To introduce the technologies that are used to develop .NET Framework applications.
Lead-in
To develop applications in the .NET Framework, you should be familiar with Windows Forms, ASP.NET Web Forms, and XML Web services.
!
Windows Forms
!
Web Forms
!
XML Web Services
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To develop applications in the .NET Framework, you should be familiar with the following technologies: Windows Forms, ASP.NET Web Forms, and XML Web services. Your choice of technology, or technologies, depends on the type of software solution that you wish to implement. This topic briefly introduces the technologies and provides references to additional information.
Windows Forms Windows Forms are used to develop applications in which the client computer handles most of the application processing. Classic Microsoft Win32® desktop applications, such as drawing and graphics applications, data-entry systems, point-of-sale systems, and games, are well suited to Window Forms. All of these applications rely on the power of the desktop computer for processing and for high-performance content display. The System.Windows.Forms namespace contains the classes used to create Windows Forms.
Module 3: Working with Components
Web Forms ASP.NET Web Forms are used to create applications in which the primary user interface is a browser. Obviously, you can use Web Forms to create applications that are available on the World Wide Web, such as e-commerce applications, but you will also find them helpful for creating other types of applications, such as intranet applications, which users can run by using only their browser, which is already installed on their computers. Because Web Forms applications are platform-independent, users can interact with your application regardless of the type of browser or computer that they are using. You can also optimize Web Forms applications to use features that are built into the most recent browsers, such as Dynamic Hypertext Markup Language (DHTML) and HTML 4.0, which enhance performance and responsiveness. The System.Web namespace contains the classes used to create Web Forms. For more information about Windows Forms and ASP.NET Web Forms, see “Windows Forms and Web Forms Recommendations” in the .NET Framework SDK documentation.
XML Web Services An XML Web Service is a programmable entity that resides on a Web server and is exposed through standard Internet protocols. In many respects, the programming model for creating and using XML Web services is similar to the programming model for creating and using COM-based components. However, unlike COM, the XML Web services programming model is based on simple, open standards that are broadly supported. Instead of using binary communication methods between applications, XML Web services use communication based on the SOAP protocol to transport XML messages between applications. In an environment of integrated, programmable XML Web services, XML is the universal language for communication of raw data and information that can be understood and acted upon. For more information about XML Web services, see Module 13, “Remoting and XML Web Services” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
3
4
Module 3: Working with Components
" Creating a Simple .NET Framework Component Topic Objective
To provide an overview of the section topics.
Lead-in
In this section, you will learn how to use C# to create a simple component.
!
Using Namespaces and Declaring the Class
!
Creating the Class Implementation
!
Implementing Structured Exception Handling
!
Creating a Property
!
Compiling the Component
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn how to create a simple component using C#. The component provides a wrapper for an array of strings and includes a GetString method that takes an integer and returns a string. The component includes a read-only Count property that contains the number of elements in the string array, which is used to iterate over all of the array members. The GetString method also demonstrates the use of structured exception handling. Delivery Tip
If you prefer, you can use Notepad to demonstrate the code in this section, instead of discussing the content of each slide.
In subsequent sections of this module, you will learn how clients use the GetString method and the Count property to determine and display the output from the component’s string array. The simple string component that is described in this section shows the basic approach to creating reusable classes in C#.
Module 3: Working with Components
5
Using Namespaces and Declaring the Class Topic Objective
To explain how to create a new namespace that encapsulates classes.
!
Create a New Namespace
Lead-in
To create a new namespace that encapsulates the classes that you will be creating, you use the namespace statement.
using using System; System; namespace namespace CompCS CompCS {...} {...} !
Declare the Class public public class class StringComponent StringComponent {...} {...}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To create a new namespace that encapsulates the classes that you will be creating, you use the namespace statement, as in the following example: using System; namespace CompCS {...}
The following statement denotes that instances of the StringComponent class will now be created by the runtime and managed in the garbage-collected heap. public class StringComponent {...}
6
Module 3: Working with Components
Creating the Class Implementation Topic Objective
To explain how to create the class implementation.
!
private private string[] string[] stringSet; stringSet;
Lead-in
The component provides a wrapper for an array of strings.
Declare a Private Field of Type Array of String Elements
!
Create a Public Default Constructor public public StringComponent() StringComponent() {...} {...}
!
Assign the stringSet Field to an Array of Strings stringSet stringSet == new new string[] string[] {{ "C# String 0", "C# String 0", "C# "C# String String 1", 1", ... ... }; };
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The component provides a wrapper for an array of strings. You can declare a private field of type array of string elements, as in the following example: private string[ ] stringSet;
The following example shows how to create a public default constructor, which executes each time a new instance of the class is created, and how to assign the stringSet field to an array of strings: public StringComponent() { stringSet = new string[ ] { "C# String 0", "C# String 1", "C# String 2", "C# String 3" }; }
In the preceding example, you can see that the constructor has the same name as the class and does not have a return type.
Module 3: Working with Components
7
Implementing Structured Exception Handling Topic Objective
To explain how to implement structured exception handling.
!
Implement the GetString Method public public string string GetString(int GetString(int index) index) {...} {...}
Lead-in
The component uses the GetString method to return the strings in the stringSet array.
!
Create and Throw a New Object of Type IndexOutOfRangeException
if((index if((index = >= stringSet.Length)) stringSet.Length)) {{ throw throw new new IndexOutOfRangeException(); IndexOutOfRangeException(); }} return return stringSet[index]; stringSet[index]; !
Exceptions Caught by the Caller Using a try/catch/finally Statement
!
Structured Exception Handling Replaces HRESULT-Based Error Handling in COM
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Programs must be able to uniformly handle errors and exceptions that occur during execution. The common language runtime helps you design errortolerant software by providing a platform for notifying programs of errors in a uniform manner. All .NET Framework methods indicate failure by throwing exceptions. Traditionally, a language’s error-handling model relied on either the language’s unique way of detecting errors and locating handlers for them, or on the errorhandling mechanism that is provided by the operating system. The runtime implements exception handling with the following features: !
It handles exceptions without regard for the language that generates the exception or the language that will be called upon to handles the exception.
!
It does not require any particular language syntax for handling exceptions, but allows each language to define its own syntax.
!
It allows exceptions to be thrown across process boundaries and machine boundaries.
Exceptions offer several advantages over other methods of error notification. Failures do not go unnoticed. Invalid values do not continue to propagate through the system. You do not have to check return codes. Exception-handling code can be easily added to increase program reliability. Finally, the runtime’s exception handling is faster than Windows-based C++ error handling.
8
Module 3: Working with Components
The component uses the GetString method to return the strings in the stringSet array. You can use a throw statement to implement structured exception handling within the GetString method, as in the following example: public string GetString(int index) { if ((index < 0) || (index >= stringSet.Length)) { throw new IndexOutOfRangeException(); } return stringSet[index]; }
In the preceding example, the program evaluates whether the integer that is passed as an argument to GetString is a valid index for the stringSet array. If the index is invalid, the following statement creates and throws a new object of type IndexOutOfRangeException: throw new IndexOutOfRangeException();
If the index is valid, the program returns the string element of the stringSet array at that particular index. Exceptions may be caught by the caller by using a try/catch/finally statement. Place the sections of code that might throw exceptions in a try block and place code that handles exceptions in a catch block. You can also write a finally block that always runs regardless of how the try block runs. The finally block is useful for cleaning up resources after a try block. For example, in C#: try { // code that might } catch(Exception e) // place code that } finally { // place code that }
throw exceptions { handles exceptions
runs after try or catch runs
In general, it is good programming practice to catch a specific type of exception rather than using the preceding general catch statement that catches any exception. Structured exception handling replaces the HRESULT-based error-handling system that is used in COM. All .NET Framework exception-handling classes, such as IndexOutOfRangeException and user-defined exceptions, must derive from System.Exception. For more information about exception handling, see “Handling and Throwing Exceptions” in the .NET Framework SDK documentation.
Module 3: Working with Components
9
Creating a Property Topic Objective
To explain how to create a property.
Lead-in
To provide the number of string elements in the stringSet array in C#, you create a read-only property called Count.
!
Create a Read-Only Count Property to Get the Number of String Elements in the stringSet Array public public int int Count Count {{ get get {{ return return stringSet.Length; stringSet.Length; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To provide the number of string elements in the stringSet array, you create a read-only property called Count, as in the following example: public int Count { get { return stringSet.Length; } }
For Your Information
Point out that the GetString method and Count property may be used by a client to iterate over the component’s string elements. This information will help students when they create the clients in subsequent labs.
10
Module 3: Working with Components
Compiling the Component Topic Objective
To explain how to compile the source code for a component.
Lead-in
To compile the source code, first you must save the source file.
!
Use the /target:library Switch to Create a DLL #
Otherwise, an executable with a .dll file extension is created instead of a DLL library
csc csc /out:CompCS.dll /out:CompCS.dll /target:library /target:library CompCS.cs CompCS.cs
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To compile the source code for the C# component, first you must save the source file, and then enter the following command from a command prompt window: Important To use Microsoft Visual Studio® .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt. csc /out:CompCS.dll /target:library CompCS.cs
This command directs the compiler to produce the file CompCS.dll. The /target:library switch is required to actually create a DLL library, instead of an executable.
Module 3: Working with Components
11
Lab 3.1: Creating a .NET Framework Component Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn how to create a simple .NET Framework component in C#.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** For Your Information
In this introductory module, the lab problems have intentionally been made very similar to the problems presented in the module. This approach is designed to allow the student to experience a wide range of .NET software approaches within a very limited amount of time.
Objective After completing this lab, you will be able to create a simple .NET Framework component in C#.
Lab Setup Only solution files are associated with this lab. The solution files for this lab are in the folder \Labs\Lab03.1\Solution.
Scenario This lab is based on a small client server application scenario in which you write both the client application and server component using C#. This lab focuses on building the server component. Lab 3.2, “Creating a Simple Console-Based Client,” focuses on building a client program that calls the server component.
Estimated time to complete this lab: 15 minutes
12
Module 3: Working with Components
Exercise 1 Creating a Component in C# In this exercise, you will use C# to create a component that provides a wrapper for a private array of strings. The wrapper includes a read-only Count property, which returns the number of strings in the array, and a public GetString method, which takes an integer argument and returns the string in the private array at that index. The GetString method will also implement structured exception handling to ensure that the argument of the index is within range of the array.
! Specify namespace information and class declarations 1. Open Notepad and reference the System namespace. 2. Create a new namespace named CompCS. CompCS will contain the class for your component.
!
Create the class implementation
1. Create a public class named StringComponent. 2. Declare a private field named StringSet of type array of string elements. 3. Create a public default constructor. The default constructor takes no arguments. 4. Assign to the field StringSet an array of strings initialized with the following four strings: "C# "C# "C# "C#
String String String String
0", 1", 2", 3"
5. Create the public GetString method, which takes an integer as an argument. That integer must be a valid index for the StringSet array. a. If the integer is an invalid index, throw an index out of range exception. b. If the integer is a valid index, return the StringSet string at that index. 6. Create a read-only Count property that gets the number of string elements in the StringSet array. 7. Save the file as CompCS.cs in the \Labs\Lab03.1 folder.
Module 3: Working with Components
13
! Compile the source code Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt. • From a Visual Studio .NET command prompt window, enter the command to build a library named CompCS.dll from the CompCS.cs. source. You must specify that the target is a library.
14
Module 3: Working with Components
" Creating a Simple Console Client Topic Objective
To provide an overview of the section topics.
Lead-in
In this section, you will learn how to write a simple console application that calls a .NET Framework runtime-compatible component.
!
Using the Libraries
!
Instantiating the Component
!
Calling the Component
!
Building the Client
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn how to write a simple console application that calls the .NET Framework runtime-compatible component that was created in the previous section.
Module 3: Working with Components
15
Using the Libraries Topic Objective
To explain how to import libraries and to use namespace aliasing to remove ambiguity.
!
Reference Types Without Having to Fully Qualify the Type Name using using CompCS; CompCS;
Lead-in
By using the namespaces in the program, you can reference types in the library without fully qualifying the type name.
using using CompVB; CompVB; !
If Multiple Namespaces Contain the Same Type Name, Create a Namespace Alias to Remove Ambiguity
using using CSStringComp CSStringComp == CompCS.StringComponent; CompCS.StringComponent; using using VBStringComp VBStringComp == CompVB.StringComponent; CompVB.StringComponent;
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This topic explains how to reference types in your applications by using namespaces and how to use a namespace alias if you need to remove ambiguity to type references.
Using the Namespaces By using the namespaces in the program, you can reference types in the library without fully qualifying the type name. To use the component namespaces in C#, you must enter a using statement followed by the name of the component namespaces: using CompCS; using CompVB;
Using an Alias Consider a scenario where similar C# and Microsoft Visual Basic® components use the same type name (StringComponent). You must still fully qualify the type name to remove any ambiguity when referring to the GetString method and the Count property. In C#, you can create and use aliases to solve this problem. The following C# example shows how to alias the component namespaces so that you do not have to fully qualify the type names: using CSStringComp = CompCS.StringComponent; using VBStringComp = CompVB.StringComponent;
16
Module 3: Working with Components
Instantiating the Component Topic Objective
To describe how to instantiate the component in C#.
Lead-in
To instantiate the StringComponent class, you declare a local variable of type StringComponent and create a new instance of StringComponent.
!
Declare a Local Variable of Type StringComponent
!
Create a New Instance of the StringComponent Class
//… //… using using CSStringComp CSStringComp == CompCS.StringComponent; CompCS.StringComponent; //… //… CSStringComp CSStringComp myCSStringComp myCSStringComp == new new CSStringComp(); CSStringComp();
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To instantiate the StringComponent class, you declare a local variable of type StringComponent and create a new instance of StringComponent, as in the following example: //… using CSStringComp = CompCS.StringComponent; class MainApp { public static void Main() { CSStringComp myCSStringComp = new CSStringComp(); //… } }
Module 3: Working with Components
17
Calling the Component Topic Objective
To describe how to call a component after it has been instantiated.
!
Lead-in
After instantiating the StringComponent class, the client can iterate over the string array in the C# or Visual Basic component and return the appropriate output.
Iterate over All the Members of StringComponent and Output the Strings to the Console
for for (int (int index index == 0; 0; index index /// Required designer variable. /// private System.ComponentModel.Container components = null; public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); } /// <summary> /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); }
(Code continued on the following page.)
23
24
Module 3: Working with Components #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.button2 = new System.Windows.Forms.Button(); this.listBox1 = new System.Windows.Forms.ListBox(); this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(24, 232); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(64, 24); this.button1.TabIndex = 0; this.button1.Text = "&Execute"; this.button1.Click += new System.EventHandler(this.button1_Click); // // button2 // this.button2.Location = new System.Drawing.Point(192, 232); this.button2.Name = "button2"; this.button2.Size = new System.Drawing.Size(64, 24); this.button2.TabIndex = 1; this.button2.Text = "&Close"; this.button2.Click += new System.EventHandler(this.button2_Click); // // listBox1 // this.listBox1.Location = new System.Drawing.Point(8, 8); this.listBox1.Name = "listBox1"; this.listBox1.Size = new System.Drawing.Size(264, 199); this.listBox1.TabIndex = 2; //
(Code continued on the following page.)
Module 3: Working with Components
25
// Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(292, 273); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.listBox1, this.button2, this.button1}); this.Name = "Form1"; this.Text = "Client"; this.ResumeLayout(false); } #endregion /// <summary> /// The main entry point for the application. /// [STAThread] static void Main() { Application.Run(new Form1()); } private void button1_Click(object sender, System.EventArgs e) { //Local Variables CSStringComp myCompCS = new CSStringComp(); VBStringComp myCompVB = new VBStringComp(); int stringCount=0; //Display results from C# Component for (stringCount=0; stringCount<myCompCS.Count;stringCount++) { listBox1.Items.Add( myCompCS.GetString(stringCount)); }
(Code continued on the following page.)
26
Module 3: Working with Components //Display results from Visual Basic Component for (stringCount=0; stringCount<myCompVB.Count;stringCount++) { listBox1.Items.Add( myCompVB.GetString(stringCount)); } } private void button2_Click(object sender, System.EventArgs e) { Close(); } } }
Module 3: Working with Components
27
" Creating an ASP.NET Client Topic Objective
To provide an overview of the section topics.
Lead-in
In this section, you will learn how to use an ASP.NET page to obtain the string data from the .NET Framework component, to format the data in HTML, and to return this HTML to a Web browser for display.
!
Writing the HTML for the ASP.NET Application
!
Coding the Page_Load Event Handler
!
Generating the HTML Response
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this module, you have learned how to create two stand-alone versions of a client application that calls the simple string component: a console application and a form based on the Windows Forms library. Another important feature of the .NET Framework is the capability to execute the component code that was used by such stand-alone client applications in a Microsoft Internet Information Services (IIS) ASP.NET page. In this section, you will learn how to use an ASP.NET page to obtain the string data from the .NET Framework component, to format that data in HTML, and to return this HTML to a Web browser for display.
28
Module 3: Working with Components
Multimedia: ASP.NET Execution Model Topic Objective
To describe the ASP.NET execution model.
Lead-in
In this animation, you will see how ASP.NET pages are processed on the server.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To launch the animation, click the button in the lowerleft corner of the slide. To play the animation, click the Start button, and then click the First Request, Second Request, and Output Cache buttons at the top of the screen. There is no audio, but explanatory text appears in the Info column when you click one of the buttons.
Tell students that they can view the animation again later for themselves by opening the 2349B_mod3.htm file from the Media folder.
The .NET Framework provides the capability to execute code that was used in the preceding stand-alone client applications in an ASP.NET page. However, there are some differences in the code execution processes. An ASP.NET page generally produces HTML in response to a HTTP request; and the page itself is compiled dynamically, unlike the stand-alone client applications. Each ASP.NET page is individually parsed, and the syntax is checked. Then, a .NET runtime class is produced, compiled, and invoked. ASP.NET caches the compiled object, so subsequent requests do not perform the parse and compile step and thus execute much faster. In this animation, you will see how ASP.NET pages are processed on the server. To view the animation, open the 2349B_mod3.htm file from the Media folder.
Module 3: Working with Components
29
Writing the HTML for the ASP.NET Application Topic Objective
To describe how to write the HTML for an ASP.NET application.
!
Lead-in
An ASP.NET file is a text file that contains markup syntax for coding server-side page logic, dynamic output, and literal content.
Specify Page-Specific Attributes Within a Page Directive
!
Import the Namespace and the Physical Assembly
!
Specify Code Declaration Blocks <script <script language="C#" language="C#" runat=server> runat=server> ... ...
*****************************ILLEGAL FOR NON-TRAINER USE****************************** An ASP.NET file is a text file that contains markup syntax for coding serverside page logic, dynamic output, and literal content. By default, ASP.NET files have an .aspx extension or .ascx for user controls. However, ASP.NET will parse and compile any file that is mapped to the aspnet_isapi.dll under IIS.
Specifying @ Page Directives To create the ASP.NET page that calls the string component, you first specify page-specific attributes within a page directive. Page directives specify optional settings that are used by the page compiler when processing ASP.NET files, as shown in the following example:
The following table describes the two @ Page directive attributes that are used in the sample client ASP.NET page. Attribute
Description
Language
Language that is used when compiling all and blocks within a page. Can be Visual Basic, C#, or Microsoft JScript® .NET.
Description
Provides a text description of the page. Supports any string description.
30
Module 3: Working with Components
Specifying @ Import Directives After specifying an @ Page directive, you include an @ Import directive, which explicitly imports a namespace into a page, as in the following example:
When you use an @ Import directive to import a namespace into a page, all classes and interfaces of the imported namespace are made available to the page. The imported namespace can be part of the .NET Framework class library or a user-defined namespace. In addition, the @ Import directive specifies the name of the assembly that will be used. The assembly must be located in the \Bin subdirectory of the application’s starting point.
Specifying Code Declaration Blocks After specifying @ Page directives and importing the required namespaces, you add code declaration blocks in which you place the code that calls the string component in the ASP.NET page. You define code declaration blocks by using <script> tags that contain a runat attribute, which tells the server to execute the code on the server, instead of sending the code text back to the client as part of the HTML stream. The script element may optionally use a language attribute to specify the language of its inner code, as in the following example: ... <script language="C#" runat=server> ... ...
If you do not specify a language, ASP.NET defaults to the language that was configured for the base page, which is determined by the @ Page directive. For more information about directives in ASP.NET, see “Directive Syntax” in the .NET Framework SDK documentation.
Module 3: Working with Components
31
Coding the Page_Load Event Handler Topic Objective
void void Page_Load(Object Page_Load(Object sender, sender, EventArgs EventArgs EvArgs) EvArgs) {{ StringBuilder StringBuilder Out Out == new new StringBuilder(""); StringBuilder(""); int Count = 0; int Count = 0; // // Iterate Iterate over over component's component's strings strings and and concatenate concatenate Out.Append("Strings Out.Append("Strings from from C# C# Component
"); Component
"); CompCS.StringComponent CompCS.StringComponent myCSStringComp myCSStringComp == new new CompCS.StringComponent(); CompCS.StringComponent(); for(int for(int index index == 0; 0; index index runat=server/>
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The following example shows how to create the HTML response by using the Message object: <span id="Message" runat=server/>
This code places the string output that was generated by the Page_Load event handler in the HTTP response.
34
Module 3: Working with Components
The code for the final ASP.NET page is as follows:
<script language="C#" runat=server> void Page_Load(Object sender, EventArgs EvArgs) { StringBuilder Out = new StringBuilder(); int Count = 0; // Iterate over component's strings and concatenate Out.Append("Strings from C# Component
"); CompCS.StringComponent myCSStringComp = new CompCS.StringComponent(); for (int index = 0; index < myCSStringComp.Count; index++){ Out.Append(myCSStringComp.GetString(index)); Out.Append("
"); } Out.Append("
"); // Iterate over component's strings and concatenate Out.Append("Strings from VB Component
"); CompVB.StringComponent myVBStringComp = new CompVB.StringComponent(); for (int index = 0; index < myVBStringComp.Count; index++){ Out.Append(myVBStringComp.GetString(index)); Out.Append("
"); } Message.InnerHtml = Out.ToString(); } <span id="Message" runat=server/>
Module 3: Working with Components
35
Demonstration: Testing the ASP.NET Client Topic Objective
To demonstrate how to test the ASP.NET page.
Lead-in
In this demonstration, you will see how to test the ASP.NET page.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** For Your Information
For instructions on running this demonstration, see the Instructor Notes. This exact procedure is repeated in Lab 3.3, “Calling a Component Through an ASP.NET Page.”
This demonstration shows how to test the ASP.NET page that was created in Creating an ASP.NET Client in this module. Before testing the page, you must first configure a virtual directory that points to the directory that contains the .aspx file. You can use the New Directory Wizard in the IIS snap-in to do this.
36
Module 3: Working with Components
Lab 3.3: Calling a Component Through an ASP.NET Page Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn how to create an ASP.NET page that uses a previously developed .NET Framework component to create an ASP.NET application.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to create an ASP.NET page that uses a previously developed .NET Framework component to create an ASP.NET application.
Lab Setup Starter and solution files are associated with this lab. The starter files are in the folder \Labs\Lab03.3\Starter, and the solution files are in the folder \Labs\Lab03.3\Solution.
Scenario The labs associated with Module 3 are based on a small client/server application scenario in which a .NET Framework component may be used in several different client scenarios. The component may be written in any .NET Framework runtime-compatible language, but you will only create a C# component. Lab 3.2, “Creating a Simple Console-Based Client,” and Lab 3.3, “Calling a Component Through an ASP.NET Page,” demonstrate how the same component code runs unchanged in different client environments. This lab focuses on using an ASP.NET page to call the component and display the results in a Web browser.
Estimated time to complete this lab: 30 minutes
Module 3: Working with Components
Exercise 1 Writing the ASP.NET Page In this exercise, you will create an ASP.NET page that uses the same StringComponent component that was used in the stand-alone client application that you created in Lab 3.2, “Creating a Simple Console-Based Client.” You will build a string object that holds the results of processing the string array in the StringComponent class and generates output that can be displayed in a Web browser.
! Create an ASP.NET page 1. Open Notepad and enter an @ Page directive that specifies the following page attributes: a. Language = C# b. Description = “ASP.NET Component Test” 2. Import the following namespaces into the page: a. CompCS b. CompVB c. System.Text 3. Create HTML script that specifies C# as the script language and uses the runat=server directive.
! Call the StringComponent class 1. Create a handler for the Page_Load event and initialize a local variable called Out of type StringBuilder to represent the string array in the StringComponent class. StringBuilder Out = new StringBuilder("");
2. Declare a local variable called Count of type int to obtain the number of string elements in the string array. 3. Call the Append method on the Out variable with the following string that has as it final characters an HTML line break, "
": "Strings from C# Component
" 4. Assign to a local variable called myCSStringComp a new instance of the C# version of the StringComponent class. 5. Iterate over all the strings of the C# string component. In each iteration, append the component’s string to the string variable named Out and then append an HTML line break, "
". 6. Append the string "
Strings from the VB Component
" to the Out string and repeat steps 4 and 5 for the Visual Basic version of the StringComponent class. The local variable name is myVBStringComp. 7. Assign the result of processing the string array to an HTML server control so that the output is dynamically generated on a Web page. Use the ToString method to get the output string from the Out variable. 8. Save the file as ClientASP.NET.aspx in the \Labs\ Lab03.3\Starter\ASP.NET_Client folder.
37
38
Module 3: Working with Components
Exercise 2 Building and Testing the ASP.NET Page In this exercise, you will configure a virtual directory to point to a directory where the ASP.NET page is located, so that you can test the ASP.NET application that is a client of the previously created string component.
! Configure a virtual directory 1. Click Start, Control Panel, Performance and Maintenance, Administrative Tools, and then click Internet Information Services. 2. Expand the computer icon, expand the Web Sites folder, and then expand and select Default Web Site. 3. Click the Action menu, point to New, and then click Virtual Directory. The Virtual Directory Creation Wizard starts. 4. Click Next to continue. 5. In the Alias text box, type Test and click Next. 6. Browse to the \Labs\Lab03.3\Starter\ASP.NET_Client folder that contains the file ClientASP.NET.aspx that you created in Exercise 1, click OK, and then click Next. 7. On the Access Permissions page, accept the default selections, click Next, and then click Finish. The Test folder is added to the Default Web Site.
Module 3: Working with Components
39
! Test the ASP.NET Client 1. Ensure that the compiled component DLLs, CompCS.dll and CompVB.dll, are in a \Bin subdirectory under the starting point for the application virtual directory. 2. Open Microsoft Internet Explorer, in the Address bar, type http://localhost/Test/ClientASP.NET.aspx and then press ENTER. A dialog box that generates the correct string output should appear, as shown in the following illustration:
40
Module 3: Working with Components
Review Topic Objective
To reinforce module objectives by reviewing key points.
!
An Introduction to Key .NET Framework Development Technologies
!
Creating a Simple .NET Framework Component
!
Creating a Simple Console Client
!
Creating an ASP.NET Client
Lead-in
The review questions cover some of the key concepts taught in the module.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. How do .NET methods indicate failure? All .NET Framework methods indicate failure by throwing exceptions.
2. What namespace contains the classes to create a Windows Form application? The Windows Forms library is located in the System.Windows.Forms namespace.
3. What is the default extension for ASP.NET pages? By default, ASP.NET files have an .aspx extension or .ascx for user controls.
Module 4: Deployment and Versioning Contents Overview
1
Introduction to Application Deployment
2
Application Deployment Scenarios
7
Related Topics and Tools
31
Lab 4: Packaging and Deployment
37
Review
42
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 4: Deployment and Versioning
iii
Instructor Notes Presentation: 90 Minutes Lab: 50 Minutes
After completing this module, students will be able to: !
Package and deploy simple and componentized applications.
!
Create strong-named assemblies.
!
Install and remove assemblies from the global assembly cache.
!
Configure an application to control its binding to an assembly based on the assembly’s location and version data.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft® PowerPoint® file 2349B_04.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Complete the lab.
iv
Module 4: Deployment and Versioning
Module Strategy Use the following strategy to present this module: !
Introduction to Application Deployment Introduce common concepts, such as the Microsoft .NET Framework hierarchy of namespaces, the organization of assemblies, and the role of the assembly manifest. Describe simple and componentized applications, and their configuration and distribution scenarios.
!
Application Deployment Scenarios The examples in this section show how to deploy a simple stand-alone application, an application that uses a shared assembly, and an application that makes use of assembly versioning. Introduce compapp, a componentized application, which uses multiple assemblies. Contrast compapp with the simple single assembly Hello World application that was created in Module 2, “Introduction to a Managed Execution Environment,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET).
!
Related Topics and Tools Briefly introduce additional topics that are related to deployment and versioning but are beyond the scope of this course. This section also provides a list of tools that you can use to work with assemblies. You should just inform the students of these topics and encourage them to look for more information in the .NET Framework Software Development Kit (SDK) documentation. In addition, Course 2350A, Securing and Deploying Microsoft .NET Assemblies (Prerelease), covers code access security and role-based security in greater detail.
Module 4: Deployment and Versioning
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn about packaging and deployment of Microsoft .NET Framework applications and assemblies.
!
Introduction to Application Deployment
!
Application Deployment Scenarios
!
Related Topics and Tools
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This module introduces the concepts of packaging and deployment of Microsoft® .NET Framework applications and assemblies. The .NET Framework provides improved isolation of application assemblies, simplified application deployment, and robust versioning. This module walks you through the packaging and deployment of a simple Hello World application, and a small, componentized application. These applications are written in C#, the new language designed for the .NET Framework. The steps that are necessary to construct, compile, and run C# applications were explained in Module 2, “Introduction to a Managed Execution Environment,” and Module 3, “Working with Components,” both in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET). Because this course is an introduction to programming in the .NET Framework, you should spend some time reading the .NET Framework Software Development Kit (SDK) documentation. In fact, the labs, demonstrations, and material for this module and other modules in this course are based on several tutorials in the .NET Framework SDK. After completing this module, you will be able to: !
Package and deploy simple and componentized applications.
!
Create strong-named assemblies.
!
Install and remove assemblies from the global assembly cache.
!
Configure an application to control its binding to an assembly based on the assembly’s location and version data.
2
Module 4: Deployment and Versioning
" Introduction to Application Deployment Topic Objective
To introduce issues that arise in application deployment and to introduce the topics in the section.
Lead-in
You can deploy a .NET Framework application in several ways.
!
Common Concepts
!
Simple Applications
!
Componentized Applications
!
Configuration and Distribution
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can deploy a .NET Framework application in several ways depending on the following considerations: !
The complexity of the application
!
The sharing of assemblies with other applications
!
The application’s security and protection requirements
!
The application’s method of distribution
An application’s deployment is not affected by the .NET Framework common language runtime-compatible language that is used to develop the application. All applications that are written for use with the .NET Framework are compiled to the same self-describing, Microsoft intermediate language (MSIL) code and run with he same .NET Framework runtime. This section introduces common concepts, such as the .NET Framework hierarchy of namespaces, the organization of assemblies, and the role of the assembly manifest. It then describes simple and componentized applications, and their configuration and distribution.
Module 4: Deployment and Versioning
3
Common Concepts Topic Objective
To review the organization of the .NET Framework class library and assemblies, and the role of the assembly manifest.
!
Lead-in
The .NET Framework provides a common class library that is organized into a hierarchical tree of namespaces.
!
Classes and Types Used in .NET Framework Applications Are: #
Organized in a hierarchy of namespaces
#
Stored in PE files, such as DLLs and EXEs
#
Fully described by metadata
Assemblies: #
Are made up of one or more PE files
#
Contain a manifest that identifies the assembly and its files
#
Specify exported and imported classes and types
#
Are units of deployment, reuse, and versioning
*****************************ILLEGAL FOR NON-TRAINER USE****************************** As you learned in Module 2, “Introduction to a Managed Execution Environment,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET), in addition to the common language runtime, the .NET Framework provides a common class library that is organized into a hierarchical tree of namespaces. At the root of this hierarchy is the System namespace, which contains objects for many other useful classes that can be used from any .NET-compatible language. These objects include objects that are used for file I/O, messaging, networking, and security. For Your Information
This topic reviews information that was covered in Module 2. Do not spend much time on this topic. Present the material as the starting point for a discussion of deployment and versioning.
The Organization of PE Files into Assemblies The .NET Framework class library that you and others create are also organized into hierarchical namespaces and stored in portable executable (PE) files, most typically DLLs and EXEs. A single PE file can contain several namespaces, including nested namespaces. You also can split a namespace across multiple PE files. One or more PE files, and possibly non-PE files, such as resources, are combined to create an assembly, which is a physical unit that can be deployed, versioned, and reused.
The Role of the Assembly Manifest In the .NET Framework, each class type is fully described through the type’s metadata. Each assembly contains a manifest that includes the name of each type that is exported from the assembly, along with information about the file that contains that type’s metadata. The manifest also includes information about the identity of the assembly, such as name, files that make up the assembly, and version information, and full information about any dependencies on other assemblies. The .NET Framework runtime uses assembly manifests to locate and bind to the referenced types.
4
Module 4: Deployment and Versioning
Simple Applications Topic Objective
To describe how easily an application can be executed in the .NET Framework.
Lead-in
In the simplest case, a .NET Framework application can be executed locally on any computer on which the .NET runtime is already installed.
!
Require .NET Runtime Be Installed on Local Computer
!
Can Be Run Directly from a File Server or Copied Locally
!
Make No Registry Entries
!
Cannot Break Another Application #
!
Eliminate DLL Hell
Can Be Uninstalled by Deleting Locally Copied File(s)
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In the simplest case, a .NET Framework application can be executed locally on any computer on which the .NET runtime is already installed. The program can run directly from a file server, or the files can be copied locally. Nothing else is required. No registry entries are made, and no other applications are broken or caused to stop running as a result. The fact that no registry entries are made eliminates DLL versioning issues, commonly referred to as “DLL hell.” Just deleting the executable file, if it was copied locally, is sufficient to remove the application and leave no trace on the computer.
Module 4: Deployment and Versioning
5
Componentized Applications Topic Objective
To describe how assemblies are handled in componentized applications.
!
Lead-in
Assemblies Private to an Application #
Componentized applications are only slightly more complex than applications.
!
Assemblies Private to Related Applications #
!
Same as a simple application Deployed into a common subdirectory
Assemblies Shared with Other Unrelated Applications #
Require a strong name and version information
#
Deployed into the global assembly cache
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Componentized applications are only slightly more complex than simple applications. The complexity of componentized applications depends on whether their components are: !
Contained in assemblies that are private to the application.
!
Shared with other related applications.
!
Shared with other potentially unknown applications.
If all of the component assemblies are private, the componentized application can be treated in the same manner as the application. The application can run from a file server, or the application files can be copied to a local volume. Deleting all of a componentized application’s files is sufficient to remove the program. Likewise, if several related applications use the same component assemblies, those assemblies can be located in a common subdirectory. However, if the application uses assemblies that are shared with other unrelated applications, these assemblies can be installed in the global assembly cache and have certain properties, such as a unique strong name that includes version information, that enable the .NET runtime to ensure that the application binds to the appropriate versions.
6
Module 4: Deployment and Versioning
Configuration and Distribution Topic Objective
To introduce options for configuring and deploying applications in the .NET Framework.
Lead-in
As a developer of .NET Framework applications, you should be aware of the different ways in which applications may be configured and packaged for distribution.
!
Configuration #
!
Maintained in plain-text files
Deployment #
Common distribution formats, such as a .CAB file or a .MSI file
#
Common distribution mechanisms, such as Windows 2000 IntelliMirror or Microsoft Systems Management Server
*****************************ILLEGAL FOR NON-TRAINER USE****************************** As a developer of .NET Framework applications, you should be aware of the different ways in which applications may be configured and packaged for distribution. Usually, individual organizations, or an administrator within an organization, decide how an application is packaged for distribution and configured for the organization.
Configuration In the .NET Framework, you can maintain application configuration in plain text files. This use of plain text XML-based application configuration files allows administrators to tailor an application’s behavior on a particular computer without having to involve developers in the process. The following section, Application Deployment Scenarios, presents several common application scenarios. While this module does not cover ASP.NET deployment, most of the concepts that are presented in this module apply to ASP.NET deployment.
Deployment Many client applications may be further packaged in a common distribution format, such as a .CAB file or .MSI file. Client applications may also be installed by using application distribution mechanisms, such as Microsoft Windows® 2000 IntelliMirror® or Microsoft Systems Management Server (SMS), which both use the Microsoft Windows Installer technology. The Microsoft Windows Installer is an installation and configuration service that is included in the Windows 2000 operating system. It is provided in a service pack to Microsoft Windows 95, Microsoft Windows 98, and Microsoft Windows NT® version 4.0. For more information about the Microsoft Windows Installer, see the Platform SDK documentation.
Module 4: Deployment and Versioning
7
" Application Deployment Scenarios Topic Objective
To introduce the topics in the section.
!
A Simple Application
Lead-in
!
A Componentized Application
!
Specifying a Path for Private Assemblies
!
A Strong-Named Assembly
!
Deploying Shared Components
!
A Versioned Assembly
!
Creating Multiple Versions of a Strong-Named Assembly
!
Binding Policy
!
Deploying Multiple Versions of a Strong-Named Assembly
This section shows how to deploy a variety of applications, from simple applications to applications that make use of assembly versioning.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The examples in this section show how to deploy a simple application, an application that uses a shared assembly, and an application that makes use of assembly versioning.
8
Module 4: Deployment and Versioning
A Simple Application Topic Objective
To explain how a simple application is deployed.
!
Lead-in
In Module 2, you looked at the simplest .NET Framework application, the traditional Hello World application written in C#. !
Use the Microsoft Intermediate Language Disassembler (Ildasm.exe) to Examine the Assembly Manifest #
Version information
#
Imported types
#
Exported types
Deploy the Application by: #
Running the executable file directly from a file server, or
#
Installing it locally by copying the file
#
Uninstall the application by deleting the file
*****************************ILLEGAL FOR NON-TRAINER USE****************************** For Your Information
Point out that while Hello World is simplistic, it is extremely versatile in its ability to easily demonstrate many new concepts of the .NET Framework, including deployment and versioning.
In Module 2, “Introduction to a Managed Execution Environment,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET), you looked at the simplest .NET Framework application, the traditional Hello World application written in C#. The C# source code for that program, HelloDemoCS, is as follows: using System; class MainApp { public static void Main() { Console.WriteLine("Hello World using C#!"); } }
This simple executable file prints a single line to the System.Console, a type that is contained in the .NET Framework class library. It does not reference any other libraries and does not itself produce a library.
Module 4: Deployment and Versioning
Using Microsoft Intermediate Language Disassembler to Examine Hello World Compiling this small application generates the HelloDemoCS.exe. Important To use Microsoft Visual Studio® .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt. If you run the Microsoft intermediate language (MSIL) disassembler (Ildasm.exe) against this executable file, a window appears, similar to the following illustration.
The simple Hello World application highlights a particularly important concept behind programming for the .NET Framework. The application is clearly selfdescribing: all of the information that is needed to understand the application is contained in the metadata. The MSIL disassembler shows the classes or types that are created within the application. In the case of the Hello World application, the only class is MainApp. The MSIL disassembler also shows the methods Main and a default constructor, indicated by .ctor. The program does not have any other members. To save information about the assembly to a file, use the Dump command on the File menu.
9
10
Module 4: Deployment and Versioning
To see additional information about the application, double-click Manifest. The following window appears.
The preceding illustration shows the manifest, which contains information about the assembly, including the version (not yet set), external libraries, and types within those libraries, which the application uses.
Deployment Deployment to computers on which the .NET runtime is installed is a simple process. The Hello World application can run directly from a file server. More advanced programs may involve security issues. In the simple Hello World case, no files are placed on the workstation, no entries are made in the system registry, and, in effect, there is no affect on the client workstation. There is also nothing to clean up because there is nothing to remove on the client workstation. As you would expect, HelloDemoCS.exe can also be copied to a local volume. In this scenario, you can remove the program by deleting the file, and as before, nothing would remain on the workstation. Whether you run HelloDemoCS.exe from a file server or copy it to a local volume, running this application will not break another program, and no other application can cause HelloDemoCS.exe to stop functioning.
Module 4: Deployment and Versioning
11
A Componentized Application Topic Objective
To introduce compapp, a componentized application, which uses multiple assemblies.
Lead-in
The Hello World application that is discussed in the preceding topic is completely trivial and hardly representative of even the simplest real-world application.
!
Assembly Component to Be Used by Application #
Assembly Stringer.dll is built from Stringer.cs as follows:
csc csc /target:library /target:library Stringer.cs Stringer.cs !
Client Needs to Reference Assembly
csc csc /reference:Stringer.dll /reference:Stringer.dll Client.cs Client.cs !
Deployment by File Server or Local Copy
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The Hello World application that is discussed in the preceding topic is completely trivial and hardly representative of even the simplest real-world application. This topic covers compapp, a componentized application, which uses multiple assemblies.
12
Module 4: Deployment and Versioning
Creating Stringer.dll The client, Client.exe, calls types that are contained in a component assembly that is named Stringer (Stringer.dll). The source code for the Stringer assembly, is located in Stringer.cs: using System; namespace org { public class Stringer { private string[] StringSet; public Stringer() { StringSet = new string[] { "C# String 0", "C# String 1", "C# String 2", "C# String 3" }; } public string GetString(int index) { if ((index < 0) || (index >= StringSet.Length)) { throw new IndexOutOfRangeException(); } return StringSet[index]; } public int Count { get { return StringSet.Length; } } } }
Module 4: Deployment and Versioning
13
A client application that uses this component could fully qualify a reference to the Stringer class, for instance, as org.Stringer. Alternatively, a client application could include a using statement to allow easy access to the types in Stringer.dll by specifying the namespace, as shown in the Client.cs source code: using System; using org; class MainApp { public static void Main() { Stringer myStringComp = new Stringer(); string[] StringsSet = new string[4]; // Iterate over component's strings Console.WriteLine("Strings from StringComponent"); for (int index = 0; index < myStringComp.Count; index++) { StringsSet[index] = myStringComp.GetString(index); Console.WriteLine(StringsSet[index]); } // ... } }
Building the Application To build this componentized application, you first build the Stringer.dll assembly component. Then, you build Client.exe, importing the Stringer component by using the name of the file that contains the manifest, Stringer.dll, rather than the namespace name, which is org in this case. csc /target:library Stringer.cs csc /reference:Stringer.dll Client.cs
The MSIL disassembler displays the Stringer.dll and shows all of the members, as in the following illustration.
14
Module 4: Deployment and Versioning
Like HelloDemoCS.exe, Client.exe contains manifest information about itself, the System library, and the types that the application uses. However, the manifest now contains information about the Stringer assembly. A MSIL disassembler display of the manifest for Client.exe shows the reference to the Stringer assembly, as in the following illustration.
Deployment Like the simple application, Client.exe can run directly from a file server on any workstation that has the .NET runtime installed. Client.exe and Stringer.dll can also be copied to a local volume. To remove the application, you need only delete the two files.
Module 4: Deployment and Versioning
15
Specifying a Path for Private Assemblies Topic Objective
To show how to use a configuration file to specify a path for private assemblies.
Lead-in
The .NET Framework provides a configuration mechanism that allows administrators to specify a directory from which to load private assemblies.
!
Specifying a Directory From Which to Load Private Assemblies. #
Client.exe.config file specifies a privatePath tag
<probing <probing privatePath=“MyStringer"/> privatePath=“MyStringer"/> !
Configuration File’s XML Tags Are Case-Sensitive
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The preceding Client.exe example has one important weakness: both Client.exe and Stringer.dll reside in the same directory. In the real world, an administrator may wish to use a directory structure to manage assemblies. The .NET Framework provides a configuration mechanism that allows administrators to specify a directory from which to load private assemblies.
Locating Stringer.dll and Client.exe in Separate Directories Using the preceding Client example, all of the source code is the same, but for illustration purposes the build process has been modified so that the component application’s Stringer.dll is built in its own subdirectory named MyStringer. cd \compapp csc /target:library /out:MyStringer\Stringer.dll! MyStringer\Stringer.cs csc /reference:MyStringer\Stringer.dll Client.cs
Using a Configuration File to Locate Assemblies at Run Time Although the /reference: compile option locates an assembly in a directory when compiling the program, a separate XML-based application configuration file is required at run time to support assemblies that are located in other directories. For client executable files like the examples that are covered in this module, the configuration file resides in the same directory as the executable file. The configuration file has the complete name of the executable file with an additional extension of .config.
16
Module 4: Deployment and Versioning
The Client.exe configuration file is called Client.exe.config. It specifies a privatePath attribute, as shown in the following example: <probing privatePath="MyStringer"/>
Important The configuration file’s XML tags are case-sensitive. When this configuration file is placed in the same directory as the executable file, at run time, the .NET Framework uses the privatePath attribute to determine where to look for components—in addition to looking in the application directory. Note When loading an assembly, the runtime also searches for a private path that is equal to the name of the assembly. If the Stringer.dll assembly was located in a subdirectory named Stringer, instead of MyStringer, then a configuration file would be unnecessary.
Deployment Like the simple application, the revised Client.exe can run directly from a file server on any workstation that has the .NET runtime installed. Client.exe, Stringer.dll, and the application’s .config file can also be copied to a local volume, using the same relative directory structure. Deleting the files and directory effectively removes the application. While they are not used in the preceding example, it is important to know that in addition to application configuration files, the .NET Framework also supports separate user and Computer Configuration Files for many common configuration settings.
Module 4: Deployment and Versioning
17
A Strong-Named Assembly Topic Objective
To discuss the problems that may occur when multiple applications share components, and to introduce the use of strong names.
Lead-in
The client applications discussed in the preceding topics are limited in that they only illustrate the use of assemblies that are private to the Client.exe and do not demonstrate the use of assemblies that are shared by more than one application.
!
Global Assembly Cache #
!
Contains assemblies shared by unrelated applications
Strong Names Are Required for Assemblies in the Cache #
Generate a public-private key pair:
sn sn –k –k orgKey.snk orgKey.snk #
Add code to source file to specify version and key information:
#if #if STRONG STRONG [assembly: [assembly: System.Reflection.AssemblyVersion("1.0.0.0")] System.Reflection.AssemblyVersion("1.0.0.0")] [assembly: [assembly: System.Reflection.AssemblyKeyFile("orgKey.snk")] System.Reflection.AssemblyKeyFile("orgKey.snk")] #endif #endif #
Compile:
csc csc /define:STRONG /define:STRONG /target:library /target:library /out:AReverser.dll! /out:AReverser.dll! AReverser.cs AReverser.cs
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The client applications discussed in the preceding topics show the basics for constructing a complex program, but they are limited in that they only illustrate the use of assemblies that are private to the Client.exe and do not demonstrate the use of assemblies that are shared by more than one application. This topic discusses the problems that may occur when multiple applications share components. It then introduces the use of strong names, the .NET Framework solution to these problems.
Issues with Sharing Components Many applications use assemblies that are shared by more than one application. These shared assemblies, which are typically provided by third-party developers, are installed in a common location on the system, the global assembly cache. By default, the system looks for each application’s assemblies in the global assembly cache. In classic COM and COM+ applications, the sharing mechanism depends heavily on the Windows System Registry in which information about each component, including its version and physical file location, is stored. This mechanism allows multiple applications to share a single component, but it also allows a component of a newly installed application to replace an existing component and possibly cause other applications to break. Such overwriting of existing components is often difficult to detect because the application that is causing the problem appears to work properly.
18
Module 4: Deployment and Versioning
Strong Names In the .NET Framework, you can solve the problems that are caused by the sharing of components with multiple applications by more strongly associating a distinct build of a component assembly with the client application. A distinct build is indicated by a combination of a version number and a special value that is called the publicKeyToken. Important Strong names provide a strong integrity check. Passing the .NET Framework security checks guarantees that the contents of the assembly have not been changed since it was built. Note, however, that strong names in and of themselves do not imply a level of trust, such as that provided by a digital signature and supporting certificate. When component assemblies are associated with a distinct build, the system can isolate these component assemblies, thus allowing different versions to run at the same time for different client applications. This system of protection is sometimes called side-by-side execution. It differs from backward compatibility because with side-by-side execution, applications can run alongside other versions of the same applications without affecting their respective execution environments. You can facilitate side-by-side execution by assigning strong names to your assemblies. A strong name consists of the assembly’s identity, which includes its simple text name, version number, and culture information, if provided, and a public key. To demonstrate these additional build attributes the client and component scenario is extended by adding an assembly named AReverser. This assembly contains a class with a method that is named Invert, which uses the System.Array.Reverse method to reverse an array of strings. Note The code details of AReverser are not relevant to deployment issues and therefore are not shown. This module’s lab contains the complete code details.
Building the Assembly Without a Strong Name You begin by building the new component assembly without specifying any options to make it have a strong name. First, you compile the new AReverser assembly, as follows: csc /target:library /out:AReverser.dll AReverser.cs
After compiling the new AReverser assembly, you can examine the metadata by using the MSIL disassembler. The MSIL disassembler indicates that the assembly does not have an established version number, as shown in the following code: .assembly AReverser { ... .hash algorithm 0x00008004 .ver 0:0:0:0 } .module AReverser.dll
Module 4: Deployment and Versioning
19
Generating a Public-Private Key Pair To create an assembly with a strong name, you must compile the assembly by using a private key. Public keys are used for verification. Therefore, before compiling the assembly, you must first generate a public-private key pair. You use the Strong Name tool (Sn.exe) to generate a new key pair and place them in a file, as in the following example: sn –k orgKey.snk
Compiling a Strong-Named Assembly Now that you have a private key, you are ready to compile the component, specifying the key file and the version number to be assigned. You can do this by specifying AssemblyVersion and AssemblyKeyFile attributes in the AReverser.cs file using the STRONG conditional compilation symbol: #if STRONG [assembly: System.Reflection.AssemblyVersion("1.0.0.0")] [assembly: System.Reflection.AssemblyKeyFile("orgKey.snk")] #endif
You must then define STRONG in the compile process: csc /define:STRONG /target:library /out:AReverser.dll! AReverser.cs
If you run the MSIL disassembler again on AReverser.dll, you can verify that the assembly is now strong-named from the presence of a .publickey property and a non-default version, which is specified by the .ver property of 1:0:0:0. The following example shows the disassembled code: .assembly AReverser { ... .publickey = (00 ... 71 8A 7D 6A D7 ) .hash algorithm 0x00008004 .ver 1:0:0:0 } .module AReverser.dll
For more information about the Strong Name tool, see Packaging and Deployment Tools in this module.
20
Module 4: Deployment and Versioning
Deploying Shared Components Topic Objective
To explain the commands that are used to install, examine, and remove assemblies from the global assembly cache.
Lead-in
While components can easily be shared by related applications by putting them in a common subdirectory, shared assemblies that are used by many applications on the system are often stored in the system’s global assembly cache.
!
Installing the Strong-Named Component in the Global Assembly Cache gacutil gacutil /i /i AReverser.dll AReverser.dll
!
Examining the Global Assembly Cache gacutil gacutil /l /l
!
Removing a Shared Component File gacutil gacutil /u /u AReverser AReverser
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Deployment with strong-named assemblies is usually more complicated than deployment with assemblies that are not strong-named. While components can easily be shared by related applications by putting them in a common subdirectory, shared assemblies that are used by many applications on the system are often stored in the system’s global assembly cache. As with the previous examples of componentized applications, this revised Client.exe can run directly from a file server on any workstation on which the .NET runtime is installed. The Client.exe and Stringer.dll files can also be copied to a local volume.
Installing a Strong-Named Assembly into the Global Assembly Cache Installing the strong-named component into the global assembly cache requires the following additional command on the computer that will be running the corresponding Client.exe program: gacutil /i AReverser.dll
Note You must have Administrator privileges on a computer to install assemblies into the global assembly cache. Assemblies that are installed in the global assembly cache must have a strong name. After installing the AReverser assembly, you can then examine the system assembly cache by typing: gacutil /l
Module 4: Deployment and Versioning
21
The output of this command is: ... AReverser, Version=1.0.0.0, Culture=neutral, PublicKeyToken=! 0588613cb04b772e, Custom=null ...
You can also examine the system assembly cache by navigating to the \WindowsDirectory\Assembly directory and by using the cache shell extension.
Uninstalling a Strong-Named Assembly from the System Cache Cleaning up applications that use strong-named assemblies requires more work than cleaning up the simple applications or componentized applications that are described in the preceding topics. In addition to deleting the executable files, you should remove the shared component file from the global assembly cache since the global assembly cache is not automatically scavenged. To remove a shared component file, an administrator can use the cache shell extension, select the appropriate components, and delete them. Developers and administrators who want to automate the process, however, should use the command-line interface to the assembly cache manager as follows: gacutil /u AReverser
To confirm the removal, you can view the contents of the global assembly cache by using the following command: gacutil /l
For more information about installing and uninstalling shared assemblies to the global assembly cache, see Packaging and Deployment Tools in this module.
22
Module 4: Deployment and Versioning
A Versioned Assembly Topic Objective
To discuss the physical representation and logical mapping of the compatibility version number.
!
Applications Need to Bind to a Suitable Version of a Shared Assembly
Lead-in
!
The Version Number Is Represented by a 4-Part Number
The final packaging and deployment example in this module involves updating the shared assembly to a new version.
<major <major version>.<minor version>.<minor version>.... number> !
Applications Get the Versions of Assemblies with Which They Were Built and Tested #
Unless overridden by explicit policy rules
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The final packaging and deployment example in this module involves updating the shared assembly to a new version. In Creating Multiple Versions of a Strong-Named Assembly in this module, the shared assembly is updated to deliberately break compatibility with the client and to demonstrate how the .NET Framework allows you to configure the client application to bind to the desired version of the shared assembly.
Versioning Each assembly has a specific compatibility version number as part of its identity. As such, two assemblies that differ by compatibility version are completely different assemblies as far as the .NET runtime class loader is concerned. This compatibility version number is physically represented as a four-part number with the following format: <major version>.<minor version>..
Module 4: Deployment and Versioning
23
Each segment of this number has a specific meaning to the .NET runtime as it decides which version of an assembly to load. Logically, the compatibility version number has three parts, with the following meanings: 1. Incompatible A change has been made to the assembly that is known to be incompatible with previous versions. Example: Major new release of the product 2. Maybe Compatible A change has been made to the assembly that is thought to be compatible and carries less risk than an incompatible change. However, backward compatibility is not guaranteed. Example: Service Pack or release of a new daily build 3. Quick Fix Engineering (QFE) An engineering fix that customers may want to upgrade to. Example: An emergency security fix These three logical parts correspond to the physical four-part version number as follows:
For example, an assembly with compatibility version number 2.0.0.0 is considered incompatible with an assembly whose compatibility number is 1.0.0.0. Compatibility number 2.0.2.11 is considered a QFE to compatibility number 2.0.2.1.
24
Module 4: Deployment and Versioning
Creating Multiple Versions of a Strong-Named Assembly Topic Objective
To explain how to create multiple versions of a shared component by using strong-named assemblies.
Lead-in
In the .NET Framework, you can solve the problems that are inherent in working with multiple versions of a shared component by using strong-named assemblies.
!
Building Two New Versions of AReverser with a New Key Pair #
Specify version 2.0.0.0 for one and 2.0.1.0 for the other For AReverser_v2.0.0.0\AReverser.cs:
#if #if STRONG STRONG [assembly: [assembly: System.Reflection.AssemblyVersion("2.0.0.0")] System.Reflection.AssemblyVersion("2.0.0.0")] [assembly: [assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")] System.Reflection.AssemblyKeyFile("orgVerKey.snk")] #endif #endif !
Build
csc csc /define:STRONG /define:STRONG /target:library! /target:library! /out:AReverser_v2.0.0.0\AReverser.dll! /out:AReverser_v2.0.0.0\AReverser.dll! AReverser_v2.0.0.0\AReverser.cs AReverser_v2.0.0.0\AReverser.cs !
Use Ildasm.exe to Examine Different Versions #
Note the publickey and version numbers
.assembly .assembly AReverser AReverser {{ ... ... .publickey .publickey == (00 (00 24 24 ... ... 82 82 B1 B1 F2 F2 A0 A0 )) .hash .hash algorithm algorithm 0x00008004 0x00008004 .ver .ver 2:0:1:0 2:0:1:0
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In the .NET Framework, you can solve the problems that are inherent in working with multiple versions of a shared component by using strong-named assemblies. This topic examines how multiple versions of an assembly are created. Building on the code in the preceding strong-named AReverser assembly, two separate versions of the strong-named component are created, and additional application configuration options are used to show how an application can be made to run. A method in version 2.0.1.0 of AReverser.dll is deliberately made incompatible with the same method in version 2.0.0.0 so that a client that successfully calls the method that is using version 2.0.0.0 will fail with the later revision. Versioning keys can change from one version of an assembly to the next. To illustrate this, you generate a new key pair using the Strong Name (Sn.exe) tool and place that new key pair in a file, as follows: sn –k orgVerKey.snk
After creating a new private key, you add the following to the AReverser.cs file in the AReverser_v2.0.0.0 subdirectory: #if STRONG [assembly: System.Reflection.AssemblyVersion("2.0.0.0")] [assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")] #endif
Module 4: Deployment and Versioning
25
You add the following to the AReverser.cs file in the AReverser_v2.0.1.0 subdirectory: #if STRONG [assembly: System.Reflection.AssemblyVersion("2.0.1.0")] [assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")] #endif
You compile both of these versions by using the commands: csc /define:STRONG /target:library! /out:AReverser_v2.0.0.0\AReverser.dll! AReverser_v2.0.0.0\AReverser.cs csc /define:STRONG /target:library! /out:AReverser_v2.0.1.0\AReverser.dll! AReverser_v2.0.1.0\AReverser.cs
If you run the MSIL disassembler on these two updated files, you can verify that the assemblies are strong-named as indicated by the version number, 2.0.0.0 or 2.0.1.0, depending on which one you inspect with the MSIL disassembler. Notice that these two assemblies have the same publickey property, but the value of this property is different than the previous version 1.0.0.0 AReverser.dll. This property is different because a different key pair was used, orgVerKey.snk, instead of orgKey.snk. The following example shows the MSIL disassembler output for version 2.0.1.0 of the AReverser.dll: .assembly AReverser { ... .publickey = (00 24 ... 82 B1 F2 A0 ) .hash algorithm 0x00008004 .ver 2:0:1:0 }
26
Module 4: Deployment and Versioning
Binding Policy Topic Objective
!
To discuss binding policy.
#
Lead-in
Whenever the .NET Framework is asked to bind to a specific version of a shared assembly, the version of the assembly reference may be altered at several policy-resolution stages before the .NET Framework finally decides to which version to bind.
Policy Resolution
!
Allows an assembly reference, specified at compile time, to be modified after the application has been deployed without recompiling
Happens in the Following Stages: 1. Application-policy resolution 2. Publisher-policy resolution 3. Administrator-policy resolution
!
In Each Stage, an XML Configuration File Is Read #
! !
Note: XML is case-sensitive
Version Numbers of Assemblies That Are Not Strong-Named Are Not Checked Configuration File Tag Examples # privatePath # bindingRedirect
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When the .NET Framework is asked to bind to a specific version of a shared assembly, the version of the assembly reference may be altered at several policy-resolution stages before the .NET Framework finally decides which version to bind to. This policy-resolution process allows an assembly reference, which is specified at compile time, to be modified after the application has been deployed, without recompiling the assemblies involved. Policy resolution happens in the following three stages: 1. Application-policy resolution 2. Publisher-policy resolution 3. Administrator-policy resolution In each stage, an XML configuration file that describes the policy is read. The privatePath attribute in the application-configuration file illustrates the simplest form of application-policy resolution. In addition, the bindingRedirect tag can be used to redirect the reference to a different version of a shared assembly. Note The version numbers of assemblies that are not strong-named are not checked.
Module 4: Deployment and Versioning
27
Publisher-policy resolution allows shared-component vendors to make compatibility statements among different revisions of their software. These perassembly configuration files are distributed as strong-named assemblies and are installed into the global assembly cache as part of a service pack-style update. Any binding redirects that are specified by the publisher-policy file are then applied to the reference, which is then subject to administrator-policy resolution. Because publisher-policy assemblies affect all applications on the system, it is important that these assemblies are installed separately from the application. Administrator-policy resolution is the final—and the strongest—stage in the binding policy-resolution process. The administrator-policy file is located in the WindowsDirectory\Microsoft.NET\Framework\v1.0.FinalBuildNumber\ CONFIG directory and is called Machine.config. This file has the same XMLbased schema as the policy files that are used in the two previous stages of policy resolution. Administrator policy affects all assembly bindings that occur on the system and can never be bypassed.
28
Module 4: Deployment and Versioning
Deploying Multiple Versions of a Strong-Named Assembly Topic Objective
To explain how to deploy multiple versions of a strong-named assembly.
!
Lead-in
After you have configured both versions of the AReverser assembly to use strong names, you can deploy those versions.
Install Both Versions of AReverser.dll into the Global Assembly Cache gacutil gacutil /i /i AReverser_v2.0.0.0\AReverser.dll AReverser_v2.0.0.0\AReverser.dll gacutil /i gacutil /i AReverser_v2.0.1.0\AReverser.dll AReverser_v2.0.1.0\AReverser.dll
!
Compile the VerClient Executable and Specify Version 2.0.0.0 of the AReverser Component
csc csc /reference:MyStringer\Stringer.dll! /reference:MyStringer\Stringer.dll! /reference:AReverser_v2.0.0.0\AReverser.dll /reference:AReverser_v2.0.0.0\AReverser.dll VerClient.cs VerClient.cs !
Use Version Policies to Control Assembly Binding at Run Time
*****************************ILLEGAL FOR NON-TRAINER USE****************************** After you have configured both versions of the AReverser assembly to use strong names, you can deploy those versions. To configure client applications to use strong-named assemblies of a specific version, you must first install both 2.0 versions of AReverser.dll into the global assembly cache as follows: gacutil /i AReverser_v2.0.0.0\AReverser.dll gacutil /i AReverser_v2.0.1.0\AReverser.dll
After installing these AReverser assemblies, you can then examine the global assembly cache by using: gacutil /l
You can also examine the system assembly cache by navigating to the \WindowsDirectory\Assembly directory, and using the cache shell extension. You now compile the VerClient executable file, for which you specify the version 2.0.0.0 of the AReverser component, as shown in the following example: csc /reference:MyStringer\Stringer.dll! /reference:AReverser_v2.0.0.0\AReverser.dll VerClient.cs
Module 4: Deployment and Versioning
29
Version Policies All versioning of assemblies that use the common language runtime takes place at the assembly level. The specific version of an assembly and the versions of dependent assemblies are recorded in the assembly’s manifest. The rules that specify the acceptable versions of an assembly are called version policies. Version policies are expressed in configuration files.
Using Configuration Files to Override Version Policy According to the default version policy for the runtime, applications run only with the versions that they were built and tested with unless these versions were overridden by explicit version policy in configuration files. These configuration files, which include the application configuration file, a publisher policy file, and the machine’s administrator configuration file, provide the means for overriding the version policy that is recorded in the assembly manifest.
Binding to a Specific Assembly Version In a configuration file, you can specify a version policy that instructs the runtime to bind to a specific version of an assembly that you want the application to use. This specific version policy allows you to bind to a different version of an assembly than the version that is recorded in the assembly manifest of the calling assembly. In particular, the bindingRedirect tag can be used to redirect the reference to a different version of a shared assembly, by overriding the version in the original reference with this newer version. The following option says that for any assembly reference from version 2.0.0.0 through 2.0.0.9, the version that should instead be used at run time is 2.0.1.0:
This option allows an administrator to reconfigure an application without having to have it recompiled.
30
Module 4: Deployment and Versioning
The following sample VerClient.exe.config file instructs the runtime to look in subdirectory MyStringer for assembly references and to bind to version 2.0.0.0 of AReverser: <probing privatePath="MyStringer"/> <dependentAssembly>
Note The publicKeyToken tag’s value can be obtained using the Sn.exe tool or by examining the global assembly cache by using gacutil /l or the cache shell extension. Important The Sn.exe switches to obtain the publicKeyToken do not work correctly in some of the earlier .NET Framework versions, such as build 9188. Key Points
Reinforce the fact that the configuration file’s XML tags are case-sensitive.
Because a method of a type in version 2.0.1.0 of AReverser.dll was deliberately made incompatible with the same method in version 2.0.0.0, a version 2.0.0.0compatible client that attempted to call this later revision would fail. If VerClient.exe.config changed the line: newVersion="2.0.0.0"/>
to: newVersion="2.0.1.0"/>
The resulting error message would be similar to: Unhandled Exception: System.MissingMethodException:! Method not found: at MainApp.Main()
More typically, the configuration file mechanism allows an administrator to repair an application so that it will continue to run even if it is broken by a subsequent install of another application that used a different version of the same shared component. Finally, when you need to clean up the application, remove the shared component files from the global assembly cache, as shown in the following example: gacutil /u AReverser
Module 4: Deployment and Versioning
31
" Related Topics and Tools Topic Objective
To introduce the topics in the section.
Lead-in
This section briefly introduces additional topics that are related to deployment and versioning.
!
Related Topics
!
Packaging and Deployment Tools
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This section briefly introduces additional topics that are related to deployment and versioning, but are beyond the scope of this course. This section also provides a list of tools that you can use to work with assemblies.
32
Module 4: Deployment and Versioning
Related Topics Topic Objective
To briefly introduce several related topics and provide references for students to follow up on.
Lead-in
This module introduces the concepts of packaging and deployment of .NET Framework applications and assemblies. Several related topics are worth mentioning, and, when appropriate, references to additional information are provided.
!
ASP.NET
!
Assemblies
!
Security
!
Localization
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This module introduces the concepts of packaging and deployment of .NET Framework applications and assemblies. Several related topics are worth mentioning, and, when appropriate, references to additional information are provided. For Your Information
Do not spend much time on this topic. The subject matter is mostly beyond the scope of the course. You should just inform the students of these topics and encourage them to look for more information in the provided references.
ASP.NET This module focuses on packaging and deploying traditional client applications. For more information about deploying ASP.NET applications to a Web server, see the .NET Framework SDK documentation.
Assemblies For more information about how the .NET Framework locates assemblies when they are referenced at run time, see “How the Runtime Locates Assemblies” in the .NET Framework SDK documentation. This topic covers the Assembly Resolver, strong-named assemblies, application and administrator version policies, codebase locations, and QFE updates.
Security The.NET Framework offers code access security and role-based security to address mobile code security concerns and to provide support that enables components to determine what users are authorized to do. These security mechanisms use a simple, consistent model so that developers who are familiar with code access security can easily use role-based security and developers who are familiar with role-based security can easily use code access security. Code access security and role-based security are implemented by using a common infrastructure that is supplied by the common language runtime.
Module 4: Deployment and Versioning
33
Code Access Security and Role-based Security Code access security uses permissions to control the access that code has to protected resources and operations. It helps to protect computer systems from malicious mobile code and provides a way to allow mobile code to run safely. Role-based security provides information that is needed to make decisions about what a user is allowed to do. These decisions can be based on the user’s identity, role membership, or both. A more detailed discussion of security is outside the scope of this course. For more information about security, see “Securing Your Application” in the .NET Framework SDK documentation. Additionally, Course 2350A, Securing and Deploying Microsoft .NET Assemblies (Prerelease) covers code access security and role-based security in greater detail.
Localization To localize an application, you typically perform the following steps: 1. Separate your default resources from the code, and specify the localized text in a text file. 2. Compile the text file into a .resources file. 3. Package your default resources in the main assembly by using the C# compiler. 4. Create satellite resource assemblies, including satellites for .NET Framework cultures, by using the Alink tool. 5. Deploy the satellites to a directory structure underneath the main application. 6. Write the code to access the resources at run time. For more information about localization, see the .NET Framework SDK documentation.
34
Module 4: Deployment and Versioning
Packaging and Deployment Tools Topic Objective
To provide a brief overview of packaging and deployment tools that are included in the .NET Framework SDK.
Lead-in
The .NET Framework SDK includes several useful tools for examining assemblies and working with the global assembly cache.
!
Assembly Linker (Al.exe)
!
Global Assembly Cache tool (Gacutil.exe)
!
MSIL Disassembler (Ildasm.exe)
!
Strong Name (Sn.exe)
!
Native Image Generator (Ngen.exe)
!
Assembly Binding Log Viewer (Fuslogvw.exe)
!
.NET Framework Configuration Tool (Mscorcfg.msc)
!
Code Access Security Policy Tool (Caspol.exe)
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework SDK includes several useful tools for examining assemblies and working with the global assembly cache. For more information about the tools that are introduced in this topic, see “.NET Framework Tools and Debugger” in the .NET Framework SDK documentation.
Assembly Linker (Al.exe) The Assembly Linker is most useful to developers who need to create a single assembly from multiple components’ files, such as those that may be produced with mixed-language development.
Global Assembly Cache Tool (Gacutil.exe) The global assembly cache tool (Gacutil.exe) allows you to view and manipulate the contents of the global assembly cache. This tool provides much of the same cache-viewing functionality as the Windows Shell Extension (Shfusion.dll), but the global assembly cache tool is more usable from build scripts, makefiles, and batch files. Specifically, Gacutil.exe allows you to install assemblies into the cache, remove them from the cache, and list the contents of the cache. You must have Administrator privileges on a computer to install assemblies into the global assembly cache. To avoid removing more than one assembly from the global assembly cache, you can use a command with the name of your specific assembly, as follows: gacutil /u! hello,ver=1.0.0.1,Culture=en,PublicKeyToken=874e23ab874e23ab
The preceding command removes only the hello assembly that matches the fully specified version number, culture, and public key.
Module 4: Deployment and Versioning
35
The following command lists the contents of the global assembly cache: gacutil /l
MSIL Disassembler (Ildasm.exe) You can also explore the namespaces in files that come with the runtime or in files that you or others create by using the command-line MSIL disassembler. To open a window that displays information about System.Data.dll, use the following command: >ildasm System.Data.dll
Each of the namespace nodes represents a separate namespace, which can be expanded to explore the class objects and their methods and properties. The MSIL disassembler also features a number of command-line options, which are particularly useful when you want to redirect output to the console or to a text file for subsequent analysis. Tip Place a shortcut to Ildasm.exe in your SendTo folder.
Strong Name Tool (Sn.exe) When working with shared components, you can use the Strong Name (Sn.exe) command-line tool for several purposes. For example, you can use the tool to generate a new public-private key pair and write that pair to an output file, as follows: sn -k
Native Image Generator (Ngen.exe) The Native Image Generator creates a native image from a managed assembly and installs it into the native image cache on the local computer. Running Ngen.exe on an assembly allows the assembly to load faster, because it restores code and data structures from the native image cache rather than generating them dynamically. Pre-compiling assemblies with Ngen.exe can improve the startup time for applications, because much of the work required to execute code has been done in advance. Therefore, it is more appropriate to use Ngen.exe for client-side applications where you have determined that the CPU cycles consumed by justin-time (JIT) compilation cause slower performance. Because there are many factors that affect the startup time of an application, you should carefully determine which applications would benefit from the use of Ngen.exe. Experiment by running both a JIT-compiled and a pre-compiled version of a candidate assembly in the environment in which it will be used. This will allow you to compare the startup times for the same assembly executing under different compilation schemes.
36
Module 4: Deployment and Versioning
Assembly Binding Log Viewer (Fuslogvw.exe) For Your Information
The Assembly Binding Log Viewer tool is used in the lab.
The Assembly Binding Log Viewer can be used to display details for assembly binds. This information helps you diagnose why the .NET Framework cannot locate an assembly at run time. These failures are usually the result of an assembly deployed to the wrong location or a mismatch in version numbers or cultures.
.NET Framework Configuration Tool (Mscorcfg.msc) The .NET Framework Configuration Tool provides a graphical interface for managing .NET Framework security policy and applications that use remoting services. This tool also allows you to manage and configure assemblies in the global assembly cache. The .NET Framework Configuration Tool is a Microsoft Management Console (MMC) snap-in.
Code Access Security Policy Tool (Caspol.exe) The Code Access Security Policy Tool allows you to examine and modify machine, user, and enterprise-level code access security policies. For more information about Caspol, see Course 2350A, Securing and Deploying Microsoft .NET Assemblies and the .NET SDK.
Module 4: Deployment and Versioning
37
Lab 4: Packaging and Deployment Topic Objective
To introduce the lab.
Lead-in
In this lab, you will build an application that is named client, which retrieves an array of strings from an instance of the Stringer class and reverses the order of these strings by using an instance of the AReverser class. These classes are packaged in two separate assembly libraries that are named Stringer.dll and AReverser.dll.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: !
Create an application by using private assemblies in multiple directories.
!
Install and remove a strong-named assembly from the global assembly cache.
!
Configure an application to control its binding to an assembly based on the assembly’s location and version data.
Lab Setup Starter and solution files are associated with this lab. The starter files are in the folder \Labs\Lab04\Starter. The solution files are in the folder \Labs\Lab04\Solution.
Scenario You will build an application that is named client, which retrieves an array of strings from an instance of the Stringer class and reverses the order of these strings by using an instance of the AReverser class. These classes are packaged in two separate assembly libraries that are named Stringer.dll and AReverser.dll.
Estimated time to complete this lab: 50 minutes
38
Module 4: Deployment and Versioning
Exercise 1 Locating Private Assemblies in Multiple Directories In this exercise, you will build the client application by using private assemblies that are located in multiple directories.
! Examine the application • Open Notepad and examine the source code for the files that are listed in the following table. Filename
Directory location
Client.cs
\Labs\Lab04\Starter\Compapp
Stringer.cs
\Labs\Lab04\Starter\Compapp\MyStringer
AReverser.cs
\Labs\Lab04\Starter\Compapp\AReverser
! Compile and build the application in a command window Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt. 1. Go to the MyStringer directory and build the private assembly library named Stringer.dll in that directory. 2. Go to the AReverser directory and make the private assembly named AReverser.dll in that directory. 3. Go to the Compapp directory and build the client application, referencing the private assemblies from steps 1 and 2. Reference the DLLs in their own directories. Do not copy them to the Compapp directory.
Module 4: Deployment and Versioning
39
! Execute the application 1. Run the Assembly Binding Log Viewer by executing Fuslogvw.exe. Click on the right hand check box labeled Log Failures. 2. Execute the Client.exe application and note the exception in the space that is provided. 3. In the Assembly Binding Log Viewer click on Refresh. Then double click on the Client.exe application, and view the log entry. 4. Create a configuration file in the Client directory to specify the appropriate privatePath so that the application can successfully bind and load the necessary classes. The Client.exe application should generate the following output: Strings from StringComponent C# String 0 C# String 1 C# String 2 C# String 3 Reversed Array Strings C# String 3 C# String 2 C# String 1 C# String 0
40
Module 4: Deployment and Versioning
Exercise 2 Using Strong-Named Assemblies and Versioning In this exercise, you will build the client application by using strong-named assemblies that are located in the global assembly cache. You will then configure an application to control its binding to an assembly based on the assembly’s location and version data.
! Build a compatible strong-named version of AReverser 1. From a command prompt window, navigate to the \Labs\ Lab04\Starter\Compapp directory, and use the Strong Name tool to create a public-private key pair file that is named OrgVerKey.snk. 2. Open Notepad. On the File menu, click Open, browse to \ Labs\Lab04\Starter\ Compapp\AReverser_v2.0.0.0, and open AReverser.cs. 3. Examine the source code and note that it is the same code that was used in the preceding exercise. In particular, note that the signature of the Invert method is: public string[] Invert(string[] myString)
4. Modify the source code to specify the key pair file from step 1 and to specify a version number of 2.0.0.0. 5. From a command prompt window, navigate to the \Labs\ Lab04\Starter\Compapp directory and build the 2.0.0.0 strong-named version of AReverser.cs. The AReverser.dll should be output into the AReverser_v2.0.0.0 subdirectory. 6. Use the global assembly cache tool (Gacutil.exe) to install this assembly in the global assembly cache. 7. Examine the global assembly cache by using Gacutil.exe or Windows Explorer, and note the presence of AReverser and its properties.
! Build an incompatible strong-named version of AReverser 1. In Notepad, on the File menu, click Open, browse to \ Labs\Lab04\Starter\Compapp\AReverser_v2.0.1.0, and open AReverser.cs. 2. Examine the source code and note that it is different than the code that was used in the preceding exercise. In particular, note that the signature of the Invert method is: public string[] Invert(string[] myString, int myCount)
3. Modify the source code to specify the key pair file OrgVerKey.snk and to specify a version number of 2.0.1.0. 4. From a command prompt window, navigate to the \Labs\ Lab04\Starter\Compapp directory and build the 2.0.1.0 strong-named version of AReverser.cs. The AReverser.dll should be output into the AReverser_v2.0.1.0 subdirectory. 5. Install this assembly in the global assembly cache. 6. Examine the global assembly cache by using Gacutil.exe or Windows Explorer, and note the presence of AReverser and its properties.
Module 4: Deployment and Versioning
41
! Compile, build, and execute the application 1. From a command prompt window, navigate to the Client directory and build the client application. Reference the private assembly MyStringer\Stringer.dll and the strong-named assembly AReverser_v2.0.0.0\AReverser.dll. 2. Execute the Client.exe application, which should generate the following output: Strings from StringComponent C# String 0 C# String 1 C# String 2 C# String 3 Reversed Array Strings C# String 3 C# String 2 C# String 1 C# String 0
! Configure to bind to the latest version of AReverser Tip You can obtain the publicKeyToken tag’s value by examining the global assembly cache using gacutil /l or the cache shell extension. 1. Modify the configuration file to bind to the incompatible later version 2.0.1.0 of AReverser. 2. Execute Client.exe and note the exception. 3. Use the global assembly cache tool to remove the two strong-named versions of AReverser. 4. Confirm the removal of the two strong-named versions of AReverser by viewing the global assembly cache with Gacutil.exe and Windows Explorer.
42
Module 4: Deployment and Versioning
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
Introduction to Application Deployment
!
Application Deployment Scenarios
!
Related Topics and Tools
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. What part of the assembly identifies its imported and exported types and its version information? The manifest.
2. What software does a computer require to run a .NET application locally? The .NET Framework common language runtime.
3. Name two simple ways to run a .NET Framework application. Copy the executable file and referenced assemblies to the local computer, or access them on a file server.
Module 4: Deployment and Versioning
43
4. Describe how an application can use an assembly that is located in an application’s subdirectory. Create a configuration file in the application’s directory that specifies a PrivatePath as follows: <probing privatePath="MyStringer"/>
5. What kind of assembly can be placed in the global assembly cache and be versioned? A strong-named assembly.
6. What command is used to generate public-private key pairs? The Strong Name (Sn.exe) tool is used to generate a new key pair and place them in a file: sn –k orgKey.snk
7. What command is used to install a strong-named assembly into the global assembly cache? > gacutil -i
THIS PAGE INTENTIONALLY LEFT BLANK
Module 5: Common Type System Contents Overview
1
An Introduction to the Common Type System
2
Elements of the Common Type System
8
Object-Oriented Characteristics
25
Lab 5: Building Simple Types
39
Review
44
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 5: Common Type System
iii
Instructor Notes Presentation: 90 Minutes Lab: 45 Minutes
After completing this module, students will be able to: !
Describe the difference between value types and reference types.
!
Explain the purpose of each element in the type system, including values, objects, and interfaces.
!
Explain how object-oriented programming concepts, such as abstraction, encapsulation, inheritance, and polymorphism, are implemented in the Common Type System.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft® PowerPoint® file 2349B_05.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Complete the lab.
Module Strategy Use the following strategy to present this module: !
An Introduction to the Common Type System Cover the basic architecture of the Common Type System. Introduce System.Object as the root type for all types in the Microsoft .NET Framework common language runtime. Explain how all types in the Common Type System are either reference types or value types.
!
Elements of the Common Type System In this section, cover the primitive types, objects, properties, and other elements of the Common Type System. The information will not be new to students with a C++ object-oriented background, so you can cover these topics quickly or omit most of the material in this section. This decision will depend on the student’s experience.
!
Object-Oriented Characteristics Discuss how the object-oriented characteristics of the .NET Framework common language runtime are supported. If the class is already experienced in object-oriented techniques, you can omit this section.
Module 5: Common Type System
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn about the Common Type System and object-oriented programming.
!
An Introduction to the Common Type System
!
Elements of the Common Type System
!
Object-Oriented Characteristics
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this module, you will learn about the Common Type System. Specifically, you will learn to differentiate between value types and reference types. You will also examine how classes, interfaces, properties, methods, events, and values are represented in the Microsoft® .NET Framework. After completing this module, you will be able to: !
Describe the difference between value types and reference types.
!
Explain the purpose of each element in the type system, including values, objects, and interfaces.
!
Explain how object-oriented programming concepts, such as abstraction, encapsulation, inheritance, and polymorphism, are implemented in the Common Type System.
2
Module 5: Common Type System
" An Introduction to the Common Type System Topic Objective
To provide an overview of the section topics.
Lead-in
In this section, you will learn about the architecture of the Common Type System.
!
Common Type System Architecture
!
Value Types vs. Reference Types
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn about the Common Type System architecture. Specifically, you will learn how types are specified and represented in the .NET Framework common language runtime, and you will learn the difference between value types and reference types.
Module 5: Common Type System
3
Common Type System Architecture Topic Objective
To describe the basic architecture of the Common Type System.
Lead-in
A type defines characteristics for a set of values.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** A type defines characteristics for a set of values. For example, the set of whole numbers between negative 32768 and positive 32767 is called the short type in C#, or System.Int16 in the .NET Framework class library. The short type has a physical representation, which, in this case, is a 16-bit value. The short type also defines operations allowed on the type, such as addition and subtraction.
System.Object System.Object is the root type for all types in the .NET Framework common language runtime. For this reason, any type you use will have System.Object as its base class. System.Object has the following methods. Methods
Description
Public Equals
Overloaded. Determines whether two Object instances are equal.
Public GetHashCode
Serves as a hash function for a particular type; suitable for use in hashing algorithms and data structures, such as a hash table.
Public GetType
Gets the type of the object.
Public ReferenceEquals
Determines whether the specified Object instances are the same instance.
Public ToString
Returns a string that represents the current object.
Protected Finalize
Overridden. Allows an Object to attempt to free resources and perform other cleanup operations before the Object is reclaimed by garbage collection.
Protected MemberwiseClone
Creates a shallow copy of the current Object.
For more information about the methods of System.Object, see Module 6, “Working with Types,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET).
4
Module 5: Common Type System
Value Types Value types are any type that inherits from System.ValueType. Value types are typically simple types, such as integer values or long values. Value types are special because they are allocated on the stack. New value types are defined by using the struct or enum keywords. You cannot create value types by using the class keyword. System.ValueType inherits from System.Object, which means that all value types can behave like System.Object. The type system defines primitive types, which are Byte, Int16, Int32, Int64, Single, Double, Boolean, Char, Decimal, IntPtr, and String. There are also built-in types such as System.Array available.
Reference Types Reference types are any type that inherits from System.Object and not System.ValueType. Reference types are analogous to pointers in C++, but there are subtle differences in the way reference types and pointers work. Reference types are allocated on the heap. Reference types also must be garbage collected. One example of a reference type is the System.IO file class, which represents a file in the system.
Fields, Methods, and Properties The Common Type System defines fields, methods, and properties that are available to all value types and reference types. A field is a data value stored in a class. For example, Name and Age could be fields describing an employee stored in an Employee structure. A method is an operation that can be performed on a class. For example, a Tenure method could calculate how long an employee has been employed. Properties are a convenient way to expose data values to external objects and code. For example, the Name and Age fields could be exposed as properties, which would allow the Employee structure to control how the name and age are stored and retrieved.
Interfaces Interfaces are the only types that do not inherit from System.Object. Interfaces have no implementation; they merely provide a description of methods, properties, and events. Any class that inherits from an interface must implement all of the methods, properties, and events in that interface. Interfaces provide a mechanism for specifying contractual relationships between classes. Interfaces also provide a means of implementing multiple inheritance. For more information about inheritance, see Module 6, “Working with Types,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
Module 5: Common Type System
5
Value Types vs. Reference Types Topic Objective
To explain the difference between value types and reference types.
!
Lead-in
With the exception of interfaces, all types in the Common Type System are divided into two categories: value types and reference types.
!
Value Types Are Primitive or User-Defined Structures #
Allocated on stack
#
Assigned as copies
#
Default behavior is pass by value
Reference Types Are Objects That Are: #
Allocated on heap using the new keyword
#
Assigned as references
#
Passed by reference
*****************************ILLEGAL FOR NON-TRAINER USE****************************** With the exception of interfaces, all types in the Common Type System are divided into two categories: value types and reference types.
Value Types Value types represent primitive or user-defined structures. Value types are allocated on the stack and are removed from the stack when the method call in which they were declared returns. When a value type is passed as an argument to a method, it is passed by value, unless the ref keyword is used. All value types must inherit from System.ValueType.
6
Module 5: Common Type System
An integer is an example of a value type. The following example shows an integer value type and its effect on method calls and assignment operations. using System; public class MainClass { public static void Main() { // Create a new integer with value 5 int a = 5; // Create a new uninitialized integer int b; // Initialize b by copying the value 5 into b b = a; //Increase the value of b by 5 b += 5; Console.WriteLine("{0}, {1}", a, b); Add5(b); Console.WriteLine("{0}, {1}", a, b); } public static void Add5(int num) { num += 5; } }
This code generates the following output: 5, 10 5, 10
In the preceding example, the variables a and b represent two separate instances of the integer type. The Assignment operator (=) copies the value from one variable to another. When the Add5 method is called, the = operator updates a copy of the integer, not the original integer, because a copy of the value is pushed onto the stack when passing value types.
Module 5: Common Type System
Reference Types Reference types are a reference to a value. Reference types are allocated on the heap and will remain on the heap until they are garbage collected. Reference types must be allocated by using the new keyword. When you pass reference types as parameters, the reference is pushed onto the stack, and the method call works with the actual value, not with a copy of the value. All reference types inherit from System.Object. The following example shows the effect of assignments and method calls when you use the StringBuilder class, which is a reference type: using System; using System.Text; public class MainClass { public static void Main() { //Create a new StringBuild instance //with the string "Bob" StringBuilder name1 = new StringBuilder("Bob"); StringBuilder name2; //Make name2 reference the same StringBuilder instance name2 = name1; name2.Append("by"); Console.WriteLine("Values are {0}, {1}", name1, name2); Possessive(name2); Console.WriteLine("Values are {0}, {1}", name1, name2); } public static void Possessive(StringBuilder name) { name.Append("'s"); } }
This code generates the following output: Values are Bobby, Bobby Values are Bobby’s, Bobby’s
In this example, name1 and name2 always refer to the same StringBuilder instance on the heap. Thus, whenever a change is made to the underlying instance, both variables are affected. Also, name2 is passed by reference to the Possessive method, so that when the Possessive method makes a change to the instance, both name2 and name1 are affected.
7
8
Module 5: Common Type System
" Elements of the Common Type System Topic Objective
To provide an overview of the section topics and objectives.
Lead-in
In this section, you will learn about primitive types, objects, properties, and other elements of the Common Type System.
!
Primitive Types
!
Objects
!
Constructors
!
Properties
!
Custom Types
!
Enumerations
!
Interfaces
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn about primitive types, objects, properties, and other elements of the Common Type System.
Module 5: Common Type System
9
Primitive Types Topic Objective
To explain how primitive types are used in the common language runtime.
Lead-in
Primitive types in the .NET Framework common language runtime are simple value types, commonly found in most programming languages.
!
Simple Small Types Common in Most Languages
!
Naming Differences
!
C# Support
!
Conversions
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Primitive types in the .NET Framework common language runtime are simple value types, which are commonly found in most programming languages. Primitive types are simple small types, such as Boolean types or character types. The following table identifies all of the primitive types available in the .NET Framework common language runtime. The table lists the Microsoft intermediate language (MSIL) assembler name of the type, which you can see when you view the disassembly of managed code in the MSIL Disassembler, the name of the type in C#, the name of the type in the .NET Framework class library, and the description of the type. Name in MSIL Assembler
Name in C#
Name in class library
Description
bool
bool
System.Boolean
True/false value
char
char
System.Char
Unicode 16-bit char
float32
float
System.Single
IEEE 32-bit float
float64
double
System.Double
IEEE 64-bit float
int8
sbyte
System.SByte
Signed 8-bit integer
int16
short
System.Int16
Signed 16-bit integer
int32
int
System.Int32
Signed 32-bit integer
int64
long
System.Int64
Signed 64-bit integer
unsigned int8
byte
System.Byte
Unsigned 8-bit integer
unsigned int16
ushort
System.UInt16
Unsigned 16-bit integer
unsigned int32
uint
System.UInt32
Unsigned 32-bit integer
unsigned int64
ulong
System.UInt64
Unsigned 64-bit integer
10
Module 5: Common Type System
The following example shows how to create and initialize the primitive System.UInt16 type in C# to store an employee’s age: System.UInt16 age = new System.UInt16(); age = 5;
Primitive types are inherently supported in C#, allowing you to use simpler syntax, as in the following example: ushort age = 5;
You can convert primitive types by using the Convert class. The Convert class defines static methods to convert from one primitive type to another. The following example shows how to convert a long type to an unsigned short type: long longAge = 32; ushort ushortAge; ushortAge = Convert.ToUInt16(longAge);
C# provides a more convenient conversion mechanism: casting. The following example shows how to explicitly cast a long type to an unsigned short type: long longAge = 32; ushort ushortAge; ushortAge = (ushort) longAge;
Casting in C# will automatically compile to conversion methods, such as ToUInt16. Also, you can use casting to avoid memorizing the class library names for primitive types.
Module 5: Common Type System
11
Objects Topic Objective
To explain the role of object types in the Common Type System.
Lead-in
Everything in the Common Type System is an object and inherits from System.Object.
!
Every Class Inherits from System.Object
!
Objects Specify Data and Behavior
!
Fields Define the Data
!
Methods Define the Behavior class class Accumulator Accumulator {{ public public float float Result; Result; public public void void Add(float Add(float amount) amount) {{ Result Result += += amount; amount; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Everything in the Common Type System is an object and inherits from System.Object. An object type declaration specifies data and behavior. The Common Type System defines data in terms of fields and defines behavior in terms of methods. Each field or method in an object is called a member of that object. An object is an instance of a class. The class specifies the types that are contained in a class and the class’s operations; an object is an instance containing a set of data values. In C#, you can use the class keyword to declare an object type that is a reference type. The following example shows how to declare a class called Accumulator. class Accumulator { // Fields and methods defined here }
Fields Fields describe the data in an object. In the preceding example of the Accumulator class, a field is needed to store the current result that has been accumulated. class Accumulator { public float Result; }
12
Module 5: Common Type System
Methods Methods describe the operations in an object. A method can have arguments and can also have a return type. Because the Common Type System requires strong typing, you must define a specific type for every parameter and return value. The following example shows how to specify an Add method for the Accumulator class that takes an amount of type float and adds it to the Result field. class Accumulator { public float Result; public void Add(float amount) { Result += amount; } }
The following code is an example of client code: Accumulator calc = new Accumulator(); calc.Result = 0; calc.Add(5); Console.WriteLine(calc.Result);
This code generates the following output: 5
Module 5: Common Type System
13
Constructors Topic Objective
To explain how to use constructors to initialize classes.
Lead-in
If you design a class that requires special initialization code, you can use a constructor to initialize the class properly when objects are created.
!
Constructors Are Used to Initialize Classes
!
Constructors Can Have Zero or More Parameters
class class Rectangle Rectangle {{ private private int int x1,y1,x2,y2; x1,y1,x2,y2; public public Rectangle(int Rectangle(int x1, x1, int int y1, y1, int int x2,int x2,int y2) y2) {{ this.x1 this.x1 == x1; x1; this.x2 this.x2 == x2; x2; this.y1 this.y1 == y1; y1; this.y2 this.y2 == y2; y2; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** If you design a class that requires special initialization code, you can use a constructor to initialize the class properly when objects are created. A constructor is a method with the same name as the class. The constructor runs when a new instance of the class is created. The following example shows how a constructor is used to initialize a class: public class Rectangle { //Rectangle coordinates private int x1,y1,x2,y2; //Initialize coordinates in constructor public Rectangle() { x1 = 0; x2 = 0; y1 = 0; y2 = 0; } } public class MainClass { public static void Main() { //Creating a new Rectangle //will call constructor Rectangle r = new Rectangle(); } }
14
Module 5: Common Type System
Default Constructor If you do not specify a constructor, the C# compiler will automatically provide a default constructor. The default constructor takes no parameters and calls the base class constructor.
Parameterized Constructor You can also create a constructor with parameters as follows: class Rectangle { //Rectangle coordinates private int x1,y1,x2,y2; //Initialize coordinates in constructor public Rectangle(int x1, int y1, int x2,int y2) { this.x1 = x1; this.x2 = x2; this.y1 = y1; this.y2 = y2; } } public class MainClass { public static void Main() { //Must specify parameters //for this constructor Rectangle r = new Rectangle(2,2,10,10); } }
Module 5: Common Type System
15
Properties Topic Objective
To describe how to use properties.
!
Properties Are Similar to Fields
Lead-in
!
Properties Use get and set Accessor Methods for Managing Data Values
Properties are values that can be stored or retrieved on a class.
public public float float Start Start {{ get get {{ return return start; start; }} set set {{ if if (start (start >= >= 0) 0) start start == value; value; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Properties are values that can be stored or retrieved on a class. Like fields and methods, properties are also considered members of an object. Properties are different than fields because access to properties is controlled through get and set accessor methods. Also, properties are not necessarily stored in a single variable. They may be calculated on the fly or stored in multiple variables.
get and set Accessors Property storage and property retrieval are defined by the get and set accessor methods. You write code in the get accessor method to determine how to retrieve a property value. You write code in the set accessor method to determine how to store a property value. By omitting the get or the set accessor, you can make a property read-only or write-only.
16
Module 5: Common Type System
The following example shows how to implement a Distance class, which can calculate the length of a trip from start to finish. The class contains three properties: Start, Finish, and Length. The Length property is read-only and is calculated on the fly. class Distance { float start, finish; public float Start { get { return start; } set { if (start >= 0) start = value; } } public float Finish { get { return finish; } set { if (finish >= start) finish = value; } } public float Length { get { return (finish - start); } } } public class MainClass { public static void Main() { Distance d = new Distance(); d.Start = 5; d.Finish = 10; float length = d.Length; } }
Module 5: Common Type System
17
In the preceding example, verification code is used in the set statements to control how the properties are set. Negative numbers are not allowed. Additional code could be written to throw an exception if invalid values are used. The client code in MainClass does not have to use method syntax with parentheses to refer to properties. In general, you should always use properties to expose data items to external classes. Properties allow you to encapsulate fields, so that you can change them in the future, if necessary. For more information about encapsulation, see Encapsulation in this module.
18
Module 5: Common Type System
Custom Types Topic Objective
To describe how to build data types that use the C# struct keyword.
Lead-in
You can build your own data type by using the C# struct keyword.
!
Inherit from System.ValueType
!
Are Defined with the struct Keyword in C#
!
Can Have Methods, Properties, and Fields
struct struct Employee Employee {{ public public string string Name; Name; public public ushort ushort Age; Age; public public DateTime DateTime HireDate; HireDate; public public float float Tenure() Tenure() {{ TimeSpan TimeSpan ts ts == DateTime.Now DateTime.Now –– HireDate; HireDate; return ts.Days/(float)365; return ts.Days/(float)365; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can build your own data types by using the C# struct keyword. The struct keyword will define a custom type that inherits from System.ValueType, which in turn inherits from System.Object. All value types are sealed; additional classes cannot be derived from them. The following example shows how the C# struct keyword is used to build a simple custom type that describes an individual employee: struct Employee { public string Name; public ushort Age; public DateTime HireDate; }
In C#, structures can also have methods as in the following example: struct Employee { public string Name; public ushort Age; public DateTime HireDate; public float Tenure() { TimeSpan ts = DateTime.Now – HireDate; return ts.Days/(float)365; } }
Structures can also have properties. For example, the Employee structure could be modified so that employee age is a property. Then the set accessor could be used to verify that the age is a valid age.
Module 5: Common Type System
19
Enumerations Topic Objective
To describe how to create enumerations, which allow developers to specify and represent simple types.
!
Lead-in
It is common for a variable to take on only one of a small number of values.
!
.NET Framework Enumerations #
Inherit from System.Enum
#
Use the enum keyword
Bit Flags
enum enum SeatPreference SeatPreference :: {{ Window, Window, //Assigned //Assigned Center, Center, //Assigned //Assigned Aisle //Assigned Aisle //Assigned }}
ushort ushort value value value value value value
00 11 22
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In programming, it is common for a variable to take on only one of a small number of values. For example, the variable for an airline passenger’s seating preference has only three values: Window, Center, or Aisle. It is much easier and safer for a developer to reference such values with text, rather than numbers. The .NET Framework common language runtime supports enumerations, which allow a developer to specify and represent these simple types.
.NET Framework Enumerations In the .NET Framework, enumerations inherit from the class System.Enum, which inherits from System.ValueType. The following example shows how to create an enumeration: enum SeatPreference : ushort { Window, //Assigned value 0 Center, //Assigned value 1 Aisle //Assigned value 2 }
In the preceding example, the base type of the enumeration is defined as ushort. You can use any simple type for a base type, except System.Char. An enumeration will be of type int, unless otherwise specified. The literals are specified in a comma-delimited list. Unless you specify otherwise, the first literal is assigned a value of 0, the second a value of 1, and so on.
20
Module 5: Common Type System
To use enumeration literals, you must fully specify the literal name as in the following example: SeatPreference sp; //Assign enumerated window type seat to sp variable sp = SeatPreference.Window; Console.WriteLine(sp.GetHashCode()); Console.WriteLine(sp.ToString());
This code generates the following output: 0 Window
The GetHashCode method will return the literal value. By using the ToString method, you can obtain the human-readable string name of the literal from a variable. This feature is useful when you need to print or save a human-readable form of the enumeration’s literals. Also, you can use the Parse method to obtain an enumerated value from a string. This feature is useful when receiving input in a text form that requires conversion to the appropriate enumerated value. The following example shows how to convert the string “center” into an enumerated value for the SeatPreference enumeration. sp = (SeatPreference) System.Enum.Parse(typeof(SeatPreference), “Center”);
Module 5: Common Type System
21
Bit Flags Enumerations in the .NET Framework are also useful for storing bit flags that often involve bitwise operations, such as the bitwise AND operator (&), and the bitwise OR operator (|). Use the Flags attribute to create an enumeration of bit flags. The following example refers to a computer-controlled weather display that uses icons to represent weather conditions. The enumeration would allow you to combine the icons in various ways to represent different conditions: [Flags] enum WeatherDisplay : ushort { Sunny = 1, Cloudy = 2, Rain = 4, Snow = 8, Fog = 16 } WeatherDisplay wd; //Assign both Sunny and Cloudy attributes //to create partly sunny forecast wd = WeatherDisplay.Sunny | WeatherDisplay.Cloudy; Console.WriteLine(wd.ToString()); wd = (WeatherDisplay)System.Enum.Parse(typeof(WeatherDisplay), "Rain, Snow"); Console.WriteLine(wd.GetHashCode());
This code generates the following output: Sunny, Cloudy 12
When using the Flags attribute, you must specify the values for the literals manually.
22
Module 5: Common Type System
Interfaces Topic Objective
!
An Interface Is a Contractual Description of Methods and Properties
Lead-in
!
An Interface Has No Implementation
!
Use Casting in Client Code to Use an Interface
To describe the uses of interfaces. An interface is a contractual description of a set of related methods and properties.
interface interface ICDPlayer ICDPlayer {{ void void Play(short Play(short playTrackNum); playTrackNum); void void Pause(); Pause(); void void Skip(short Skip(short numTracks); numTracks); short short CurrentTrack CurrentTrack {{ get; get; set; set; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** An interface is a contractual description of a set of related methods and properties. An interface has a name, and it has methods and properties. For example, an interface named ICDPlayer may have methods, such as Play, Pause, and Skip. It may also have properties, such as CurrentTrack and Time. The following example shows how to define an interface called ICDPlayer that has the methods Play, Pause, and Skip, and has the property CurrentTrack: interface ICDPlayer { void Play(short playTrackNum); void Pause(); void Skip(short numTracks); short CurrentTrack { get; set; } }
An interface has no implementation. Any class can inherit from any interface. To inherit from an interface, a class must implement all methods, properties, and events on that interface. Thus an interface serves as a contract specifying to any user of the class that the class has implemented all methods properties, and events defined in the interface.
Module 5: Common Type System
The following example shows how to implement the ICDPlayer interface in a class called Device. public class Device : ICDPlayer { // Internal property values protected string deviceName; protected short currentTrack; //Constructor public Device() { deviceName = "Default"; currentTrack = 1; } //Properties public string DeviceName { get { return deviceName; } set { deviceName = value; } } public short CurrentTrack { get { return currentTrack; } set { currentTrack = value; } } //Methods public void Play(short playTrackNum) { Console.WriteLine("Now Playing Track: {0}", playTrackNum); currentTrack = playTrackNum; } public void Pause() { Console.WriteLine("Now Paused"); } public void Skip(short numTracks) { Console.WriteLine("Skipped {0} Tracks",numTracks); } }
23
24
Module 5: Common Type System
In the preceding example, all of the methods and properties of ICDPlayer were implemented. A class may implement additional properties and methods, such as the DeviceName property in the Device class. Client code uses an interface by casting to the interface name. The following example shows how a client can create an instance of the Device class and then use the ICDPlayer interface: public class MainClass { public static void Main() { Device Device1 = new Device(); ICDPlayer CD1 = (ICDPlayer) Device1; //Call Play method on ICDPlayer interface CD1.Play(1); //Get CurrentTrack property of ICDPlayer interface Console.WriteLine("Current Track = {0}", CD1.CurrentTrack); //Get DeviceName property of Device object Console.WriteLine("Device Name = {0}", Device1.DeviceName); } }
In the preceding example, the Device1 variable also could have been used to access methods and properties of the ICDPlayer interface. For more information about how to separate interface methods and properties from class methods and properties, see Module 6, “Working with Types,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
Module 5: Common Type System
25
" Object-Oriented Characteristics Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
In this section, you will learn how the object-oriented characteristics of the .NET Framework common language runtime are supported.
!
Abstraction
!
Encapsulation
!
Inheritance
!
Polymorphism
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn how the object-oriented characteristics of the .NET Framework common language runtime are supported. Everything in the common language runtime is an object that encompasses fields, methods, properties, and events. Object-oriented characteristics, such as abstraction, encapsulation, inheritance, and polymorphism, make it easier to work with code in the .NET Framework.
26
Module 5: Common Type System
Abstraction Topic Objective
To describe the use and advantages of abstraction in object-oriented programming.
!
Abstraction Works from the Specific to the General
!
Grouping Elements Makes It Easier to Work with Complex Data Types
!
Abstraction Is Supported Through Classes
Lead-in
In object-oriented programming, the term abstraction refers to the process of reducing an object to its essence so that only essential elements are represented.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In object-oriented programming, the term abstraction refers to the process of reducing an object to its essence so that only essential elements are represented. For programmers, abstraction means working from the specific to the general. An example of abstraction would be a phone number, such as (206) 555-1212. At the specific level, this phone number is a sequence of numbers: 2065551212. However, most people do not memorize phone numbers at this specific level. Instead they memorize such information by grouping the numbers into meaningful chunks. In this example, the phone number becomes an area code (206), prefix (555), and number (1212). This abstraction makes it easier for a person to remember the number. The number is reduced to three chunks to memorize, instead of 10 individual numbers. Abstraction is a powerful process that allows people to group and communicate information in simpler and more meaningful ways. In the Common Type System and other object-oriented systems, the fundamental unit of abstraction is the class. Class types allow you to group different types of information, and they provide the functionality to help you operate on that information. From a design perspective, abstraction allows you to treat information and functionality as complete functional units, rather than as disparate pieces of information and operations.
Module 5: Common Type System
27
Encapsulation Topic Objective
To explain the process of encapsulation in objectoriented programming.
!
Encapsulation Is the Process of Hiding Internal Details of a Class
Lead-in
!
Encapsulation Keywords
Encapsulation, or information hiding, is the process of packaging attributes and functionality to prevent other objects from manipulating data or procedures directly.
#
public
#
protected
#
internal
#
private
!
Type-Level Accessibility
!
Nested Classes
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In object-oriented programming, encapsulation, or information hiding, is the process of packaging attributes and functionality to prevent other objects from manipulating data or procedures directly. Encapsulation also allows the object that is requesting service to ignore the details of how the service is provided. By hiding the internal unnecessary data and functionality of a class from other classes, encapsulation provides greater flexibility in the design of classes. For example, when a sorting class performs sorts on data items, the internal data structure of the class will probably store the data items, such as a linked list. The class will also have internal functions that operate on the linked list. If clients that use the sorting class have direct access to the linked list, they will incorporate code that relies on the linked list. In fact, if no accessor functions are written for the linked list, the clients must use the linked list to store or retrieve data items. If the sorting class changes in the future, it may change the linked list to an array for optimization, and the clients will stop functioning correctly. At that time, the clients would require rewriting to use the array syntax. If the sorting class hid the internals of its implementation and provided accessor functions that mapped to the internal data structure, this problem could be avoided. C# provides a set of access modifiers that vary the degree of accessibility to members of any type. The following table lists all of the access modifiers supported in C#. Access modifier
Description
public
Makes a member available to all other classes in all assemblies
protected
Makes a member available only to classes that inherit from this class
internal
Makes a member available only to other classes in the same assembly
private
Makes a member available only to the same class
28
Module 5: Common Type System
If no access modifier is specified, a default modifier is applied. The following table shows which modifiers are applied to different types by default. Members of
Default member accessibility
enum
public
class
private
interface
public
struct
private
Type-Level Accessibility You can specify accessibility at the type level and at the member level. Access modifiers can be applied to enumerations, classes, interfaces, and structs. However, for top-level types, internal and public are the only allowed modifiers. You cannot create a private class or a protected interface at the top level in a namespace. The access modifier applied at the type level will supercede member-level access modifiers if the type-level access modifier is more restrictive. For example, an internal class can have a public method, but the internal modifier will make the public method internal as well. The following example shows the effect of applying access modifiers at the class level and the member level: public class COuter1 { public static short MyPublicShort = 0; internal static short MyInternalShort = 0; private static short MyPrivateShort = 0; } public class MainClass { public static void Main() { COuter1.MyPublicShort = 1; //Success because publicly available COuter1.MyInternalShort = 2; //Success because in same assembly COuter1.MyPrivateShort = 3; //Failure because private only available to COuter1 class } }
Module 5: Common Type System
Nested Classes When classes are nested, the nested classes cannot exceed the accessibility of the containing class. For example, if a nested class is marked as public, but the containing class is marked as internal, the nested class must also be internal. The following example shows the effect of access modifiers on nested classes: internal class COuter1 { public class CInner1 { public static short MyPublicShort = 0; internal static short MyInternalShort = 0; private static short MyPrivateShort = 0; } internal class CInner2 { public static short MyPublicShort = 0; internal static short MyInternalShort = 0; private static short MyPrivateShort = 0; } private class CInner3 { public static short MyPublicShort = 0; internal static short MyInternalShort = 0; private static short MyPrivateShort = 0; } } public class MainClass { public static void Main() { COuter1.CInner1.MyPublicShort = 1; //Success because internal at class level //and in same assembly COuter1.CInner2.MyPublicShort = 2; //Success because internal and in same assembly COuter1.CInner3.MyPublicShort = 3; //Failure because class is private, and all members are private } }
29
30
Module 5: Common Type System
Inheritance Topic Objective
To explain how inheritance works in the Common Type System.
!
Inheritance Is the Reuse of Class Members in Other Classes
Lead-in
!
The Common Type System Only Supports Single Inheritance for Classes
!
Member Hiding
Inheritance is a method of reuse that enables one class to reuse, or inherit, the fields, properties, and methods of another class.
#
Redefine the same method in the derived class
#
Use the new keyword
!
Abstract Members
!
Sealed Classes
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In object-oriented programming, inheritance is a method of reuse that enables one class to reuse, or inherit, the fields, properties, and methods of another class. Inheritance represents an “is a” relationship between classes. For example, if a square class inherits from a shape class, the square “is a” kind of shape. Inheritance is useful when multiple classes share the same methods or data. The common methods and data can be abstracted into a separate class that the other classes inherit from. Inheritance is pervasive in the Common Type System. Every class must inherit from System.Object, so all classes will have the members of System.Object.
Single Inheritance The Common Type System only supports single inheritance of class types. Single inheritance means a class can inherit directly from only one other class. Some object-oriented systems, such as C++, allow multiple inheritance, which means that one class can inherit from many other classes. The Common Type System does support multiple inheritance through interfaces. In that case, one class can inherit from one other class and from zero or more interfaces. For more information about multiple inheritance through interfaces, see Module 6, “Working with Types,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET). In the simplest form of inheritance, a class inherits its members from the base class and gains all the functionality of the base class, and at the same time it adds additional functionality of its own.
Module 5: Common Type System
In the following example, three classes are defined. The base class is called Animal; it implements an Age property and a Move method. The second class is called Dog; it inherits from Animal and implements an additional method called Bark. The third class is called Cat; it also inherits from Animal and implements an additional method called Meow. public class Animal { protected short age = 0; public short Age { get { return age; } set { if (value > 0) age = value; } } public void Move() { Console.WriteLine("Animal is Moving"); } } public class Dog : Animal { public void Bark() { Console.WriteLine("Bark!"); } } public class Cat : Animal { public void Meow() { Console.WriteLine("Meow!"); } } public class MainClass { public static void Main() { Dog d = new Dog(); Cat c = new Cat(); d.Age = 3; d.Move(); d.Bark(); c.Age = 2; c.Move(); c.Meow(); } }
31
32
Module 5: Common Type System
This code generates the following output: Animal is Moving Bark! Animal is Moving Meow!
In the Main method, a Dog object and Cat object are created. Methods and properties of the Animal class are called on the Dog object and the Cat object. In this way, the Animal class functionality for storing the age of an animal and implementing movement in an animal can be written once and reused in multiple classes.
Member Hiding Sometimes you will want a class to inherit from a base class, but you will want to modify some of the base class’s functionality. For example, you may want a new kind of animal class called Slug to inherit from the Animal class, but you will want to use the Move method to make the slug move slowly. You can accomplish this effect by creating a method with the same name and parameter list as the base class method. The following example shows how you can customize the Move method for slugs by making the Slug class replace the Move method of the Animal class: public class Slug : Animal { public new void Move() { Console.WriteLine("Moving very slowly"); } } public class MainClass { public static void Main() { Slug s = new Slug(); s.Move(); } }
This code generates the following output: Moving very slowly
In the preceding example, the new keyword is used to signal that a method was replaced. In this context, the new keyword denotes that a method was hidden. This use of the new keyword should not be confused with the use of the new keyword to allocate a new object. While the new keyword is not required for method hiding, it enhances readability. The C# compiler will issue a warning if the new keyword is not used on replaced methods.
Module 5: Common Type System
33
Abstract Members Methods, fields, and properties can also be abstract. A method, field, or property is abstract when the base class does not implement the member, and the derived class must implement the member. Abstract members are marked with the abstract keyword. When derived classes implement abstract methods, they must use the override keyword to indicate that they are overriding the base-class functionality. The following example shows an abstract Shape class that has one abstract method called Draw. The Shape class implements a Move method, which, when called, will call the derived class Draw method. Thus the Shape class has information about how to move but does not have information about how to draw after it moves. The abstract method forces the derived class to implement the Draw method so that the Shape class can move properly. using System; abstract class Shape { protected int x, y; //Derived class must implement next method public abstract void Draw(); public void Move(int x, int y) { this.x = x; this.y = y; //Call derived class implementation Draw(); } } class Square : Shape { public override void Draw() { Console.WriteLine("Drawing a Square at {0},{1}",x,y); } } class MainClass { public static void Main() { Square s = new Square(); //Call base class Move method, which in turn calls //derived class Draw method s.Move(1,1); } }
34
Module 5: Common Type System
This code generates the following output: Drawing a Square at 1,1
If a class contains any abstract members, the entire class becomes an abstract class. As a result, an instance of the abstract class cannot be created because it is missing functionality for a method. For this reason, the Shape class in the preceding example was marked as abstract.
Sealed Classes You can prevent other classes from deriving from a specific class by sealing that class. Use the sealed modifier to make a sealed class. sealed class SealedClass { public static void Foo() { } }
The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run time optimizations. In particular, because a sealed class is known to never have any derived classes, it is possible to transform virtual function member invocations on sealed class instances into non-virtual invocations.
Module 5: Common Type System
35
Polymorphism Topic Objective
To explain the use of polymorphism to define a base class.
!
Polymorphism Allows a Reference Variable to Call the Correct Method
!
Virtual Methods Enable Polymorphism in the Common Type System
Lead-in
In object-oriented programming, polymorphism allows you to define a base class that includes routines that perform standard operations on groups of related objects, without regard to the exact type of each object.
!
#
Use the virtual keyword in the base class
#
Use the override keyword in the derived class
Sealed Methods
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Polymorphism derives from the Greek for “many forms.” In object-oriented programming, polymorphism allows you to define a base class that includes routines that perform standard operations on groups of related objects, without regard to the exact type of each object. For example, in the preceding animal scenario, you could add a method called MakeNoise. When this method is called, Animal objects should print “Animal is making noise.” A dog object should print “Bark!”, and a cat object should print “Meow!” In the following example, the code does not work as expected; it prints “Animal is making noise,” instead of the desired specific animal noise, such as “Bark” or “Meow”: public abstract class Animal { //..Other properties and methods public void MakeNoise() { Console.WriteLine("Animal is making noise"); } } public class Dog : Animal { //..Other properties and methods public new void MakeNoise() { Console.WriteLine("Bark!"); } }
(Code continued on the following page.)
36
Module 5: Common Type System public class Cat : Animal { //..Other properties and methods public new void MakeNoise() { Console.WriteLine("Meow!"); } } public class MainClass { public static void Main() { Dog d = new Dog(); Cat c = new Cat(); d.Age = 3; c.Age = 2; WorkWithAnimal(d); WorkWithAnimal(c); } public static void WorkWithAnimal(Animal a) { Console.WriteLine("Working with animal age {0}",a.Age); a.MakeNoise(); } }
This code generates the following output: Working with animal age 3 Animal is making noise Working with animal age 2 Animal is making noise
In the preceding example, because the reference in the WorkWithAnimal method is type Animal, the Animal base class MakeNoise always gets called. The desired behavior would be a dog object making a noise like a dog, not an animal.
Module 5: Common Type System
To resolve this problem, use the virtual keyword to mark a method as virtual. Using the virtual keyword will ensure that the correct method call gets called on the basis of the object type, not the reference type. The derived class must then use the override keyword on the same method, as in the following example: public abstract class Animal { //..Other properties and methods public virtual void MakeNoise() { Console.WriteLine("Animal is making noise"); } } public class Dog : Animal { //..Other properties and methods public override void MakeNoise() { Console.WriteLine("Bark!"); } } public class Cat : Animal { //..Other properties and methods public override void MakeNoise() { Console.WriteLine("Meow!"); } } public class MainClass { public static void Main() { Dog d = new Dog(); Cat c = new Cat(); d.Age = 3; c.Age = 2; WorkWithAnimal(d); WorkWithAnimal(c); } public static void WorkWithAnimal(Animal a) { Console.WriteLine("Working with animal age {0}",a.Age); a.MakeNoise(); } }
37
38
Module 5: Common Type System
This code generates the following output: Working with animal age 3 Bark! Working with animal age 2 Meow!
Sealed Methods You can use the sealed modifier to prevent derived classes from overriding specific methods. Methods marked with the sealed modifier are called sealed methods. The sealed modifier can only be used in conjunction with the override modifier. class A { public virtual void Foo() {/*...*/ } } class B : A { //Any class derived from B will //not be able to override Foo public sealed override void Foo() {/*..*/ } }
Module 5: Common Type System
39
Lab 5: Building Simple Types Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create simple types and enumerations, and work with inheritance.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: !
Create enumerations.
!
Create custom value types as structures.
!
Inherit from a base class and override properties and methods.
!
Create constructors.
!
Use appropriate levels of encapsulation through the private and public access modifiers.
Prerequisites Before working on this lab, you must have: !
Knowledge about how to use Microsoft Visual Studio® .NET for creating and working with console applications.
!
Knowledge about the C# language.
!
Knowledge about the System.Console namespace for interacting with the console.
Estimated time to complete this lab: 45 minutes
40
Module 5: Common Type System
Exercise 1 Creating an Enumeration In this exercise, you will create a simple program that uses an enumeration to reference weekdays. You will enumerate the weekdays, Monday through Friday, as a type. Then you will write code to accept a day from the user, match the day to the enumerated type, and write a response back to the user.
! Create an enumeration 1. Open the Days project located in the folder \Labs\Lab05\ Starter\Days. 2. Open the Main.cs file. 3. Create an enumeration called Weekdays. The enumeration should be of type int and should enumerate the literals Monday, Tuesday, Wednesday, Thursday, and Friday.
! Use an enumeration 1. Locate the Main method of the MainClass class. 2. In the Main method, write code to perform the following tasks: a. Write a question to the console that reads, “What day of the week is it?” b. Use the Console.ReadLine method to get the user’s response. c. Determine which day the user selected by parsing the Weekdays enumeration. Store the correct Weekdays literal in a variable of type Weekdays. d. Use a switch statement on the mapped response to determine which day of the week it is. Based on the response, write a string to the user. For example, for Monday, write “Monday is the first weekday.” For Tuesday, write “Tuesday is the second weekday,” and so on. 3. Compile and test the code. Run the program and type in a weekday. You should get an appropriate response based on the day you entered.
Module 5: Common Type System
41
Exercise 2 Creating a Custom Value Type In this exercise, you will create a custom value type or structure to represent a complex number. Complex numbers have a real part and an imaginary part. You will design the structure to store the real and imaginary parts, and you will perform addition on complex numbers.
! Create a structure 1. Open the Complex project located in the folder \Labs\ Lab05\Starter\Complex. 2. Open the Main.cs file. 3. Create a new structure called Complex. a. Create a public member variable called Real of type int. b. Create a public member variable called Imaginary of type int. 4. Create an overloaded Addition operator (+) for the Complex structure. The overloaded operator should look as follows: public static Complex operator +(Complex c1, Complex c2) { }
a. In the Addition operator method, create a variable called result of type Complex to hold the result of the addition. b. Add c1.Real to c2.Real and store the result in result.Real. c. Add c1.Imaginary to c2.Imaginary and store the result in result.Imaginary. d. Return the result.
! Use the structure 1. Locate the Main method in the MainClass class. 2. In the Main method, write code to create a variable called num1 of type Complex. 3. Create a second variable called num2 of type Complex. 4. Initialize num1 and num2 with real and imaginary parts. For example, num1 could have values of 2 and 3 for the real and imaginary parts. num2 could have values of 3 and 4 for the real and imaginary parts. 5. Add the two numbers together, and print both the real and imaginary parts. 6. Compile the program and run it. Verify that the result is correct. Using the values in step 4, you should see the results of 5 and 7 for the real and imaginary parts.
42
Module 5: Common Type System
Exercise 3 Creating Objects In this exercise, you will derive new classes from base classes and then use the derived classes. You will work with a base class called Polygon. The class represents the geometric characteristics of polygons. Polygons have a number of sides. Thus the Polygon class defines a NumSides property but does not implement it. The derived classes implement the NumSides property. For example, a Triangle class could be created that overrides the NumSides property and initializes it to 3. A Pentagon class could be created that overrides the NumSides property and initializes it to 5. The Polygon class also implements a Degrees property that returns the sum of all the angles in the polygon. For example, the angles in a triangle always add up to 180 degrees. This simple calculation is based on the number of sides in the polygon, and so the derived class must implement the NumSides property. The Polygon class also defines an abstract method called Area that will return the area of the polygon. The derived class must override this method to calculate the correct area.
! Derive a new class 1. Open the Shapes project located in the folder \Labs\Lab05\ Starter\Shapes. 2. Open the Polygon.cs file and study the code for the Polygon class. Notice that it is an abstract class that has an abstract property called NumSides, a property called Degrees, and an abstract method called Area that returns the area inside the polygon. 3. Open the Main.cs file. 4. Create a new class called Square that inherits from Polygon. a. Create private member variables called x1, y1, and size of type int to store the Cartesian coordinates of the upper-left corner and the size of the square, which is the length of one side. b. Create a private member variable called numSides of type ushort to store the number of sides that the square has. c. Create a constructor that accepts three integer parameters (x1, y1, size) to construct a new square. Initialize the private member variables to the values passed as parameters. Initialize numSides to 4. d. Override the NumSides property in the base class to return the numSides variable. e. Create read/write properties called X1, and Y1 respectively that map to the private variables x1, and y1. f. Create two read-only properties called X2 and Y2. These represent the lower-right coordinate of the square and are calculated by adding size to x1 or y1 respectively. g. Override the Area method to return the area of the square. The area can be calculated with the following formula: (size)2.
Module 5: Common Type System
43
! Use the derived class 1. Locate the Main method in the MainClass class. 2. In the Main method, write code to create a new Square object. Initialize the square with values for the upper-left corner and size, such as 1, 1, and 4. 3. Write the number of degrees in the square to the console by printing the Degrees property. 4. Write the area to the console by calling the Area method. 5. Write the x1, y1, x2, y2 coordinates to the console by using the properties of the square object. 6. Compile the program and test it. Verify that all the methods and properties work as expected. Given a square with upper-left coordinates of 1, 1, and a size of 4, the number of degrees is 360, the area is 16, and the lower-right coordinates are 5, 5.
44
Module 5: Common Type System
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
An Introduction to the Common Type System
!
Elements of the Common Type System
!
Object-Oriented Characteristics
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. What are the differences between value types and reference types? Value types are allocated on the stack, assigned as copies, and passed by value. Reference types are allocated on the heap, assigned as references, and passed by reference.
2. What are the differences between fields and properties? A field is a data value in a class that can be directly accessed and manipulated by other classes. A property is a value in a class that is accessed through get and set accessor methods. The actual data value of a property may be stored in the class instance, or calculated when accessed.
3. How do you create an enumeration in C#? Use the enum keyword: enum name : type { …literals… }
Module 5: Common Type System
45
4. What is an interface? An interface is a contractual description of a set of related methods and properties.
5. How is encapsulation supported by the .NET Framework? Encapsulation is supported through access modifiers, such as public, protected, internal, and private.
THIS PAGE INTENTIONALLY LEFT BLANK
Module 6: Working with Types Contents Overview System.Object Class Functionality Specialized Constructors
1 2 12
Type Operations
18
Interfaces
28
Managing External Types
34
Lab 6: Working with Types
38
Review
43
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 6: Working with Types
iii
Instructor Notes Presentation: 75 Minutes Lab: 45 Minutes
After completing this module, students will be able to: !
Apply attributes to control visibility and inheritance in classes and interfaces.
!
Create and use interfaces that define methods and properties.
!
Explain how boxing and unboxing works and when it occurs.
!
Use operators to determine types at run time and cast values to different types.
!
Explain what features are available to work with unmanaged types, such as COM types.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft® PowerPoint® file 2349B_06.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Complete the lab.
iv
Module 6: Working with Types
Module Strategy Use the following strategy to present this module: !
System.Object Class Functionality In this section, discuss how System.Object provides classes to generate hash functions, represent strings, and compare objects for identity and equality. Explain that this section does not cover all of the classes in System.Object, and that other classes are covered elsewhere in the course. For example, finalization and the Finalize method are covered in detail in Module 9, “Memory and Resource Management,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET).
!
Specialized Constructors This section covers more advanced types of constructors. Explain how static constructors work, when to use them, and when to use private constructors. If the students in your class already have experience in C++, you may not need to spend much time on these topics.
!
Type Operations The Microsoft .NET Framework common language runtime supports a variety of type operations for working with types. Discuss conversions and conversion operators for determining and converting the type of an object. Also cover how to cast types for conversion and for treating a type as a different type. For experienced C++ programmers, you may be able to cover type conversion and casting quickly. C++ programmers may find it useful that the as operator in C# is similar to dynamic_cast in C++. Spend most of this section discussing boxing and unboxing. Students will need to be aware of the performance consequences of boxing. Explain how you can avoid or minimize these consequences if you must use boxing.
!
Interfaces Discuss how multiple inheritance works through interfaces and explain how to explicitly implement interfaces. As with the other topics in this module, you may be able to cover this section quickly if your audience is already familiar with object-oriented programming techniques. For experienced C++ programmers, consider mentioning that explicit interface implementation was not possible in C++.
!
Managing External Types Briefly introduce Platform Invocation Services and COM interoperability. Be aware that more information about these topics is available in Module 15, “Interoperating Between Managed and Unmanaged Code,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) and “Interoperating with Unmanaged Code” in the .NET Framework SDK documentation.
Module 6: Working with Types
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn how to apply your knowledge of the Common Type System to various programming scenarios.
!
System.Object Class Functionality
!
Specialized Constructors
!
Type Operations
!
Interfaces
!
Managing External Types
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this module, you will learn how to apply your knowledge of the Common Type System to various programming scenarios. This module will help you understand how to use types efficiently when developing Microsoft® .NET Framework applications. You should understand that many nuances in the type system can affect program clarity and performance if ignored. This module covers the use of attributes to control visibility and inheritance on types and explains how to work with various type operations, such as boxing and unboxing, and type operators. The module then explores how to work with types programmatically by using operators to coerce, cast, or discover types at run time. In addition, this module discusses how to build an interface that supports methods and properties and how to make interface designs more efficient. The module also highlights features that are designed to help you work with unmanaged types, such as COM types. After completing this module, you will be able to: !
Apply attributes to control visibility and inheritance in classes and interfaces.
!
Create and use interfaces that define methods and properties.
!
Explain how boxing and unboxing works and when it occurs.
!
Use operators to determine types at run time and cast values to different types.
!
Explain what features are available to work with unmanaged types, such as COM types.
2
Module 6: Working with Types
" System.Object Class Functionality Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
In this section, you will learn about the common methods that you need to override on the System.Object class.
!
Hash Codes
!
Identity
!
Equality
!
String Representation
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn about the common methods that you need to override on the System.Object class. This section does not cover finalization and the Finalize method, which are covered in detail in Module 9, “Memory and Resource Management,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C#™ .NET). Also, this section does not cover the MemberwiseClone method.
Module 6: Working with Types
3
Hash Codes Topic Objective
To explain how to use hash codes to perform quick lookups in tables and other types of collections.
Lead-in
A hash function is used to quickly generate a number, or hash code, that corresponds to the value of an object.
!
Hash Code Used to Perform Quick Lookups
!
Override GetHashCode Method on System.Object
!
Should Return Same Hash Code for Objects of Same Value
!
Should Implement Efficient Algorithm
struct struct Student Student {{ string string name; name; int int ID; ID; //Unique //Unique for for each each instance instance public public override override int int GetHashCode() GetHashCode() {{ return return ID; ID; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** A hash function is used to quickly generate a number, or hash code, that corresponds to the value of an object. Hash codes are useful for performing quick lookups in tables, such as the HashTable class, and other kinds of collections. A hash table uses the hash code to drastically limit the number of objects that must be searched to find a specific object in a collection of objects. The hash table does this by getting the hash value of the object and eliminating all objects with a different hash code. This preliminary search leaves only those objects with the same hash code to be searched. Because there are few instances with that hash code, searches are much quicker. System.Object provides a GetHashCode method, which returns an int type. You should override this method to return a hash code on any custom classes or structures that you create. One reason for overriding this method is that when two objects are equal in value, you should get the same hash code for each object if you call GetHashCode. In the case of custom objects, the default implementation of GetHashCode does not give you the same hash code for two objects that are equal in value.
4
Module 6: Working with Types
A good hash code algorithm will support the best performance by generating a random distribution for all input. You should base your hash code algorithm on one of the unique fields in the class. Also, you should never throw an exception from the GetHashCode method because GetHashCode can be called frequently and should always work reliably. The following example shows how to implement GetHashCode for a Student structure that stores a student’s name and ID. struct Student { string name; int ID; //Unique for each instance public override int GetHashCode() { return ID; } }
Module 6: Working with Types
5
Identity Topic Objective
To explain how identity is determined in the .NET Framework common language runtime.
!
Compare to Determine If Two References Are Actually the Same Object
Lead-in
!
Use the Object.ReferenceEquals Method to Test Identity
There are two kinds of comparison for objects: identity and equality. This topic covers object identity.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** There are two kinds of comparison for objects: identity and equality. This topic covers object identity.
Determining Identity Two objects are identical if they are, in fact, the same object. Every object in the .NET Framework common language runtime has an identity that makes it unique in the system. In C++, an object’s identity is determined by its address. Thus, if two pointers are compared and contain the same address, they point to the same object. In COM, an object’s identity is determined by the IUnknown interface. Thus, if two IUnknown interface pointers are compared and contain the same address, they are the same COM object. In the .NET Framework common language runtime, you can use the Object.ReferenceEquals method to compare for identity. Internally, ReferenceEquals compares the addresses of the objects in memory to determine if they are the same object. If they are the same object, ReferenceEquals returns true.
6
Module 6: Working with Types
Using the Object.ReferenceEquals Method In the following example, a value type variable called x is created and passed in two parameters to the Test method. The Test method compares the two parameters to determine if they are identical. class MyObject { public int X; } class MainClass { public static void Main() { MyObject obj1 = new MyObject(); obj1.X = 5; Test(obj1, obj1); MyObject obj2 = new MyObject(); obj2.X = 5; Test(obj1, obj2); } public static void Test(MyObject a, MyObject b) { if (Object.ReferenceEquals(a,b)) Console.WriteLine("Identical"); else Console.WriteLine("Not Identical"); } }
This code generates the following output: Identical Not Identical
Module 6: Working with Types
7
Equality Topic Objective
To explain how to override the Equals method and to introduce guidelines for implementing code to provide equality comparison for types.
Lead-in
Objects can also be compared for equality.
!
Comparing Two Objects to Determine If They Are Equal
!
Override the Equals Method
!
Supply == and != Operators
!
Guidelines #
If overriding Equals, override GetHashCode
#
If overloading ==, override Equals to use same algorithm
#
If implementing IComparable, implement Equals
#
Equals, GetHashCode, and == operator should never throw exceptions
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Objects can also be compared for equality. The Object.Equals method, equality operator (==), or inequality operator (!=) are used to test equality.
Equals Method The Equals method is part of the Object class. You should override this method in your classes and structures to perform appropriate behavior when comparing objects of certain types. The default Object.Equals method calls Object.ReferenceEquals, which results in an identity comparison instead of a value comparison. In general, you should also override the == and != operators to allow easier syntax to compare objects.
8
Module 6: Working with Types
The following example shows how to override the Equals method and the == and != operators to test user-defined Rectangle objects for equality. class Rectangle { //Rectangle coordinates public int x1,y1,x2,y2; public Rectangle(int x1, int y1, int x2, int y2) { this.x1 = x1; this.x2 = x2; this.y1 = y1; this.y2 = y2; } public override int GetHashCode() { return x1; } public override bool Equals (Object obj) { //Check for null and compare run-time types. if (obj == null || GetType() != obj.GetType()) return false; Rectangle r = (Rectangle)obj; return (x1 == r.x1) && (y1 == r.y1) && (x2 == r.x2) && (y2 == r.y2); } static public bool operator == (Rectangle r1, Rectangle r2) { //Check for null parameters //Cast to object to avoid recursive call if ((object)r1 == null) return false; //Let Equals method handle comparison return r1.Equals(r2); } static public bool operator != (Rectangle r1, Rectangle r2) { //Check for null parameters //Cast to object to avoid recursive call if ((object)r1 == null) return true; //Let Equals method handle comparison return !r1.Equals(r2); } } class MainClass { public static void Main() { Rectangle r1 = new Rectangle(5,5,50,55); Rectangle r2 = new Rectangle(5,5,50,55); Console.WriteLine(r1.Equals(r2)); Console.WriteLine(r1 == r2); Console.WriteLine(null == r1); } }
Module 6: Working with Types
9
This code generates the following output: True True False
Guidelines for Equality Comparison Use the following guidelines when implementing code to provide equality comparison for types. !
Anytime you override the Equals method, also override the GetHashCode method. If two objects are equal, they must return the same hash code. The default implementation of GetHashCode does not return the same value.
!
Anytime you overload the == operator, also override the Equals method and the != operator to use the same algorithm. This technique allows infrastructure code, such as HashTable and ArrayList classes, that uses the Equals method, to behave in the same manner as user code that is written with the == operator.
!
Anytime you implement the IComparable interface, also implement the Equals method, and ensure that both elements use the same algorithm for comparisons. You should also consider overloading the comparison operators because any client code that uses the IComparable interface is also likely to use these operators.
!
The Equals method, GetHashCode method, and comparison operators should never throw an exception. Exceptions in comparison operators can cause confusion because most programmers do not anticipate these types of exceptions. Also, these methods are called frequently and need to be efficient and clean to avoid writing additional error-handling code for what are considered simplistic methods.
10
Module 6: Working with Types
String Representation Topic Objective
To explain how the ToString method is used in the .NET Framework common language runtime.
Lead-in
All objects have the ability to represent themselves in a string form.
!
Override ToString to Customize String Form of a Class
struct struct President President {{ public public string string FirstName; FirstName; public string public string LastName; LastName; public public override override string string ToString() ToString() {{
}}
}}
!
return return FirstName FirstName ++ "" "" ++ LastName; LastName;
Use IFormattable Interface and ToString Method for Localized Strings
*****************************ILLEGAL FOR NON-TRAINER USE****************************** One characteristic of all objects is the ability to represent themselves in a string form. The ToString method provides this ability for all objects. Methods in the Microsoft .NET Framework common language runtime, such as Console.WriteLine, frequently use ToString. The ToString method also is useful for debugging. The default behavior of the Object.ToString method is to return the name of the class. The following example shows what happens when ToString is called on a President structure. struct President { public string FirstName; public string LastName; } class MainClass { public static void Main() { President firstPres; firstPres.FirstName = "George"; firstPres.LastName = "Washington"; Console.WriteLine(firstPres.ToString()); } }
This code generates the following output: President
Module 6: Working with Types
You can override the ToString method to provide custom behavior, as shown in the following example: struct President { public string FirstName; public string LastName; public override string ToString() { return FirstName + " " + LastName; } } class MainClass { public static void Main() { President firstPres; firstPres.FirstName = "George"; firstPres.LastName = "Washington"; Console.WriteLine(firstPres.ToString()); } }
This code generates the following output: George Washington
If an object needs to be represented in localized string forms, you should not override Object.ToString. Instead, you should implement the IFormattable interface and its ToString method. The IFormattable interface can take into account different locales.
11
12
Module 6: Working with Types
" Specialized Constructors Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
!
Static Constructors
!
Private Constructors
This section covers more advanced types of constructors. It explains how static constructors work, when to use them, and when to use private constructors.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This section covers more advanced types of constructors. It explains how static constructors work, when to use them, and when to use private constructors.
Module 6: Working with Types
13
Static Constructors Topic Objective
To explain how static constructors work.
Lead-in
Static constructors are used to initialize static fields.
!
Used to Initialize Static Members
class class DeviceConnection DeviceConnection {{ public public static static uint uint ConnectionCount; ConnectionCount; public void OpenConnection(string public void OpenConnection(string connectionName) connectionName) {{ ConnectionCount++; ConnectionCount++; //Other //Other work work to to open open device device }} static static DeviceConnection() DeviceConnection() {{ //Initialize //Initialize static static members members ConnectionCount ConnectionCount == 0; 0; }} }} !
.cctor in Disassembly
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Static constructors are used to initialize static fields. Static constructors are also known as class constructors and type constructors. Static constructors are called after a program begins running but before the first instance of the class is created.
14
Module 6: Working with Types
The following example shows a DeviceConnection class that represents a generic connection to a device. The class maintains a static field that holds the current connection count. The static constructor is created with the same name as the class but has the static attribute, rather than the public or private attribute. class DeviceConnection { public static uint ConnectionCount; public void OpenConnection(string connectionName) { ConnectionCount++; //Other work to open device } static DeviceConnection() { //Initialize static members ConnectionCount = 0; } } class MainClass { public static void Main() { // At some point before next line, // static constructor is called DeviceConnection d = new DeviceConnection(); d.OpenConnection("GameConsole:Joy1/3"); // Next line prints 1 Console.WriteLine(DeviceConnection.ConnectionCount); } }
A static constructor has no access modifiers, such as private or public. Inside a static constructor, only static fields can be used. Instance fields must be initialized in an instance constructor. When the static constructor is viewed in disassembly, it has a different name, .cctor, which stands for class constructor. In disassembly, instance constructors are called .ctor. You can have both types of constructors in the same class.
Module 6: Working with Types
15
If you initialize a static field inline with a value, a static constructor is created automatically. The following example shows how the DeviceConnection class can be rewritten to use an implicit static constructor. The presence of the static constructor can be verified by viewing the disassembly of the code. class DeviceConnection { //Next line automatically creates static constructor public static uint ConnectionCount = 0; public void OpenConnection(string connectionName) { ConnectionCount++; //Other work to open device } }
In the preceding example, the static field ConnectionCount is initialized inline to a value of 0. When you initialize static fields inline, a static constructor is implicitly created in which the initialization occurs. If you also provide an explicit static constructor, the inline initialization is compiled into the explicit static constructors. Inside the static constructor, the code for the inline initializations runs first, and then the code that you wrote in the static constructor runs.
16
Module 6: Working with Types
Private Constructors Topic Objective
To explain when to use private constructors.
!
Prevent a Class from Being Instantiated
Lead-in
!
Use Them on Classes with All Static Members
!
Use a Protected Constructor to Inherit from the Class
A private constructor can never be called. Therefore any class with a private constructor cannot be instantiated.
class class Trig Trig {{ public public static static double double Sin Sin {{ //Calculate and //Calculate and return return public public static static double double Cos Cos {{ //Calculate and //Calculate and return return public public static static double double Tan Tan {{ //Calculate //Calculate and and return return private private Trig(){} Trig(){} }}
(double (double x) x) sin(x) sin(x) }} (double (double x) x) cos(x) cos(x) }} (double (double x) x) tan(x) tan(x) }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** A private constructor can never be called. Therefore any class with a private constructor cannot be instantiated. The only type of class that uses a private constructor is a class with all static members. The following example shows how a private constructor is used to prevent a class from being instantiated. class Trig { public static double Sin (double x) { //Calculate and return sin(x) } public static double Cos (double x) { //Calculate and return cos(x) } public static double Tan (double x) { //Calculate and return tan(x) } private Trig(){} }
Module 6: Working with Types
17
Classes with all static members can be used to maintain global algorithms, as shown in the preceding example. They can also be used as a singleton class, which has only one set of values and methods that is available while a program is running. To derive from a class with static members, you should mark the constructor as protected. Marking the constructor as protected will prevent programmers from creating instances of the class, but allow other classes to derive from it. Note A class with static members is different than an interface. The static members can contain implementations, a class can have static fields, but an interface cannot.
18
Module 6: Working with Types
" Type Operations Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
The .NET Framework common language runtime supports a variety of type operations for working with types.
!
Conversions
!
Casting
!
Boxing
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework common language runtime supports a variety of type operations for working with types. This section discusses conversions and conversion operators for determining and converting the type of an object. This section also discusses how to cast types for conversion and for treating a type as a different type. It also describes boxing and unboxing value types to treat them as reference types.
Module 6: Working with Types
19
Conversions Topic Objective
To explain when and how to use explicit and implicit conversions.
Lead-in
Conversion is the process of changing a type to another type.
!
Explicit Conversions
int int xx == 5; 5; double double yy == (double) (double) x; x; !
Implicit Conversions
int int xx == 5; 5; double double yy == x; x; !
Conversion Operators
public public public public
static static static static
implicit implicit explicit explicit
operator operator operator operator
byte(Digit byte(Digit Digit(byte Digit(byte
d) d) b) b)
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Conversion is the process of changing a type to another type. Conversions are necessary when a value of one type must be assigned to a variable of a different type during assignment or when passing arguments to a method call.
Explicit and Implicit Conversions In explicit conversions, you do not need to specify the name of the type in a cast for assignment conversions or operand conversions. The following example shows how to convert an int value to a double value by using an explicit conversion. public static bool BiggerThanFive(double value) { if (value > 5) return true; else return false; } public static void Main() { int x = 5; //Use an explicit conversion for assignment double y = (double) x; //Use an explicit conversion for operand bool answer = BiggerThanFive((double) x); }
The preceding example is an example of a widening conversion. The bit size of an int is 32 bits, and the bit size of a double is 64 bits. Therefore, the type was widened during the conversion.
20
Module 6: Working with Types
In implicit conversions, you do not need to specify the name of the type during the conversion. Widening conversions can occur implicitly, and so they allow for simpler syntax. The preceding example could be written more simply as follows: public static void Main() { int x = 5; //Use an implicit conversion for assignment double y = x; //Use an implicit conversion for operand bool answer = BiggerThanFive(x); }
The following table shows the allowable implicit conversions for numeric types in the common language runtime. From
To
sbyte
short, int, long, float, double, or decimal
byte
short , ushort, int, uint, long, ulong, float, double, or decimal
short
int, long, float, double, or decimal
ushort
int, uint, long, ulong, float, double, or decimal
int
long, float, double, or decimal
uint
long, ulong, float, double, or decimal
long
float, double, or decimal
char
ushort, int, uint, long, ulong, float, double, or decimal
float
double
ulong
float, double, or decimal
Some conversions can result in a loss of precision. For example, converting an int to a float is an allowable implicit conversion, but a float has only seven digits of precision. Depending on the value of the int, some digits of precision may be lost. The following example shows a narrowing conversion in which a double is converted to an integer. double y = 4.56; int x = (int) y;
In this case, narrowing conversions must be explicit because there is almost always a loss of precision in the value that is being converted. In the preceding example, the value 4.56 will be truncated to 4 when it is assigned to x.
Module 6: Working with Types
21
Conversion Operators You can create user-defined conversions by providing conversion operators in your class or struct. Use the following syntax for specifying a conversion operator: public static [implicit | explicit] operator conv-type-out (conv-type-in operand) in which: !
conv-type-out is the name of the type to convert to.
!
conv-type-in is the name of the type to convert from.
!
operand is the name of the parameter holding the value being converted.
The following example shows how a structure that is called Digit, which represents a value from 0 to 9, can convert implicitly to a byte and explicitly to a Digit. struct Digit { byte value; public Digit(byte value) { if (value < 0 || value > 9) throw new ArgumentException(); this.value = value; } public static implicit operator byte(Digit d) { //Implicitly convert from Digit to short return d.value; } public static explicit operator Digit(byte b) { //Explicitly convert from short to Digit return new Digit(b); } } class MainClass { public static void Main() { Digit dig = new Digit(3); byte b = dig; //Implicit conversion operator invoked Console.WriteLine(b); //Prints 3 Digit dig2 = (Digit) b; //Explicit conversion invoked Console.WriteLine(dig2); //Implicit conversion prints 3 } }
22
Module 6: Working with Types
In the preceding example, the conversion from Digit to byte is implicit, while the conversion from byte to Digit is explicit. You should use the following guidelines to determine whether a conversion operator should be implicit or explicit. !
A conversion operator should be implicit to make code easier to read. Implicit conversions should never throw an error.
!
A conversion operator should be explicit whenever information could be lost in the conversion or if the conversion could throw an exception.
Module 6: Working with Types
23
Casting Topic Objective
To explain how casting is used in the .NET Framework.
Lead-in
Casting is used for explicit conversions. However, casting is also used for changing the type that is used to reference an object.
!
Casting Up from Derived Class to Base Class
!
Casting Down from Base Class to Derived Class
!
Type Operators
!
#
is
#
as
#
typeof
Casting Interfaces
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Casting is used for explicit conversions. However, casting is also used for changing the type that is used to reference an object.
Casting in an Inheritance Hierarchy Casting on objects is frequently used to change the reference type of an object to a base class reference or a derived class reference. When casting a derived class to a base class, you do not need to explicitly cast the object. The following example shows how a Shape type reference can be used to work with a Square object. //Shape is a base class class Shape {...} //Square is a derived class class Square : Shape {...} Square sq = new Square(); Shape sh = sq; sh.ShapeMethod();
When casting from a base class to a derived class, you must explicitly cast the object. If you cast to a derived type that does not match the underlying object, an InvalidCastException error will be thrown.
24
Module 6: Working with Types
The following example shows how to cast from a base class type to a derived class type. Square sq = new Square(); Shape sh = sq; Square sq2 = (Square) sh;//Cast down to Square sh = new Shape(); sq2 = (Square) sh;//InvalidCastException
Type Operators C# provides several operators to help cast object types appropriately. These operators help you avoid InvalidCastException errors. The is operator compares an object to a type. If the type matches the object, true is returned. If the type does not match the object, false is returned. Shape sh = new Shape(); Square sq; if (sh is Square) sq = (Square) sh;
The as operator attempts to convert an object to a type. If the conversion is successful, a reference of the specified type is assigned. Otherwise, null is assigned. Shape sh = new Shape(); Square sq = sh as Square; if (sq == null) Console.WriteLine("Shape was not a square"); else Console.WriteLine("Shape was a square");
You can use the typeof operator for more advanced type operations. The typeof operator will return a System.Type instance that describes the type through reflection. If you have an instance of a class, you can call GetType to obtain the same information. For more information about reflection, the mechanism for obtaining information about loaded assemblies and the types defined within them, including classes, interfaces, and value types, see “Discovering Type Information at Run time,” in the .NET Framework Software Developer’s Kit (SDK) documentation and Module 17, “Attributes,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
Casting Interfaces Casting is also used to obtain interface references to an object. The following example shows how the interface ICDPlayer can be obtained through casting from the Device class, which implements the ICDPlayer interface. ICDPlayer player; Device d = new Device(); player = (ICDPlayer) d;//Cast to interface player.Play();
Module 6: Working with Types
25
Boxing Topic Objective
To explain when and how boxing occurs.
!
Lead-in
Boxing and unboxing are conversions that occur automatically when a value type is converted to an object or when an object is converted to a value type.
Boxing Occurs to Convert a Value Type to a Reference Type #
Instance of System.Object is allocated on heap
#
Value type is copied to new object
int int xx == 5; 5; //Value //Value type type Object o = x; Object o = x; //Boxed //Boxed Console.WriteLine("The Console.WriteLine("The answer answer is is :: {0} {0} ", ", x);//Boxed x);//Boxed !
Unboxing Occurs to Retrieve Value Type from Object
int int yy == (int) (int) o; o; //unbox //unbox !
Boxing Can Be Expensive If Inside Loops
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Boxing and unboxing are conversions that occur automatically when a value type is converted to an object or when an object is converted to a value type. Boxing and unboxing occur implicitly and can affect the performance of your application code.
When Boxing Occurs Boxing occurs when a value type must be converted to a reference type. Boxing only occurs when converting to the type System.Object. Any other reference type must have implicit or explicit conversion operators to convert from value types. The following example shows how an integer value type is converted to an object reference type. int x = 5; //Value type Object o = x; //Implicit boxing operation occurs
In the preceding example, a boxing operation occurs when the object o is assigned the value in x. Boxing frequently occurs when calling methods that accept parameters of type Object. For example, Console.WriteLine can accept a string and an object parameter as follows. int x = 5; Console.WriteLine("The answer is: {0}",x);//Boxing occurs
In the preceding example, a boxing operation occurs on the call to Console.WriteLine. Because Console.WriteLine requires an Object type as the second parameter, x is implicitly boxed as an Object type.
26
Module 6: Working with Types
You can determine where boxing operations occur in your code by examining the disassembly. For example, if you use the Microsoft intermediate language (MSIL) Disassembler (Ildasm.exe), you will see an operation code called box, which is followed by the type that is being boxed.
How Boxing Works A value type must be copied from the stack to the heap if the value is going to be treated as a reference type. The boxing operation allocates memory on the heap for the value type and creates a reference to the new memory location. Then it copies the value type into the new memory location.
Unboxing To work with the value inside a boxed type, it must first be unboxed. Unboxing a type will copy it from the heap into a variable on the stack. The following example shows how to unbox an integer. int x = 5; Object o = x; //box int y = (int) o; //unbox
If the underlying value type that is being unboxed is incompatible with the type of the variable that it is assigned to, you will get an InvalidCastException. int x = 5; short s; Object o = x; //box s = (short) o; //Will throw InvalidCastException
Consequences Boxing can cause performance problems if you do not minimize its occurrence. The following example shows the consequences of boxing. int age = 5; int count = 1; for (count = 1;count < 10;count++) { Console.WriteLine("Current Age = {0}",age);//1 Box Console.WriteLine("Age in {0} year(s) will be {1}", count, age+count);//2 box operations }
In the preceding example, three boxing operations will occur for every iteration through the loop. This kind of scenario can be very costly in terms of performance. One solution is to box value types before entering a loop, as in the following example. int age = 5; Object oAge = age; //1 box operation int count = 1; for (count = 1;count < 10;count++) { Console.WriteLine("Current Age = {0}",oAge); Console.WriteLine("Age in {0} year(s) will be {1}", count, age+count);//2 box operations }
Module 6: Working with Types
27
In cases when the need for a higher level of performance is necessary, custom classes can be created to encapsulate values and work with values as reference types. class RefInt32 { public int Value; public RefInt32(int value) { this.Value = value; } public override string ToString() { return Value.ToString(); } public static implicit operator int (RefInt32 refInt) { return refInt.Value; } } int age = 5; RefInt32 refAge = new RefInt32(age); //Convert to ref type int count = 1; RefInt32 refCount = new RefInt32(count); //Convert to ref type RefInt32 refTotal = new RefInt32(0); for (refCount.Value = 1;refCount.Value < 10;refCount.Value++) { refTotal.Value = refAge.Value + refCount.Value; Console.WriteLine("Current Age = {0}",refAge); Console.WriteLine("Age in {0} year(s) will be {1}", refCount,refTotal);//0 box operations }
The preceding example demonstrates the importance of overriding appropriate Object methods to get expected behavior. If the recipient of your reference type calls one of the methods that are expecting appropriate behavior, it may cause problems. In the preceding example, you must override ToString.
28
Module 6: Working with Types
" Interfaces Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
!
Inheritance Considerations
!
Explicit Interface Implementation
When designing and using interfaces, you should consider a number of factors.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When designing and using interfaces, you should consider a number of factors. This section discusses how multiple inheritance works through interfaces. This section also explains how to explicitly implement interfaces.
Module 6: Working with Types
29
Inheritance Considerations Topic Objective
To explain the use of inheritance and the use of interfaces.
Lead-in
Interfaces are used only through inheritance.
!
Multiple Inheritance
interface interface IFoo IFoo interface interface IBar IBar class class MyObject MyObject !
{void {void DoSomething1();} DoSomething1();} {void {void DoSomething2();} DoSomething2();} :: IFoo, IFoo, IBar IBar {...} {...}
Deriving New Interfaces from Existing Ones
interface interface INewInterface INewInterface :: IFoo, IFoo, IBar IBar {void DoSomething3();} {void DoSomething3();} !
Base Class Interfaces
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Interfaces are used only through inheritance. There are several ways to group or partition functionality when you are using interfaces. You can use multiple inheritance to combine functionality from multiple interfaces. Interfaces also can inherit from other interfaces, allowing functionality to be partitioned into multiple groups.
Multiple Inheritance In the .NET Framework common language runtime, a class must inherit from only one class. However, a class can inherit from zero or more interfaces in addition to inheriting from one class. Interfaces are the only type to allow multiple inheritance in the common language runtime. The following example shows how a class can inherit from two interfaces. interface IFoo { void DoSomething1(); } interface IBar { void DoSomething2(); } class MyObject : IFoo, IBar { public void DoSomething1() { Console.WriteLine("DoSomething1 called"); } public void DoSomething2() { Console.WriteLine("DoSomething2 called"); } }
30
Module 6: Working with Types
Multiple inheritance is an important design consideration when you are choosing between abstract classes and interfaces. If two sets of functionality are designed into two abstract classes, a single class cannot inherit from both classes. However, if interfaces are used, a single class can inherit from both interfaces. If a specification of functions, properties, or events is likely to be used by many different kinds of classes, you should use an interface instead of an abstract class.
Deriving New Interfaces from Existing Ones You can also create new interfaces by deriving from other interfaces. The following example shows how a new interface that is called INewInterface is created from the IFoo and IBar interfaces. interface IFoo { void DoSomething1(); } interface IBar { void DoSomething2(); } interface INewInterface : IFoo, IBar { void DoSomething3(); } class MyObject : INewInterface { public void DoSomething1() { Console.WriteLine("DoSomething1 called"); } public void DoSomething2() { Console.WriteLine("DoSomething2 called"); } public void DoSomething3() { Console.WriteLine("DoSomething3 called"); } }
Module 6: Working with Types
31
Base Class Interfaces If you derive a new class from a base class that implements an interface, you must decide if the derived class will also implement the interface. When an instance of the derived class is converted to the interface, the inheritance hierarchy is searched until a class that implements the interface is found. Then the interface methods are called on that class. The following example shows what happens when interface methods are called on a derived class that does not implement the interface. interface IFoo { void DoSomething1(); } class Base : IFoo { public void DoSomething1() { Console.WriteLine("Base DoSomething1 called"); } } class Derived : Base { public new void DoSomething1() { Console.WriteLine("Derived DoSomething1 called"); } } class MainClass { public static void Main() { Derived d = new Derived(); IFoo i = (IFoo) d; d.DoSomething1(); i.DoSomething1(); } }
This code generates the following output: Derived DoSomething1 called Base DoSomething1 called
In the preceding example, the base class DoSomething1 method is called when an IFoo variable is used. If this behavior is not the desired behavior, then the derived class must be modified to also inherit from the IFoo interface. Then, the derived class DoSomething1 method will be called when the IFoo variable is used.
32
Module 6: Working with Types
Explicit Interface Implementation Topic Objective
To distinguish class accessibility and interface accessibility, and to explain when to use explicit interface implementation.
Lead-in
When you implement an interface in a class, the interface methods are exposed through the interface type, and the class type.
!
Class vs. Interface Accessibility
!
Use Explicit Interface Implementation #
When interface member names that are combined with class names are confusing
#
When inheriting from multiple interfaces with same members
interface interface IFoo IFoo {void {void DoSomething();} DoSomething();} interface IBar {void interface IBar {void DoSomething();} DoSomething();} class class MyObject MyObject :: IFoo, IFoo, IBar IBar {{ void IFoo.DoSomething() void IFoo.DoSomething() {Console.WriteLine("IFoo {Console.WriteLine("IFoo DoSomething DoSomething called");} called");} void void IBar.DoSomething() IBar.DoSomething() {Console.WriteLine("IBar {Console.WriteLine("IBar DoSomething DoSomething called");} called");} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When you implement an interface in a class, the interface methods are exposed through the interface type and the class type. The following example shows how an interface method can be called by both the class type and the interface type. interface IFoo { void DoSomething(); } class MyObject : IFoo { public void DoSomething() { Console.WriteLine("DoSomething called"); } } class MainClass { public static void Main() { MyObject o = new MyObject(); //Call method through class type o.DoSomething(); IFoo foo = (IFoo) o; //Call method through interface type foo.DoSomething(); } }
Module 6: Working with Types
33
You can use explicit interface implementation to prevent the class type from accessing the method. This technique is useful if the interface member names are likely to confuse a user of the class. Also, explicit interface implementation is mandatory if you inherit from multiple interfaces that have the same members. To use explicit interface implementation, you prepend the name of the interface to the name of the member in your class definition. You should not use any access modifiers in the member definition. Access modifiers are not allowed because the explicit interface implementation is only accessible through the interface type. The following example shows how explicit interface implementation can avoid conflicts between multiple interfaces with the same members. interface IFoo { void DoSomething(); } interface IBar { void DoSomething(); } class MyObject : IFoo, IBar { void IFoo.DoSomething() { Console.WriteLine("IFoo DoSomething called"); } void IBar.DoSomething() { Console.WriteLine("IBar DoSomething called"); } } class MainClass { public static void Main() { MyObject o = new MyObject(); IFoo foo = (IFoo) o; IBar bar = (IBar) o; foo.DoSomething(); bar.DoSomething(); } }
This code generates the following output: IFoo DoSomething called IBar DoSomething called
34
Module 6: Working with Types
" Managing External Types Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
!
Platform Invocation Services
!
COM Interoperability
The .NET Framework common language runtime is not an isolated system. Most programs will require some level of interaction with outside applications.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework common language runtime is not an isolated system. Most programs will require some level of interaction with outside applications. The common language runtime provides Platform Invocation Services for interacting with code in DLLs. Also, the common language runtime provides integration services to interact with COM objects. Warning Using unmanaged external types is risky because the common language runtime cannot enforce security, manage memory, or provide code type safety verification checking for unmanaged code. This module does not cover all aspects of Platform Invocation Services or COM Integration Services. For more information, see Module 15, “Interoperating Between Managed and Unmanaged Code,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) and “Interoperating with Unmanaged Code” in the .NET Framework SDK documentation.
Module 6: Working with Types
35
Platform Invocation Services Topic Objective
To introduce the use of Platform Invocation Services to call external functions.
Lead-in
Platform Invocation Services, also known as PInvoke, allow managed code to interact with unmanaged code in existing DLLs.
!
Also Known as PInvoke
!
Use DllImport Attribute to Import an API Function from an External DLL
[DllImport("user32.dll", [DllImport("user32.dll", CharSet=CharSet.Ansi)] CharSet=CharSet.Ansi)] public public static static extern extern int int MessageBox(int MessageBox(int h, h, string string m, m, string string c, c, int int type); type); public public static static void void Main() Main() {{ string pText string pText == "Hello "Hello World!"; World!"; string string pCaption pCaption == "PInvoke "PInvoke Test"; Test"; MessageBox(0, MessageBox(0, pText, pText, pCaption, pCaption, 0); 0); }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Platform Invocation Services, also known as PInvoke, allow managed code to interact with unmanaged code in existing DLLs. PInvoke provides the code that is needed to locate and invoke a function and to marshal parameters to and from the function. To call an external function, you use the DllImport attribute to provide the name of the DLL and any other characteristics, such as the use of ANSI or Unicode strings by the DLL. After the function has been declared, you can call it from managed code. The following example shows how to call the MessageBox API located in User32.dll. using System; using System.Runtime.InteropServices; class MainClass { [DllImport("user32.dll", CharSet=CharSet.Ansi)] public static extern int MessageBox(int h, string m, string c, int type); public static void Main() { string pText = "Hello World!"; string pCaption = "PInvoke Test"; MessageBox(0, pText, pCaption, 0); } }
For more information on PInvoke, see Module 15, “Interoperating Between Managed and Unmanaged Code,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) and “Interoperating with Unmanaged Code” in the .NET Framework SDK documentation.
36
Module 6: Working with Types
COM Interoperability Topic Objective
To introduce COM interoperability.
Lead-in
COM interoperability allows managed code to create and call COM objects. It also allows COM objects to create and call .NET Framework objects.
!
!
Exposing .NET Framework Classes to COM #
Must create COM Callable Wrapper
#
Create CCWs by using Tlbexp.exe
#
Register by using Regasm.exe
Exposing COM Classes to the .NET Framework #
Must create Runtime Callable Wrapper
#
Create RCWs by using Tlbimp.exe
*****************************ILLEGAL FOR NON-TRAINER USE****************************** COM interoperability allows managed code to create and call COM objects. It also allows COM objects to create and call .NET Framework objects.
Exposing .NET Framework Classes to COM You can expose .NET Framework classes to COM by creating COM callable wrappers (CCW) for each .NET Framework class in your application. The CCW creates and manages standard COM elements, such as IUknown and other standard interfaces, GUIDs, IIDs, and reference counting. These elements allow COM objects to interoperate with your .NET Framework classes as if the .NET Framework classes were real COM classes. The .NET Framework classes that you write do not need to be aware of any COM elements. However, you can directly control some COM elements when necessary, by such means as specifying manual IIDs for your interfaces. You can create CCWs for your .NET Framework classes by running the Type Library Exporter (Tlbexp.exe). You can also register your .NET Framework classes as COM classes by using the Assembly Registration Tool (Regasm.exe).
Module 6: Working with Types
Exposing COM Classes to the .NET Framework Managed code can also create and call existing COM objects. You can accomplish this by creating a runtime callable wrapper (RCW) for each COM class that you want to access from managed code. Similar to the CCW, the RCW handles COM specific semantics, such as reference counting, QueryInterface for different IIDs, and marshaling. Your managed code can use COM interfaces as if they were managed interfaces and COM classes as if they were managed classes. You can create RCWs by using the Type Library Importer (Tlbimp.exe). For more information on COM Interopability, see Module 15, “Interoperating Between Managed and Unmanaged Code,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) and “Interoperating with Unmanaged Code” in the .NET Framework SDK documentation.
37
38
Module 6: Working with Types
Lab 6: Working with Types Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create a class that overrides methods in System.Object. You will also create an interface that must be implemented explicitly.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: !
Create classes that override the GetHashCode, and equality methods and operators.
!
Create classes that provide conversion operators.
!
Implement an explicit interface in a class and use it from a client.
Lab Setup Starter and solution files are associated with this lab. The starter files are in the folder \Labs\Lab06\Starter, and the solution files are in the folder \Labs\Lab06\Solution.
Prerequisites Before working on this lab, you must have: !
Knowledge about how to use Microsoft Visual Studio® .NET for creating and working with console applications.
!
Knowledge about the C# language.
!
Knowledge about the System.Console namespace for interacting with the console.
Estimated time to complete this lab: 45 minutes
Module 6: Working with Types
39
Exercise 1 Overriding System.Object Methods In this exercise, you will create a structure that represents an office in a building. The Office structure stores the size, building number, and room number of an office. You will override appropriate methods and operators to support retrieving hash codes and comparing different offices. You will also create a structure called OfficeUnit that is similar to the Office structure. The only difference between the OfficeUnit structure and the Office structure is that OfficeUnit represents size as an enumerated type with the values small, medium, or large. The Office structure represents size as square footage. You will write an explicit conversion operator to convert values of type Office to type OfficeUnit.
! Implement a hash code 1. Open the Offices project located in the \Labs\ Lab06\Starter\Offices folder. 2. Open the Office.cs file. 3. Locate the Office structure. 4. Override the GetHashCode method. Generate and return a hash code by using the following algorithm: HashCode = Building * 10000 + Room
! Provide a string representation 1. Override the ToString method. 2. Generate and return a string that contains the building number, followed by a forward slash, followed by the room number. You can use the following code to build and return the string. return String.Concat(Building,"/",Room);
! Implement equality 1. Override the Equals method. 2. Cast the object parameter to type Office. 3. Compare the size, room, and building numbers of the parameter to this. If the values are all equal, return true. If they are not equal, return false. 4. Override the == and != operators. Implement them by calling the Equals method.
40
Module 6: Working with Types
! Implement conversion 1. Define an explicit conversion operator to convert from type Office to OfficeUnit. 2. Convert the size from square footage to an OfficeSizes value according to the following table. The OfficeSizes enumeration is already defined in the start code for you. Square Footage
OfficeSizes
64 and =100
Large
3. Assign the Building and Room values to the new type and return an instance of the new type.
! Test 1. Open the Main.cs file. 2. Create two offices called o1 and o2. Assign different sizes, room numbers, and building numbers to each office variable. 3. Use the == operator to compare the two offices. Print the results to the console. 4. Call GetHashCode on both offices and print the results. 5. Call ToString on both offices and print the results. 6. Create an OfficeUnit and assign it one of the offices. Use explicit conversion to assign the office. 7. Print all of the values of the OfficeUnit. 8. Compile and run the program. The two offices should not be equal. They should have different hash codes. The ToString method should print the building and room numbers, not the class names. Finally, the printed enumerated size should match the square footage appropriately.
Module 6: Working with Types
41
Exercise 2 Implementing an Explicit Interface In this exercise, you will create an interface called IRange, which provides properties and methods to select and print a range. You will also modify a class called TextEntry to inherit from IRange. This combination allows text in the TextEntry class to be selected as a range and printed. The TextEntry class already implements a Print method. Because the IRange interface also has a Print method, you will need to explicitly implement IRange in the TextEntry class to avoid a name conflict.
! Create an explicit interface 1. Open the Interfaces project located in the \Labs\ Lab06\Starter\Interfaces folder. 2. Open the TextEntry.cs file. 3. Define an interface called IRange that has the following properties. Property Name
Type
RangeBegin
uint
RangeEnd
uint
4. Define the following methods for IRange. Method Name
Return Type
Parameters
Select
void
uint rb, uint re
Print
void
none
5. Modify the TextEntry class to inherit from IRange. 6. In the TextEntry class, create private fields named rangeBegin and rangeEnd to store the RangeBegin and RangeEnd data values. Use the uint type for both fields. 7. Implement the IRange RangeBegin property. a. Implement the get accessor by returning the value of rangeBegin. b. Implement the set accessor by storing the value in rangeBegin. Check to ensure that the new value is less than the length of the text field, and that the value of rangeEnd is not less than rangeBegin. 8. Implement the IRange RangeEnd property. a. Implement the get accessor by returning the value of rangeEnd. b. Implement the set accessor by storing the value in rangeEnd. Check to ensure that the new value does not exceed the length of the text field, and that the value of rangeEnd is not less than rangeBegin. 9. Implement the IRange Select method. Ensure that the re parameter is not less than the rb parameter, and that the new values are not greater than the length of the text field. Assign rb to rangeBegin and re to rangeEnd. 10. Implement the IRange Print method. Using the rangeBegin and rangeEnd fields, create a text range from the text field by using the SubString method. Then print the resulting string to the console.
42
Module 6: Working with Types
! Use the interface 1. Open the Main.cs file. 2. In the MainClass class, create an instance of the TextEntry class called myEntry. 3. Create an interface of type IRange named r and cast myEntry to r. 4. Initialize the Text property on myEntry with a value, such as “The quick brown fox jumped over the gate.” 5. Select a range by using the interface variable r. For example, the values 4 and 10 will select the word “quick.” 6. Call the Print method on myEntry. 7. Call the Print method on r. 8. Compile and run the program. You should see the entire Text property printed to the console, followed by the range of text you selected.
Module 6: Working with Types
43
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
System.Object Class Functionality
!
Specialized Constructors
!
Type Operations
!
Interfaces
!
Managing External Types
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. What kind of algorithm should you use to implement the GetHashCode method? The algorithm should be efficient and return a unique number.
2. What is the default behavior of the ToString method? The default behavior of the ToString method is to return the name of the class.
3. How can you determine if two separate object references are the same object? Use the ReferenceEquals method to compare the two references.
4. If you override the Equals method, what else should you override? You should override the == and != operators.
5. When should you use a private constructor? Use a private constructor to prevent a class from being instantiated. An example of such a class is a singleton class with static methods and fields.
44
Module 6: Working with Types
6. When should you use implicit conversions, and when should you use explicit conversions? Use implicit conversions for improved readability and use. Use explicit conversions when the conversion could cause a loss of data or throw an exception.
7. When do boxing operations occur? Boxing occurs when a value type is converted to an Object type. Unboxing occurs when the value is retrieved from an Object type.
8. How do you explicitly implement an interface? Do not use the public access modifier. Use the name of the interface in front of the field or method names. For example, void IFoo.DoSomething().
Module 7: Strings, Arrays, and Collections Contents Overview Strings Terminology – Collections
1 2 20
.NET Framework Arrays
21
.NET Framework Collections
39
Lab 7: Working with Strings, Enumerators, and Collections 57 Review
63
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 7: Strings, Arrays, and Collections
iii
Instructor Notes Presentation: 120 Minutes Lab: 60 Minutes
After completing this module, students will be able to: !
Parse, format, manipulate, and compare strings.
!
Use the classes in the System.Array and System.Collections namespaces.
!
Improve the type safety and performance of collections by using specialized collections and class-specific code.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft® PowerPoint® file 2349B_07.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Practice the demonstrations.
!
Complete the lab.
iv
Module 7: Strings, Arrays, and Collections
Demonstrations This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for the student notes.
Sorting and Enumerating an Array In this demonstration, you will show students how to sort and enumerate an array. The code for this demonstration is contained in one project and is located in \Democode\Mod07\Demo07.1. In addition, the code for the individual demonstration is provided in the student notes.
ArrayList In this demonstration, you will show students how ArrayList implements the IList interface by using an array whose size is dynamically increased as required. The code for this demonstration is contained in one project and is located in \Democode\Mod07\Demo07.2. In addition, the code for the individual demonstration is provided in the student notes.
Hashtable In this demonstration, you will show students how to create a hash table that is used for searches. The code for this demonstration is contained in one project and is located in \Democode\Mod07\Demo07.3. In addition, the code for the individual demonstration is provided in the student notes. In all of the preceding demonstrations, use the debugger to step through the code while you point out features.
Module 7: Strings, Arrays, and Collections
Module Strategy Use the following strategy to present this module: !
Strings Discuss how to work with strings in the Microsoft .NET Framework, including common operations, such as parsing, formatting, manipulating, and comparing strings.
!
Terminology – Collections Define the term collection as it is used in this module and identify where collections are found in the .NET Framework. Be sure that students understand that the term collection is used in its broader sense: to describe a group of items.
!
.NET Framework Arrays Introduce the System.Array class as the base class of all array types that contains methods for creating, manipulating, searching, and sorting arrays. Discuss features of arrays that are specific to C#. Explain the role of the IEnumerable and IEnumerator interfaces in System.Array and System.Collections classes. Use the Sorting and Enumerating an Array demonstration to show how to sort and enumerate an array.
!
.NET Framework Collections Briefly introduce some commonly used classes in the System.Collections namespace. Discuss the IList interface with regards to classes that represent an ordered collection of objects that can be individually indexed. Use the ArrayList demonstration to reinforce this concept. Discuss the IDictionary interface and the classes that it implements. Use the Hashtable demonstration to show how to use the IDictionary interface. Provide guidelines to help students distinguish between collections and arrays, and explain when collections are used. Discuss runtime casting for type safety and the effects of runtime casting, and boxing and unboxing on performance. Discuss techniques for handling boxing and unboxing to optimize performance.
v
Module 7: Strings, Arrays, and Collections
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn about some of the key classes in the .NET Framework class library.
!
Strings
!
Terminology – Collections
!
.NET Framework Arrays
!
.NET Framework Collections
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this module, you will learn about some of the key classes in the Microsoft® .NET Framework class library. Specifically, you will learn how to work with strings, arrays, collections, and enumerators. After completing this module, you will be able to: !
Parse, format, manipulate, and compare strings.
!
Use the classes in the System.Array and System.Collections namespaces.
!
Improve the type safety and performance of collections by using specialized collections and class-specific code.
2
Module 7: Strings, Arrays, and Collections
" Strings Topic Objective
To introduce the topics in the section.
!
Parse
Lead-in
!
Format
!
Format Examples
!
Changing Case
!
Compare
!
Trim and Pad
!
Split and Join
!
StringBuilder
!
C# Specifics
!
Regular Expressions
In this section, you will learn how to work with strings in the .NET Framework.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In the C# language, string is an alias for System.String in the .NET Framework. The System.String type represents a string of Unicode characters. Working with strings is an everyday task in software development, and includes operations, such as parsing, formatting, manipulating, and comparing strings. The String object is immutable. Therefore, every time you use one of the methods in the System.String class, you create a new string object. When you want to perform repeated modifications to a string, the overhead that is associated with creating a new String object can be costly. As an alternative, you can use the System.Text.StringBuilder class to modify a string without creating a new object. In this section, you will learn how to work with strings in the .NET Framework.
Module 7: Strings, Arrays, and Collections
3
Parse Topic Objective
!
To explain how the Parse method is used to convert numeric strings to a .NET Framework numeric base type.
Parse Method Converts a Numeric String to a Numeric
string string MyString MyString == "12345"; "12345"; int MyInt int MyInt == int.Parse(MyString); int.Parse(MyString); MyInt++; MyInt++; Console.WriteLine(MyInt); Console.WriteLine(MyInt); // // The The output output to to the the console console is is "12346". "12346".
Lead-in
The Parse method converts a string that represents a .NET Framework numeric base type to an actual .NET Framework numeric base type.
!
To Ignore Commas, Use the NumberStyles.AllowThousands Flag
string string MyString MyString == "123,456"; "123,456"; int MyInt int MyInt == int.Parse(MyString, int.Parse(MyString, System.Globalization.NumberStyles.AllowThousands); System.Globalization.NumberStyles.AllowThousands); Console.WriteLine(MyInt); Console.WriteLine(MyInt); // // The The output output to to the the console console is is "123456". "123456".
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The Parse method converts a string that represents a .NET Framework numeric base type to an actual .NET Framework numeric base type. The Parse method takes a combination of up to three parameters, as follows: !
The string to be converted
!
One or more values from the System.Globalization.NumberStyles enumeration
!
A NumberFormatInfo class
Because the Parse method assumes that all string input represents a base-10 value, non-base-10 values are not parsable. The Parse method also does not parse strings that represent the values NaN (Not A Number), PositiveInfinity, or NegativeInfinity of the Single and Double classes because they are not real numbers. The following code example converts a string to an int value, increments that value, and displays the result: string MyString = "12345"; int MyInt = int.Parse(MyString); MyInt++; Console.WriteLine(MyInt); // The output to the console is "12346".
4
Module 7: Strings, Arrays, and Collections
Handling Nonnumeric Characters The NumberStyles enumeration is useful if you have a string that contains nonnumeric characters that you want to convert into a .NET Framework numeric base type. You must use this enumeration to parse a string with a currency symbol, decimal point, exponent, parentheses, and so on. For example, a string that contains a comma cannot be converted to an int value by using the Parse method unless you pass the System.Globalization.NumberStyles enumeration.
Incorrect Way to Parse a String with Nonnumeric Characters The following code example is invalid and raises an exception. It illustrates the incorrect way to parse a string that contains nonnumeric characters. string MyString = "123,456"; // the following line raises a System.Format exception int MyInt = int.Parse(MyString); Console.WriteLine(MyInt);
Correct Way to Parse a String with Nonnumeric Characters When you apply the System.Globalization.NumberStyles enumeration with the AllowThousands flag, the Parse method ignores the comma that raised the exception in the preceding example. The following code example uses the same string as the preceding example but does not raise an exception. string MyString = "123,456"; int MyInt = int.Parse(MyString, System.Globalization.NumberStyles.AllowThousands); Console.WriteLine(MyInt); // The output to the console is "123456"
Module 7: Strings, Arrays, and Collections
5
Format Topic Objective
To explain how to use format strings, or specifiers, to format the appearance of your application.
Lead-in
The .NET Framework provides several format strings that you can use to format the appearance of strings that derive from other objects.
!
Format Strings Are Used in Methods That Create String Representations of a .NET Framework Data Type #
To display $100.00 to the console on computers on which U.S. English is the current culture
int int MyInt MyInt == 100; 100; string string MyString MyString == MyInt.ToString("C"); MyInt.ToString("C"); Console.WriteLine(MyString); Console.WriteLine(MyString); #
Alternatively
int int MyInt MyInt == 100; 100; Console.WriteLine("{0:C}", Console.WriteLine("{0:C}", MyInt); MyInt);
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework provides several format strings, or specifiers, that you can use to format the appearance of strings that derive from other objects. There are several advantages to converting base data types to strings before displaying them to users. Strings are easily displayed and can be appended to the messages and dialog boxes of your application. You can also use format specifiers to display the same numeric value in scientific format, monetary format, hexadecimal format, and so on.
When to Use Format Strings You can use format specifiers in situations where your application stores information in a format that is designed for use by the application, and not by the user. For example, a business application may keep track of the current date and time in a DateTime object to log when transactions are completed. The DateTime object stores information in which the user is not necessarily interested, such as the number of milliseconds that have elapsed since the creation of the object. You can also use format specifiers to display only information that is of interest to the user, such as the date and hour of the transaction. Additionally, you can dynamically modify strings that are created by using format specifiers to represent monetary, date, and time conventions for the current culture. For example, your application can display the date and time in the notation that is specific to the user’s current culture.
6
Module 7: Strings, Arrays, and Collections
Methods Used with Format Strings Format strings are used with any method that creates a string representation of a .NET Framework data type, such as Int32, Int64, Single, Double, Enumeration, DateTime, and so on. Format strings are also used with Console.Writeline, String.Format, and several methods in the System.IO namespace. Additionally, every base data type contains a ToString method that returns a string representation of the data type’s value and accepts a string format specifier. You can control the layout and design of strings that are created by any of these methods by using one of several format strings defined by the .NET Framework.
Using the ToString Method The ToString method is useful if you want to convert one of the standard .NET Framework data types to a string that represents that type in some other format. For example, if you have an integer value of 100 that you want to represent to the user as a currency value, you can easily use the ToString method and the currency format string ("C") to produce a string of "$100.00". The original value that is contained in the data type is not converted, but a new string is returned that represents the resulting value. This new string cannot be used for calculation until it is converted back to a .NET base data type. The original value, however, can be calculated at any time. Note Computers that do not have U.S. English specified as the current culture will display whatever currency notation is used by the current culture. In the following code example, the ToString method displays the value of 100 as a currency-formatted string in a console window: int MyInt = 100; string MyString = MyInt.ToString("C"); Console.WriteLine(MyString);
The preceding code displays $100.00 to the console on a computer on which U.S. English is the current culture.
Using Console.Writeline The Console.WriteLine method also accepts a format string specifier as an argument and can produce the same value as the preceding example. Console.Writeline accepts string format specifiers in the form, where the characters inside the curly brackets specify the formatting to apply to the variable. The following code example uses the Console.WriteLine method to format the value of MyInt to a currency value. int MyInt = 100; Console.WriteLine("{0:C}", MyInt);
In the preceding example, the 0 character specifies the variable or value on which to apply formatting. In this example, it is the first and only variable. The characters that follow the colon are interpreted as string format specifiers.
Module 7: Strings, Arrays, and Collections
7
Format Examples Topic Objective
To provide examples of format strings that return common numeric string types.
!
Currency Format #
C - $XX,XXX.XX
int int MyInt MyInt == 12345; 12345; string string MyString MyString == MyInt.ToString("C" MyInt.ToString("C" ); ); // // In In the the U.S. U.S. English English culture: culture: "$12,345.00" "$12,345.00"
Lead-in
Let’s look at some examples of format strings that return the value of MyString in the currency and date time formats.
!
Date Time Format #
D - dd MMMM yyyy
#
d - MM/dd/yyyy
DateTime DateTime MyDate MyDate == new new DateTime(2000, DateTime(2000, 1, 1, 10, 10, 0, 0, 0, 0, 0); 0); string string MyString MyString == MyDate.ToString( MyDate.ToString( "d" "d" ); ); // // In In the the U.S. U.S. English English culture: culture: "1/10/2000" "1/10/2000"
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The standard numeric, picture numeric, date and time, and enumeration format strings are described in detail in the .NET Framework Software Development Kit (SDK). This topic provides examples of these format strings. The following examples show the use of the format string that returns the value of MyInt in the currency format: int MyInt = 12345; string MyString = MyInt.ToString( "C" ); // In the U.S. English culture, MyString has the value: // "$12,345.00".
The following table lists some format characters for standard patterns that are used to format DateTime types. Format Character
Associated Property/Description
Example Format Pattern (en-US)
d
ShortDate Pattern
MM/dd/yyyy
D
LongDatePattern
Dd MMMM yyyy
f
Full date and time (long date and short time)
Dd MMMM yyyy
FullDateTimePattern (long date and long time)
Dd MMMM yyyy
F
HH:mm HH:mm:ss
8
Module 7: Strings, Arrays, and Collections
The following example shows the use of the format string that returns the value of MyDate in the short date pattern format: DateTime MyDate = new DateTime(2000, 1, 10, 0, 0, 0); string MyString = MyDate.ToString( "d" ); // In the U.S. English culture, MyString has the value: // "1/10/2000".
Module 7: Strings, Arrays, and Collections
9
Changing Case Topic Objective
To explain how to use the String.ToUpper and String.ToLower methods to change the case of a string.
Lead-in
You can easily change the case of a string by using the String.ToUpper and String.ToLower methods.
!
You Can Easily Change the Case of a String #
String.ToUpper – converts to upper case
string string MyString MyString == "hello "hello world!"; world!"; // // outputs: outputs: HELLO HELLO WORLD! WORLD! Console.WriteLine(MyString.ToUpper()); Console.WriteLine(MyString.ToUpper()); #
String.ToLower – converts to lower case
string string MyString MyString == "HELLO "HELLO WORLD!"; WORLD!"; // outputs: hello world! // outputs: hello world! Console.WriteLine(MyString.ToLower()); Console.WriteLine(MyString.ToLower());
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can easily change the case of a string by using the two methods that are described in the following table. Method Name
Use
String.ToUpper
Converts all characters in a string to uppercase.
String.ToLower
Converts all characters in a string to lowercase.
String.ToUpper and String.ToLower provide an override that accepts a culture. The String.ToUpper method changes all of the characters in a string to uppercase. The following code example converts the string "hello world!" from lowercase to uppercase: string MyString = "hello world!"; Console.WriteLine(MyString.ToUpper());
The preceding example displays HELLO WORLD! to the console. The String.ToLower method is similar to the String.ToUpper method, but it converts all of the characters in a string to lowercase. The following code example converts the string "HELLO WORLD!" to lowercase. string MyString = "HELLO WORLD!"; Console.WriteLine(MyString.ToLower());
The preceding example displays hello world! to the console.
10
Module 7: Strings, Arrays, and Collections
Compare Topic Objective
To introduce some of the value-comparison methods that are used to compare the values of strings.
!
The .NET Framework Has Methods to Compare Strings #
Lead-in
The .NET Framework provides several valuecomparison methods to compare the values of strings.
For example, the Compare method compares the current string object to another string or object, returning: - Negative if first string is less than second string - 0 if the two strings are equal - Positive if first string is greater than second string
string string MyString MyString == "Hello "Hello World!"; World!"; Console.WriteLine( Console.WriteLine( String.Compare(MyString,"Hello String.Compare(MyString,"Hello World!")); World!")); // // outputs: outputs: 00
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework provides several methods to compare the values of strings. The following table describes some of the value-comparison methods. Method Name
Use
String.Compare
Compares the values of two strings. Returns an integer value.
String.StartsWith
Determines if a string begins with the string passed. Returns a boolean value.
String.IndexOf
Returns the index of the first occurrence of a String, or one or more characters, within this instance.
For more information about value comparison methods and for a complete list of these methods, see “Comparing Strings” in the .NET Framework SDK documentation. For example, the String.Compare method provides a thorough way to compare the current string object to another string or object. You can use this function to compare two strings or substrings of two strings. Additionally, overloads are provided that regard or disregard case and cultural variance.
Module 7: Strings, Arrays, and Collections
The following table shows the three integer values that are returned by the Compare(string strA, string strB) method. Value Type
Condition
A negative integer
strA is less than strB
0
strA equals strB
A positive integer
strA is greater than strB
The following code example uses the Compare method to determine whether two strings are the same. string MyString = "Hello World!"; Console.WriteLine(String.Compare(MyString, "Hello World!"));
The preceding example displays 0 to the console.
11
12
Module 7: Strings, Arrays, and Collections
Trim and Pad Topic Objective
To explain how to use methods of the System.String class to trim and pad strings.
Lead-in
You can remove or extend the spaces around strings by using methods of the System.String class.
!
Trim Methods Remove Spaces
string string MyString MyString == "" Big Big "; "; Console.WriteLine("Hello{0}World!", Console.WriteLine("Hello{0}World!", MyString MyString ); ); string TrimString = MyString.Trim(); string TrimString = MyString.Trim(); Console.WriteLine("Hello{0}World!", Console.WriteLine("Hello{0}World!", TrimString TrimString ); ); // outputs the following lines to the // outputs the following lines to the console: console: //Hello //Hello Big Big World! World! //HelloBigWorld! //HelloBigWorld! !
Pad Methods Expand a Specific Number of Characters
string string MyString MyString == "Hello "Hello World!"; World!"; Console.WriteLine(MyString.PadLeft(20, Console.WriteLine(MyString.PadLeft(20, '-')); '-')); // // outputs outputs the the following following line line to to the the console: console: //--------Hello //--------Hello World! World! to to the the console. console.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When you want to remove or extend the spaces around strings, the System.String class provides methods for trimming and padding strings.
Trimming When you are parsing a sentence into individual words, you might have white spaces on either end of a word. You can use one of the trim methods in the System.String class to remove any number of spaces from the beginning or end of the string. The following table describes two of the available trim methods. Method Name
Use
String.Trim
Removes white spaces from the beginning and end of a string.
String.Remove
Removes a specified number of characters from a specified index position in a string.
For example, you can easily remove white spaces from both ends of a string by using the String.Trim method, as shown in the following code example. string MyString = " Big "; Console.WriteLine("Hello{0}World!", MyString ); string TrimString = MyString.Trim(); Console.WriteLine("Hello{0}World!", TrimString );
This code outputs the following lines to the console: Hello Big World! HelloBigWorld!
For a complete list of trim methods in the System.String class, see “Trimming and Removing Characters” in the .NET Framework SDK documentation.
Module 7: Strings, Arrays, and Collections
13
Padding System.String also provides methods that you can use to create a new version of an existing string that is expanded by a specific number of characters. The following table describes the available pad methods. Method Name
Use
String.PadLeft
Right aligns and pads a string, so its rightmost character is a specified distance from the beginning of the string.
String.PadRight
Left aligns and pads a string, so its rightmost character is a specified distance from the beginning of the string.
For example, the String.PadLeft method creates a new string that moves an existing string to the right, so its last character is a specified number of spaces from the first index of the string. White spaces are inserted if you do not use an override that allows you to specify your own custom padding character. The following code example uses the PadLeft method to create a new string with a total length of 20 spaces. string MyString = "Hello World!"; Console.WriteLine(MyString.PadLeft(20, '-'));
This example displays the following text to the console. --------Hello World!
14
Module 7: Strings, Arrays, and Collections
Split and Join Topic Objective
To explain how to use the Split and Join methods to break up and concatenate strings.
!
#
Lead-in
The System.String class provides the Split method to break up strings and the Join method to concatenate strings.
Split Method Is Used to Break Up a String Into an Array of Substrings
#
String is broken at positions indicated by the specified separator characters parameter If the separator parameter is null, the whitespace characters are assumed to be the separator
string string Line Line == "Hello "Hello World"; World"; string[] Words string[] Words == Line.Split(null); Line.Split(null); // // Words[0] Words[0] == "Hello" "Hello" and and Words[1] Words[1] == "World" "World" !
Join Method Is Used to Concatenate Strings #
A specified separator string is placed between each element of a string array
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The System.String class provides the Split method to break up strings and the Join method to concatenate strings.
The Split Method For Your Information
You should cover the String.Split method because it is used in the lab.
You use the Split method to break up a string instance into an array of substrings at the positions that are specified by separator characters. If separator characters are omitted, that is to say, if the parameter is null, the whitespace characters are assumed to be the separator. If the separator is a zero-length string, a single-element array that contains the entire expression string is returned. The following example shows how to break up a string into a string array of words: string Line = "Hello World"; string[] Words = Line.Split(null); // Words[0] = "Hello" and Words[1] = "World"
The Join Method You use the Join method to concatenate a specified separator string between each element of a specified String array, which yields a single concatenated string. If the separator is omitted, that is to say null, the space character (" ") is used. If the separator is a zero-length string (""), all of the items in the list are concatenated with no delimiters.
Module 7: Strings, Arrays, and Collections
15
StringBuilder Topic Objective
To explain how to use the StringBuilder method to modify a string without creating a new object.
Lead-in
When you want to perform repeated modifications to a string, use the System.Text.StringBuilder class to modify a string without creating a new object.
!
The String Object is Immutable
!
System.Text.StringBuilder – Allows You to Modify a String Without Creating a New Object
StringBuilder StringBuilder MyStringBuilder MyStringBuilder == new new StringBuilder("Hello"); StringBuilder("Hello"); !
You Can Specify the Maximum Number of Characters
// // MyStringBuilder MyStringBuilder can can hold hold aa maximum maximum of of 25 25 characters characters StringBuilder StringBuilder MyStringBuilder MyStringBuilder == new new StringBuilder("Hello StringBuilder("Hello World!", World!", 25); 25); !
Methods Include: #
Append, AppendFormat, Insert, Remove, and Replace
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The String object is immutable. Therefore, every time you use one of the methods in the System.String class, you create a new string object. When you want to perform repeated modifications to a string, the overhead that is associated with creating a new String object can be costly. As an alternative, you can use the System.Text.StringBuilder class to modify a string without creating a new object.
Creating a StringBuilder Object You can create a new instance of the StringBuilder object by initializing your variable with one of the overloaded constructor methods, as shown in the following code example: StringBuilder MyStringBuilder = new StringBuilder("Hello");
Although the StringBuilder object is a dynamic object that allows you to expand the number of characters in the string that it encapsulates, you can specify a value for the maximum number of characters that it can hold. This value is called the capacity of the object and must not be confused with the length of the string that the current StringBuilder object holds. Any attempt to expand the StringBuilder class beyond the maximum range causes an ArgumentOutOfRangeException to be thrown. The following code example specifies that the MyStringBuilder object can be expanded to a maximum of 25 spaces. StringBuilder MyStringBuilder = new StringBuilder("Hello World!", 25);
16
Module 7: Strings, Arrays, and Collections
StringBuilder Methods The following table describes the methods that you can use to modify the contents of the StringBuilder object. Method Name
Use
StringBuilder.Append
Appends information to the end of the current StringBuilder object.
StringBuilder.AppendFormat
Replaces zero or more format specifications with the appropriately formatted value of an object.
StringBuilder.Insert
Inserts a string or object into the specified index of the current StringBuilder object.
StringBuilder.Remove
Removes a specified number of indexes from the current StringBuilder object.
StringBuilder.Replace
Replaces a specified index or character with the passed character.
Module 7: Strings, Arrays, and Collections
17
C# Specifics Topic Objective
To explain what the string type is in the .NET Framework and to describe the functions of the +, [ ], and != operators, and @quoting.
Lead-in
The string type represents a string of Unicode characters; string is an alias for System.String in the .NET Framework.
!
C# string Type Is a String of Unicode Characters # #
#
Alias for System.String Equality operators (== and !=) compare the values of string objects, not references The + operator concatenates strings
string string aa == "\u0068ello "\u0068ello "; "; string string bb == "world"; "world"; Console.WriteLine( Console.WriteLine( aa ++ bb == == "hello "hello world" world" );//True );//True #
The [ ] operator accesses individual characters of a string
char char xx == "test"[2]; "test"[2]; // // xx == 's'; 's'; #
With @-quoting, escape sequences are not processed
@"c:\Docs\Source\a.txt" @"c:\Docs\Source\a.txt" // // rather rather than than "c:\\Docs\\Source\\a.txt" "c:\\Docs\\Source\\a.txt"
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The string type represents a string of Unicode characters; string is an alias for System.String in the .NET Framework. Although string is a reference type, the equality (==) operator and the inequality (!=) operator are defined to compare the values of string objects, not the references. Comparing the values of string objects makes testing for string equality more intuitive, as in the following example: string a = "hello"; string b = "hello"; Console.WriteLine( a == b );
// output: True -- same value
The + operator concatenates strings, as in the following example: string a = "good " + "morning";
The [ ] operator accesses individual characters of a string, as in the following example: char x = "test"[2];
// x = 's';
String literals are of type string and can be written in two forms: quoted and @-quoted. Quoted string literals are enclosed in quotation marks ("), as in the following example: "good morning"
// a string literal
Quoted string literals can also contain any character literal, including escape sequences, as in the following example: string a = "\\\u0066\n";
// backslash, letter f, new line
18
Module 7: Strings, Arrays, and Collections
@-quoted string literals start with @ and are enclosed in quotation marks, as in the following example: @"good morning"
// a string literal
The advantage of using @-quoted string literals is that escape sequences are not processed. This makes it easy to write a fully qualified file name, as in the following example: @"c:\Docs\Source\a.txt" // rather than "c:\\Docs\\Source\\a.txt"
To include a quoted phrase in an @-quoted string, use two pairs of double quotation marks, as in the following example: @"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.
The following code example uses the C# features that are discussed in this topic: using System; class test { public static void Main( String[] args ) { string a = "\u0068ello "; string b = "world"; Console.WriteLine( a + b ); Console.WriteLine( a + b == "hello world" ); } }
The preceding code example displays the following output to the console: hello world True
Module 7: Strings, Arrays, and Collections
19
Regular Expressions Topic Objective
To briefly describe how regular expressions can be used in the .NET Framework.
!
Regular Expressions – Powerful Text Processing
!
Pattern-Matching Notation Allows You to:
Lead-in
Regular expressions allow you to quickly parse large amounts of text in order to find specific character patterns; to extract, edit, replace, or delete text substrings; and to add the extracted strings to a collection to generate a report.
!
#
Find specific character patterns
#
Extract, edit, replace, or delete text substrings
#
Add the extracted strings to a collection to generate a report
Designed to be Compatible With Perl 5
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This topic provides a brief summary of regular expressions. Do not spend much time on this slide, but encourage students to refer to the .NET Framework SDK, especially for details about using the regular expression classes.
Regular expressions provide a powerful, flexible, and efficient method to process text. The extensive pattern-matching notation of regular expressions allows you to quickly parse large amounts of text to find specific character patterns; to extract, edit, replace, or delete text substrings; and to add the extracted strings to a collection in order to generate a report. For many applications that deal with strings, such as HTML processing, log file parsing, and HTTP header parsing, regular expressions are an essential tool. The .NET Framework regular expressions incorporate the most popular features of other regular expression implementations, such as those used in Perl and awk. Designed to be compatible with Perl 5 regular expressions, .NET Framework regular expressions include features that are not yet available in other implementations, such as right-to-left matching and dynamic compilation. The .NET Framework regular expression classes are part of the .NET Framework class library and can be used with any language or tool that targets the common language runtime, including ASP.NET and Microsoft Visual Studio® .NET. A detailed explanation of how to use the regular expression classes is beyond the scope of this course. For more information about using regular expression classes, see the .NET Framework SDK documentation.
20
Module 7: Strings, Arrays, and Collections
Terminology – Collections Topic Objective
To define the term collection as it is used in this module and to identify where collections are found in the .NET Framework.
Lead-in
In this module, the term collection is used in its broader sense: to describe a group of items.
!
In This Module, the Term Collection Is Used in Its Broader Sense to Refer to a Group of Items
!
In the .NET Framework, Collections Are Found in the Namespaces #
System.Array
#
System.Collections
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Do not spend much time on this slide. The objective of this slide is to define the term collection clearly, before discussing the collections that are found in the .NET Framework.
In this module, the term collection is used in its broader sense to refer to a group of items. In the .NET Framework, examples of collections are found in the namespaces System.Array, and System.Collections.
Module 7: Strings, Arrays, and Collections
21
" .NET Framework Arrays Topic Objective
To introduce the topics in the section.
Lead-in
The System.Array class is the base class of all array types and contains methods for creating, manipulating, searching, and sorting arrays.
!
System.Array
!
C# Specifics
!
Iterating Over
!
Comparing
!
Sorting
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The System.Array class is the base class of all array types and contains methods for creating, manipulating, searching, and sorting arrays. The Array class is not part of the Collections namespace. However, it is still a collection because it is based on the IList interface. In an array style of collection, an item in the collection is referred to by the term element. Specific elements are identified by their array index. The low bound or lower bound of an Array is the index of its first element. To understand the functionality of the various collection classes, you must understand their key interfaces. In this section, you will learn about the interfaces that are used by the methods of System.Array.
22
Module 7: Strings, Arrays, and Collections
System.Array Topic Objective
To explain how array types are used and defined.
!
System.Array Is the Base Class of All Array Types
Lead-in
!
Arrays Implement the Following Interfaces
The System.Array class is the base class of all array types and contains methods for creating, manipulating, searching, and sorting arrays.
#
!
System.Array Has Methods For #
!
ICloneable, IList, ICollection, and IEnumerable Creating, manipulating, searching, and sorting
Null, Empty String, and Empty (0 item) Arrays Should Be Treated the Same #
Therefore, return an Empty array, instead of a null reference
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The System.Array class is the base class of all array types and contains methods for creating, manipulating, searching, and sorting arrays. Arrays are always allocated on the garbage-collected heap. Arrays can be single dimensional or multidimensional. You can also create arrays of arrays, called jagged arrays. For optimum performance, it is highly recommended that each rank of an array be zero-based. Additionally, each rank of an array must be zero-based when passing arrays between programming languages.
Defining an Array Type You define an array type by specifying the element type of the array, the rank, or number of dimensions, of the array, and the upper and lower bounds of each dimension of the array. All of these details are included in any signature of an array type. The runtime automatically creates exact array types as they are required. No separate definition of the array type is needed. Arrays of a particular type can only hold elements of that type. If you need to manipulate a set of unlike objects or value types, consider using one of the collection types that are defined in the System.Collections namespace.
Module 7: Strings, Arrays, and Collections
23
Methods and Properties of System.Array Some of the methods and properties for the System.Array class are described in this topic. Many of these methods are used to implement the class’s interfaces. System.Array implements the interfaces that are described in the following table. Interface
Use
ICloneable
Supports cloning, which creates a new instance of a class with the same value as an existing instance.
IList
Represents a collection of objects that can be individually indexed.
ICollection
Defines size, enumerators, and synchronization methods for all collections.
IEnumerable
Exposes the enumerator, which supports a simple iteration over a collection.
The interfaces that are listed in the preceding table are supported not only by arrays but also by many of the System.Collections classes. Subsequent topics in this module discuss specific interfaces and the classes that implement them. The following tables describe some of the public members that are available through the System.Array class. Static Method
Use
BinarySearch
Overloaded. Searches a one-dimensional sorted Array for a value, using a binary search algorithm.
CreateInstance
Overloaded. Initializes a new instance of the Array class.
Sort
Overloaded. Sorts the elements in one-dimensional Array objects.
Property
Use
IsFixedSize
Gets a value that indicates whether the Array has a fixed size.
IsReadOnly
Gets a value that indicates whether the Array is read-only.
Length
Gets the total number of elements in all of the dimensions of the Array.
Rank
Gets the rank (number of dimensions) of the Array.
Note The IsFixedSize and IsReadOnly properties are always false unless they are overridden by a derived class.
24
Module 7: Strings, Arrays, and Collections
The following table describes some of the public instance methods that are available through the System.Array class. Instance Method
Use
Clone
Creates a shallow copy of the Array.
GetEnumerator
Returns an IEnumerator for the Array.
GetLength
Gets the number of elements in the specified dimension of the Array.
GetLowerBound
Gets the lower bound of the specified dimension in the Array.
GetUpperBound
Gets the upper bound of the specified dimension in the Array.
GetValue
Overloaded. Gets the values of the Array elements at the specified indexes.
SetValue
Overloaded. Sets the specified Array elements to the specified value.
For complete lists of public members of the System.Array class, see “Array Members” in the .NET Framework SDK documentation.
Empty Arrays Nulls should only be returned by reference properties that refer to another object or component. String and Array properties should never return null, because a programmer typically does not expect null in this context. For example, a programmer typically would assume that the following code works: public void DoSomething(…) { string[] sa = SomeOtherFunc(); // The following line assumes sa is never null if (sa.Length > 0) { // do something else } }
Generally, null, empty string, and empty (0 item) arrays should be treated in the same way. Therefore, return an Empty array, instead of a null reference.
Module 7: Strings, Arrays, and Collections
25
C# Specifics Topic Objective
To explain features that are unique to C# arrays.
Lead-in
C# arrays are similar to arrays in most other popular languages, but you should be aware of the differences that are presented in this topic.
!
C# Array Indexes Start at Zero
!
Declaring an Array – Size Is Not Part of Its Type
int[] int[] numbers; numbers; // // declare declare numbers numbers as as // an int array // an int array of of any any size size !
Creating an Array
int[] int[] numbers numbers == new new int[5]; int[5]; // // declare declare and and create create !
Initializing an Array
int[] int[] numbers numbers == new new int[5] int[5] {1, {1, 2, 2, 3, 3, 4, 4, 5}; 5}; !
Using System.Array Members
int[] int[] numbers numbers == {1, {1, int LengthOfNumbers int LengthOfNumbers
2, 2, 3, 3, 4, 4, 5}; 5}; == numbers.Length; numbers.Length;
*****************************ILLEGAL FOR NON-TRAINER USE****************************** C# arrays are zero-indexed. That means the array indexes start at zero. Arrays in C# and arrays in most other popular languages work similarly, but there are a few differences that you should be aware of. When declaring an array, the brackets ([]) must come after the type, and not after the identifier. Placing the brackets after the identifier is not legal syntax in C#, as shown in the following example: int[] table; // not int table[];
In addition, and unlike the C language, the size of the array is not part of its type. This allows you to declare and assign to an array any array of int objects, regardless of the array’s length, as in the following examples. int[] numbers; // declare numbers as an int array of any size numbers = new int[10];
// numbers is a 10-element array
numbers = new int[20];
// now it's a 20-element array
26
Module 7: Strings, Arrays, and Collections
Declaring an Array C# supports single-dimensional arrays and multidimensional arrays, which are also know as rectangular arrays, and jagged arrays. The following code examples show how to declare each of these arrays: int[] numbers; // single-dimensional array string[,] names; // multidimensional array byte[][] scores; //Array-of-arrays
Instantiating an Array Declaring arrays, as shown in the preceding examples, does not actually create the arrays. As discussed later in this topic, arrays in C# are objects and must be instantiated. The following code examples show the syntax to create an array for each of the arrays already discussed: int[] numbers = new int[5]; // Single-dimensional array string[,] names = new string[5,4]; // Multi-dimensional array byte[][] scores = new byte[5][]; // Array-of-arrays for (int x = 0; x < scores.Length; x++) { scores[x] = new byte[4]; }
Module 7: Strings, Arrays, and Collections
The following example shows a complete C# program that declares and instantiates arrays as discussed in this topic. using System; class DeclareArraysSample { public static void Main() { // Single-dimensional array int[] numbers = new int[5]; // Multidimensional array string[,] names = new string[5,4]; // Array-of-arrays (jagged array) byte[][] scores = new byte[5][]; // Create the jagged array for (int i = 0; i < scores.Length; i++) { scores[i] = new byte[i+3]; } // Print length of each row for (int i = 0; i < scores.Length; i++) { Console.WriteLine("Length of row {0} is {1}", i, scores[i].Length); } } }
The preceding program displays the following output: Length Length Length Length Length
of of of of of
row row row row row
0 1 2 3 4
is is is is is
3 4 5 6 7
Initializing Arrays C# provides simple and straightforward ways to initialize arrays when they are declared, by enclosing the initial values in curly braces ( {} ). Important If an array is not initialized when it is declared, array members are automatically initialized to the default initial value for the array type.
27
28
Module 7: Strings, Arrays, and Collections
Single-Dimensional Array The following examples show different ways to initialize single-dimensional arrays: int[] numbers = new int[5] {1, 2, 3, 4, 5}; string[] names = new string[3] {"Matt", "Joanne", "Robert"};
You can omit the size of the array, as in the following examples: int[] numbers = new int[] {1, 2, 3, 4, 5}; string[] names = new string[] {"Matt", "Joanne", "Robert"};
If an initializer is provided, you can also omit the new statement, as in the following examples: int[] numbers = {1, 2, 3, 4, 5}; string[] names = {"Matt", "Joanne", "Robert"};
Multidimensional Array The following examples show different ways to initialize multidimensional arrays: int[,] numbers = new int[3, 2] { {1, 2}, {3, 4}, {5, 6} }; string[,] siblings = new string[2, 2] { {"Mike","Amy"}, {"Mary","Albert"} };
You can omit the size of the array, as in the following examples: int[,] numbers = new int[,] { {1, 2}, {3, 4}, {5, 6} }; string[,] siblings = new string[,] { {"Mike","Amy"}, {"Mary","Ray"} };
If an initializer is provided, you can also omit the new statement, as in the following examples: int[,] numbers = { {1, 2}, {3, 4}, {5, 6} }; string[,] siblings = { {"Mike", "Amy"}, {"Mary", "Albert"} };
Module 7: Strings, Arrays, and Collections
29
Jagged Array (Array-of-Arrays) The following examples show different ways to initialize jagged arrays. You can initialize jagged arrays by using the following style: int[][] numbers = new int[2][] { new int[] {2,3,4}, new int[] {5,6,7,8,9} };
You can omit the size of the first array, as in the following example: int[][] numbers = new int[][] { new int[] {2,3,4}, new int[] {5,6,7,8,9} };
Accessing Array Members Accessing array members in C# is straightforward and similar to how you access array members in C and C++. For example, the following code creates an array called numbers and then assigns 5 to the fifth element of the array: int[] numbers = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; numbers[4] = 5;
The following code example declares a multidimensional array and assigns 5 to the member located at [1, 1]: int[,] numbers = { {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10} }; numbers[1, 1] = 5;
The following code example shows how to access a member of a jagged array: int[][] numbers { new int[] {1, new int[] {3, }; numbers[1][1] =
= new int[][] 2}, 4} 5;
Arrays Are Objects Arrays in C# are actually objects. System.Array is the abstract base type of all array types. Therefore, you can use the properties and other class members of System.Array. For example, you can use the Length property to get the length of an array. The following code example assigns the length of the numbers array, which is 5, to a variable called LengthOfNumbers: int[] numbers = {1, 2, 3, 4, 5}; int LengthOfNumbers = numbers.Length;
The System.Array class provides many other useful methods and properties, such as methods for sorting, searching, and copying arrays.
30
Module 7: Strings, Arrays, and Collections
Iterating Over Topic Objective
To explain the role of the IEnumerable and IEnumerator interfaces in collections.
Lead-in
A .NET Framework interface provides a way to group a set of related members that can be used to perform a particular action.
!
System.Array and System.Collections Classes Implement IEnumerable Interface and its GetEnumerator Method
!
Enumerator Classes Implement IEnumerator #
Members: MoveNext, Reset, and Current
int[] int[] numbers numbers == new new int[5] int[5] {1, {1, 2, 2, 3, 3, 4, 4, 5}; 5}; IEnumerator e = numbers.GetEnumerator(); IEnumerator e = numbers.GetEnumerator(); while while (e.MoveNext()) (e.MoveNext()) {{ Console.WriteLine("Number: Console.WriteLine("Number: {0}", {0}", (int)e.Current); (int)e.Current); }} // // alternatively alternatively foreach foreach (int (int ii in in numbers) numbers) {{ Console.WriteLine("Number: Console.WriteLine("Number: {0}", {0}", i); i); }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Emphasize the difference between enumeration and an enumerator.
To understand the functionality of the various collection classes, you must understand the key interfaces of the collection classes. A .NET Framework interface provides a way to group a set of related members that can be used to perform a particular action. For example, a client of an object of any class that implements the IEnumerable interface can obtain an enumerator object for that class. The System.Array class and all of the System.Collections classes support the IEnumerable interface. Note It is easy to confuse the term enumerator with enumeration. However, enumerator and enumeration are very different concepts. Enumeration, an enum type in C#, is a distinct type with named constants, as in the following example: enum Color {Red,Green,Blue}
Support for IEnumerable and the GetEnumerator Method These classes inherit from IEnumerable and implement its single method GetEnumerator. The GetEnumerator method returns an enumerator that implements the IEnumerator interface. Enumerators are intended to be used only to read data in the collection. You cannot use enumerators to modify the underlying collection. The enumerator is required to be safe. In other words, enumerators must have a fixed view of the items in a collection that remain the same, even if the collection is modified. For example, if you call GetEnumerator on a collection at the point in time when the collection contains the elements 1, 2, and 3, the enumerator object that is returned by this method must always produce 1, 2, and 3 when it is iterated over, even if the collection is later changed.
Module 7: Strings, Arrays, and Collections
31
Using Enumerators with Collections When working with collections, you typically implement an enumerator in one of the following ways: !
The enumerator makes a copy of all of the items in the collection. Making a copy of a large collection guarantees safety but imposes a severe penalty in performance and memory utilization for large collections and therefore is usually not done.
!
The enumerator has a reference to the collection. If the enumerator implementation uses a reference to the collection, the copy penalty is avoided. However, safety must be guaranteed by another means. If the collection is static or unchanging, you need take no additional action to ensure safety. The .NET Framework collections are typically not static. Instead, they implement a versioning mechanism. Every time a .NET Framework collection changes, the collection’s version number is incremented. An enumerator that detects that the collection’s version number has changed after the enumerator was created throws an InvalidOperationException that should be caught and handled by the enumerator’s client.
The IEnumerator interface has the Current public instance property. The Current property gets the current element in the collection. An enumerator maintains a reference to the item in the collection that is currently being enumerated. The enumerator is in an invalid state if it is positioned before the first element in the collection or after the last element in the collection. When the enumerator is in an invalid state, calling Current throws an exception. The IEnumerator interface also requires the following public instance methods. Method
Use
MoveNext
Advances the enumerator to the next element of the collection.
Reset
Sets the enumerator to its initial position, which is before the first element in the collection.
Initially, the enumerator is positioned before the first element in the collection. Reset also brings the enumerator back to this position. Therefore, after an enumerator is created or after a Reset, you must call MoveNext to advance the enumerator to the first element of the collection before reading the value of Current. Current returns the same object until MoveNext or Reset is called. After the end of the collection is passed, the enumerator returns to an invalid state. At this time, calling MoveNext returns false. Calling Current throws an exception if the last call to MoveNext returned false.
32
Module 7: Strings, Arrays, and Collections
The following code example shows how to iterate over a collection. In this example, the collection is an instance of System.Array. int[] numbers = new int[5] {1, 2, 3, 4, 5}; IEnumerator e = numbers.GetEnumerator(); while (e.MoveNext()) { Console.WriteLine("Number: {0}", (int)e.Current); }
This code outputs: Number: Number: Number: Number: Number:
1 2 3 4 5
Using foreach to Iterate Through an Array C# provides the foreach statement, which is a less verbose way to iterate through the elements of an array. For example, the following code example creates an array called numbers and iterates through it with the foreach statement: int[] numbers = {4, 5, 6, 1, 2, 3, -2, -1, 0}; foreach (int i in numbers) { System.Console.WriteLine("Number: {0}", i); }
Module 7: Strings, Arrays, and Collections
33
Comparing Topic Objective
To explain how the IComparer and IComparable interfaces are used to sort and order a collection’s items.
!
To Sort and Search, Collections Must Be Able to Compare Items
!
IComparer’s Compare Method Compares Two Objects of Any Type
int int Compare( Compare( object object x, x, object object yy ); ); #
Lead-in
!
To sort a collection, you must be able to compare and order the items of the collection.
Comparer class is the default implementation of IComparer Its Compare method uses IComparable.CompareTo
IComparable’s CompareTo Method Compares the Current Instance to an Object of the Same Type
int int CompareTo( CompareTo( object object anObject anObject ); ); ! CompareTo Returns
Value Less than zero
Meaning Instance is less than object
Zero Greater than zero
Instance is equal to object Instance is greater than object
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To sort a collection, you must be able to compare and order the items of the collection. The IComparer and IComparable interfaces are used to sort and order a collection’s items. The IComparer interface’s Compare method compares two objects of any type and returns a value that indicates whether one object is less than, equal to, or greater than the other. The Compare method provides the sort order of a collection and is also used in conjunction with the Array.BinarySearch method.
Default Implementation of IComparer The Comparer class provides the default implementation of the IComparer interface. The Comparer class’s Compare(object a, object b) method is implemented as follows: !
If a implements IComparable, then a.CompareTo(b) is returned.
!
Otherwise, if b implements IComparable, then b.CompareTo(a) is returned.
The IComparable interface has a single CompareTo method that compares the current instance with another object of the same type.
34
Module 7: Strings, Arrays, and Collections
Return Values of CompareTo The value returned by the CompareTo method is a 32-bit signed integer that indicates the relative order of the instance and the object that is passed as a parameter, as in the following example: int CompareTo(object anObject);
The following table describes the possible meanings of the return value. Value
Meaning
Less than zero
This instance is less than anObject.
Zero
This instance is equal to anObject.
Greater than zero
This instance is greater than anObject.
By definition, any object compares greater than a null reference, and two null references compare equal to each other.
Issues with Using IComparable.CompareTo The parameter, anObject, must be the same type as the class or value type that implements this interface. Otherwise, ArgumentException is thrown. The default comparison procedures use the Thread.CurrentCulture of the current thread unless it is otherwise specified. String comparisons may have different results depending on the culture. To perform case-insensitive comparisons on strings, you can use the CaseInsensitiveComparer class’s implementation of the IComparer interface.
Module 7: Strings, Arrays, and Collections
35
Sorting Topic Objective
To explain how to compare and order the items of a collection by using the IComparer and IComparable interfaces. Knowledge of this topic is required for the lab.
Lead-in
Some collections, such as Array, sort their items when their Sort method is called.
!
Sort Method Using Element’s IComparable.CompareTo Array.Sort( Array.Sort( anArray anArray ); );
!
IComparable.CompareTo Design Pattern
public public int int CompareTo(Object CompareTo(Object anObject) anObject) {{ if if (( anObject anObject == == null) null) return return 1; 1; if if (( !(anObject !(anObject is is ) ) )) {{ throw throw new new ArgumentException(); ArgumentException(); }} // Do comparison // Do comparison and and return return aa // negative integer if // negative integer if instance instance > anObject anObject }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Some collections, such as Array, sort their items when their Sort method is called. Overloaded versions of the Sort method allow you to supply an IComparer implementation in the method call that is used to perform the ordering of the elements. These overloaded versions of the Sort method provide you with the flexibility to resort the same collection of items by using different IComparer implementations. If you use the array’s public static void Sort(Array) method, the default Comparer class implementation of IComparer is used, and the array’s elements are sorted by using the elements’ implementation of the CompareTo method of the IComparable interface. Other collection classes, such as SortedList, have constructors whose parameters determine the sort order. These classes are less flexible than classes that implement a sort method because after they are constructed their sort ordering cannot be changed.
36
Module 7: Strings, Arrays, and Collections
Demonstration: Sorting and Enumerating an Array Topic Objective
To demonstrate how to sort and enumerate an array.
Lead-in
In this demonstration, we will sort an array by using the CompareTo method when the value type or class of the items in the collection implements the IComparable interface.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** As stated in Sorting in this module, you can sort an array by using the CompareTo method when the value type or class of the items in the collection implements the IComparable interface. For Your Information
You should carefully cover the CompareTo method because it is used in the lab.
In this demonstration, an Employee class contains an employee’s name, level number, and hiring date. To sort an array of Employees based on increasing level numbers where equal level numbers are then ordered by increasing hiring date, implement the following: using System; namespace ArraySorting { public class Employee : IComparable { public string name; public int level; public DateTime hiringDate; public Employee( string name,int level,DateTime hiringDate) { this.name = name; this.level=level; this.hiringDate=hiringDate; } public int CompareTo(Object anObject) { if (anObject == null) return 1; if ( !(anObject is Employee) ) { throw new ArgumentException(); }
(Code continued on the following page.)
Module 7: Strings, Arrays, and Collections Employee anEmployee = (Employee)anObject; if ( level < anEmployee.level ) return -1; else { if ( level == anEmployee.level ) { if (hiringDate < anEmployee.hiringDate) return -1; else { if ( hiringDate == anEmployee.hiringDate) return 0; else return 1; } } else return 1; } } } public class ArraySort
{
public static void Main()
{
// Create and initialize a new Array instance. Employee[] myEmployees = new Employee[10]; myEmployees[0] = new Employee( "a",2,new DateTime(1990,1,1)); myEmployees[1] = new Employee( "b",2,new DateTime(2000,1,1)); myEmployees[2] = new Employee( "c",2,new DateTime(1990,1,1)); myEmployees[3] = new Employee( "d",4,new DateTime(2000,1,1)); myEmployees[4] = new Employee( "e",4,new DateTime(1990,1,1)); myEmployees[5] = new Employee( "f",4,new DateTime(2000,1,1)); myEmployees[6] = new Employee( "g",1,new DateTime(1990,2,5)); myEmployees[7] = new Employee( "h",1,new DateTime(2000,1,1)); myEmployees[8] = new Employee( "i",1,new DateTime(1990,1,1)); myEmployees[9] = new Employee( "j",0,new DateTime(2001,1,1)); // Display the values of the Array. Console.WriteLine( "The Array instance initially contains values:" ); PrintIndexAndValues( myEmployees ); // Sort the values of the Array. Array.Sort( myEmployees );
(Code continued the following page.)
37
38
Module 7: Strings, Arrays, and Collections // Display the values of the Array. Console.WriteLine( "After sorting:" ); PrintIndexAndValues( myEmployees ); } public static void PrintIndexAndValues( Array myEmployees ) { foreach ( Employee e in myEmployees ) { Console.WriteLine( "name: {0} \tlevel: {1} \tdate:{2:d}", e.name, e.level, e.hiringDate); } } } }
The preceding code displays the following output to the console: The Array instance initially contains values: name: a level: 2 date:1/1/1990 name: b level: 2 date:1/1/2000 name: c level: 2 date:1/1/1990 name: d level: 4 date:1/1/2000 name: e level: 4 date:1/1/1990 name: f level: 4 date:1/1/2000 name: g level: 1 date:2/5/1990 name: h level: 1 date:1/1/2000 name: i level: 1 date:1/1/1990 name: j level: 0 date:1/1/2001 After sorting: name: j level: 0 date:1/1/2001 name: i level: 1 date:1/1/1990 name: g level: 1 date:2/5/1990 name: h level: 1 date:1/1/2000 name: c level: 2 date:1/1/1990 name: a level: 2 date:1/1/1990 name: b level: 2 date:1/1/2000 name: e level: 4 date:1/1/1990 name: d level: 4 date:1/1/2000 name: f level: 4 date:1/1/2000
Module 7: Strings, Arrays, and Collections
39
" .NET Framework Collections Topic Objective
To introduce the topics in the section.
Lead-in
The System.Collections namespace contains interfaces and classes that define various collections of objects, such as lists, queues, arrays, hashtables, and dictionaries.
!
Examples of System.Collections Classes
!
Lists
!
Dictionaries
!
SortedList
!
Collection Usage Guidelines
!
Type Safety and Performance
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The System.Collections namespace contains interfaces and classes that define various collections of objects, such as lists, queues, arrays, hashtables, and dictionaries.
40
Module 7: Strings, Arrays, and Collections
Examples of System.Collections Classes Topic Objective
!
To show some of the collection classes in the System.Collections namespace.
ArrayList #
!
DictionaryBase #
Lead-in
Let’s look briefly at some of the collection classes in the System.Collections namespace.
!
Represents a collection of keys and values that are organized around the key’s hash code
SortedList #
!
Provides abstract base class for strongly-typed collection of associated keys and values
Hashtable #
!
Implements IList by using a dynamically-sized array
Represents the collection of keys and values, sorted by keys and accessible by key and index
BitArray, Queue, Stack, CollectionBase, ReadOnlyCollectionBase
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Do not spend a lot of time on this slide. Just introduce students to some commonly used collection classes and encourage them to refer to the .NET Framework SDK for more information.
The following table shows some of the collection classes in the System.Collections namespace. Class
Description
ArrayList
Implements the IList interface by using an array whose size is dynamically increased as required.
BitArray
Manages a compact array of bit values, which are represented as Booleans, where true indicates that the bit is on (1) and false indicates the bit is off (0).
CollectionBase
Provides the abstract base class (MustInherit in Microsoft Visual Basic®) for a strongly-typed collection.
DictionaryBase
Provides the abstract base class (MustInherit in Visual Basic) for a strongly-typed collection of associated keys and values.
Hashtable
Represents a collection of associated keys and values that are organized around the hash code of the key.
Queue
Represents a first-in, first-out collection of objects.
ReadOnlyCollectionBase
Provides the abstract base class (MustInherit in Visual Basic) for a strongly-typed read-only collection.
SortedList
Represents a collection of associated keys and values that are sorted by the keys and are accessible by key and by index.
Stack
Represents a simple last-in-first-out collection of type Object.
Module 7: Strings, Arrays, and Collections
41
Lists Topic Objective
To describe the public instance members that the IList interface implements.
!
IList – Interface for Classes That Represent an Ordered Collection of Objects That Can Be Individually Indexed
!
Some Classes That Implement IList
Lead-in
IList is an interface for classes that represent an ordered collection of objects that can be individually indexed. Array, ArrayList, StringCollection, and TreeNodeCollection are some of the classes that implement IList.
#
!
Array, ArrayList, StringCollection, and TreeNodeCollection
Methods Include: #
Add, Clear, Contains, Insert, IndexOf, Remove, and RemoveAt
*****************************ILLEGAL FOR NON-TRAINER USE****************************** IList is an interface for classes that represent an ordered collection of objects that can be individually indexed. Array, ArrayList, StringCollection, and TreeNodeCollection are some of the classes that implement IList. The following tables describe the public instance properties that the IList interface implements. Property
Use
IsFixedSize
When implemented by a class, gets a value indicating whether the IList has a fixed size.
IsReadOnly
When implemented by a class, gets a value indicating whether the IList is read-only.
Item
When implemented by a class, gets or sets the element at the specified index. In C#, this property is the indexer for the IList class.
42
Module 7: Strings, Arrays, and Collections
The following table describes the public instance methods that the IList interface defines. Method
Use
Add
When implemented by a class, adds an item to the IList.
Clear
When implemented by a class, removes all items from the IList.
Contains
When implemented by a class, determines whether the IList contains a specific value.
IndexOf
When implemented by a class, determines the index of a specific item in the IList.
Insert
When implemented by a class, inserts an item to the IList at the specified position.
Remove
When implemented by a class, removes the first occurrence of a specific object from the IList.
RemoveAt
When implemented by a class, removes the IList item at the specified index.
Module 7: Strings, Arrays, and Collections
43
Demonstration: ArrayList Topic Objective
To demonstrate how ArrayList implements the IList interface by using an array whose size is dynamically increased as required.
Lead-in
In this demonstration, ArrayList implements the IList interface by using an array whose size is dynamically increased as required.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this demonstration, ArrayList implements the IList interface by using an array whose size is dynamically increased as required. using System; using System.Collections; class listSample { public static void Main(string[] args) { ArrayList fruit = new ArrayList(); fruit.Add("Apple"); fruit.Add("Pear"); fruit.Add("Orange"); fruit.Add("Banana"); Console.WriteLine ("\nList Contains:"); foreach (string item in fruit) { Console.WriteLine(item); } Console.WriteLine ( "\nResult of Contains method for Kiwi: {0}", fruit.Contains("Kiwi")); Console.WriteLine ( "\nAdding Kiwi at Orange:"); fruit.Insert(fruit.IndexOf("Orange"),"Kiwi"); Console.WriteLine ("\nList Contains:"); foreach (string item in fruit) { Console.WriteLine(item); }
(Code continued the following page.)
44
Module 7: Strings, Arrays, and Collections Console.WriteLine ( "\nResult of Contains method for Kiwi: {0}", fruit.Contains("Kiwi")); Console.WriteLine ( "\r\nPress Return to exit."); Console.Read(); } }
The preceding code example displays the following output to the console: List Contains: Apple Pear Orange Banana Result of Contains method for Kiwi: False Adding Kiwi at Orange: List Contains: Apple Pear Kiwi Orange Banana Result of Contains method for Kiwi: True Press Return to exit.
Module 7: Strings, Arrays, and Collections
45
Dictionaries Topic Objective
To describe the IDictionary interface and the classes that it implements.
!
Lead-in
IDictionary is an Interface for Collections of Associated Keys and Values #
IDictionary is an interface for collections of associated keys and values. !
Collection Classes That Implement IDictionary Include #
!
Each association must have a unique non-null key, but the value of an association can be any object reference, including a null reference Hashtable, DictionaryBase, and SortedList
Methods Include: #
Add, Clear, Contains, GetEnumerator, and Remove
*****************************ILLEGAL FOR NON-TRAINER USE****************************** IDictionary is an interface for collections of associated keys and values. Each association must have a unique non-null key, but the value of an association can be any object reference, including a null reference. Hashtable, DictionaryBase, and SortedList are examples of collection classes that implement IDictionary. The IDictionary interface allows the contained keys and values in the items in a collection to be enumerated, but it does not imply any particular sort order. The IDictionaryEnumerator interface inherits from IEnumerator and adds members to return the object’s Key and Value fields individually or in a DictionaryEntry structure. IDictionary implementations can be divided into the following categories: !
Read-only You cannot modify a read-only IDictionary.
!
Fixed-size You cannot add or remove elements from a fixed-size IDictionary, but you can modify existing elements.
!
Variable-size You can add, remove, or modify elements in a variable-size IDictionary.
46
Module 7: Strings, Arrays, and Collections
The following table describes some of the public instance properties that the IDictionary interface implements. Property
Use
IsFixedSize
When implemented by a class, gets a value indicating whether the IDictionary has a fixed size.
IsReadOnly
When implemented by a class, gets a value indicating whether the IDictionary is read-only.
Item
When implemented by a class, gets or sets the element at the specified index. In C#, this property is the indexer for the class.
Keys
When implemented by a class, gets an ICollection containing the keys of the IDictionary.
Values
When implemented by a class, gets an ICollection containing the values in the IDictionary.
The following table describes some of the public instance methods that the IDictionary interface implements. Method
Use
Add
When implemented by a class, adds an entry with the provided key and value to the IDictionary.
Clear
When implemented by a class, removes all elements from the IDictionary.
Contains
When implemented by a class, determines whether the IDictionary contains an entry with the specified key.
GetEnumerator
When implemented by a class, returns an IDictionaryEnumerator for the IDictionary.
Remove
When implemented by a class, removes the entry with the specified key from the IDictionary.
For a complete list of the members of the IDictionary interface, see “IDictionary Members” in the .NET Framework SDK documentation.
Module 7: Strings, Arrays, and Collections
47
Demonstration: Hashtable Topic Objective
To demonstrate how to create a hash table that is used for searches.
Lead-in
The Hashtable class represents a collection of associated keys and values that are organized on the basis of the keys’ hash code.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The Hashtable class represents a collection of associated keys and values that are organized on the basis of the keys’ hash code. The objects that are used as keys in a Hashtable must implement or inherit the Object.GetHashCode and Object.Equals methods. If key equality were simply reference equality, the inherited implementation of these methods would suffice. Furthermore, these methods must produce the same results when they are called with the same parameters while the key exists in the Hashtable. Key objects must be immutable as long as they are used as keys in the Hashtable. The foreach statement of the C# language requires the type of the elements in the collection. Because each element of the Hashtable is a key-and-value pair, the element type is not the type of the key or the type of the value. Instead, the element type is DictionaryEntry, as in the following example: foreach (DictionaryEntry myEntry in myHashtable) {...}
48
Module 7: Strings, Arrays, and Collections
The code in this demonstration creates a hash table of employee numbers and names, and searches the table for an employee by number and by name. using System; using System.Collections; class HashTableSample { public static void Main(String[] args) { //create hash table of employee numbers and names Hashtable table = new Hashtable(); table.Add("0123","Jay"); table.Add("0569","Brad"); table.Add("1254","Brian"); table.Add("6839","Seth"); table.Add("3948","Rajesh"); table.Add("1930","Lakshan"); table.Add("9341","Kristian"); printTable(table); //now we'll look to see if an employee is in the table //by key Console.Write( "Search for employee by key, enter ID ==-> "); string input = Console.ReadLine(); if (table.Contains(input)) { Console.WriteLine("Found {0} in the list.",input); } else { Console.WriteLine("Employee {0} not found.",input); } //by value Console.Write( "Search for employee by value, enter name ==-> "); input = Console.ReadLine(); if (table.ContainsValue(input)) { Console.WriteLine("Found {0} in the list.",input); } else { Console.WriteLine("Employee {0} not found.",input); } printTable(table); // remove an employee by key Console.Write("Remove employee by key, enter ID ==-> "); input = Console.ReadLine(); table.Remove(input); printTable(table); Console.WriteLine ("\r\nPress Return to exit."); Console.Read(); }
(Code continued on the following page.)
Module 7: Strings, Arrays, and Collections public static void printTable(Hashtable table) { Console.WriteLine ("Current list of employees:\n"); Console.WriteLine ("ID\tName"); Console.WriteLine ("--\t----"); foreach (DictionaryEntry d in table) { Console.WriteLine ("{0}\t{1}", d.Key, d.Value); } } }
The preceding code example displays the following output or similar output to the console: Current list of employees: ID Name ----1254 Brian 6839 Seth 3948 Rajesh 1930 Lakshan 0123 Jay 0569 Brad 9341 Kristian Search for employee by key, enter ID ==-> 111 Employee 111 not found. Search for employee by value, enter name ==-> Jay Found Jay in the list. Current list of employees: ID Name ----1254 Brian 6839 Seth 3948 Rajesh 1930 Lakshan 0123 Jay 0569 Brad 9341 Kristian Remove employee by key, enter ID ==-> 9341 Current list of employees: ID -1254 6839 3948 1930 0123 0569
Name ---Brian Seth Rajesh Lakshan Jay Brad
Press Return to exit.
49
50
Module 7: Strings, Arrays, and Collections
SortedList Topic Objective
To explain how arrays are used in a SortedList and how to create, initialize, and access a SortedList. This topic is very important because its contents are required for the lab.
Lead-in
A SortedList maintains two arrays internally to store entries to the list: one array for the keys, and another array for the associated values.
!
SortedList Maintains Two Arrays for Entries #
One array for the keys and another array for the associated values
SortedList SortedList mySL mySL == new new SortedList(); SortedList(); // Add an entry with // Add an entry with aa key key == "First" "First" and and aa value value == 11 mySL.Add("First", 1); mySL.Add("First", 1); // // Increment Increment the the value value of of the the entry entry whose whose key key == "First" "First" mySL["First"] = (Int32)mySL["First"] + 1; mySL["First"] = (Int32)mySL["First"] + 1; !
Count Property – Number of Elements in the SortedList
!
Sorted Using a Specific IComparer Implementation or According to the Key's IComparable Implementation
!
Printing the Keys and Values of a SortedList
for for (( int int ii == 0; 0; ii 0) { bytes = mySocket.Receive(RecvBytes, RecvBytes.Length, 0); strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes); } } catch (Exception e) { Console.WriteLine("Exception {0}",e.ToString()); } return strRetPage; } }
Module 11: Internet Access
25
Handling Errors Topic Objective
!
To explain how the Status property is used to determine if an error has occurred.
Lead-in
The WebRequest and WebResponse classes throw system exceptions and Web-specific exceptions, which are instances of WebException and thrown by the GetResponse method.
A WebException Can Be Thrown by GetResponse " "
Has Status property, value from WebExceptionStatus If WebExceptionStatus.ProtocolError, then WebResponse contains protocol error information
try try {{ // // ... ... Create Create aa request, request, get get response response and and process process stream. stream. }} catch catch (WebException (WebException webExcp) webExcp) {{ Console.WriteLine("A Console.WriteLine("A WebException WebException has has been been caught"); caught"); Console.WriteLine(webExcp.ToString()); Console.WriteLine(webExcp.ToString()); WebExceptionStatus WebExceptionStatus status status == webExcp.Status; webExcp.Status; if if (status (status == == WebExceptionStatus.ProtocolError) WebExceptionStatus.ProtocolError) {{ Console.Write( Console.Write( "The "The server server returned returned protocol protocol error error "); "); Console.WriteLine(webExcp.Response.ToString()); Console.WriteLine(webExcp.Response.ToString()); }} }} catch catch (Exception (Exception e) e) {// {// Code Code to to catch catch other other exceptions exceptions goes goes here.} here.}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The WebRequest and WebResponse classes throw system exceptions, such as InvalidArgumentException, and Web-specific exceptions, which are instances of WebException and are thrown by the GetResponse method. Each WebException includes a Status property that contains a value from the WebExceptionStatus class. You can examine the Status property to determine the particular error that has occurred and take the proper steps to resolve the error. The following table describes some of the possible values for the Status property. For a complete list of values, see the .NET Framework SDK documentation. Value
Description
ConnectFailure
The remote service could not be contacted at the transport level.
ConnectionClosed
The connection was closed prematurely.
KeepAliveFailure
The server closed a connection made with the Keep-alive header set.
NameResolutionFailure
The name service could not resolve the host name.
ProtocolError
The response received from the server was complete but indicated an error at the protocol level.
ReceiveFailure
A complete response was not received from the remote server.
RequestCanceled
The request was canceled.
SecureChannelFailure
An error occurred in a secure channel link.
SendFailure
A complete request could not be sent to the remote server.
26
Module 11: Internet Access
(continued) Value
Description
ServerProtocolViolation
The server response was not a valid HTTP response.
Success
No error was encountered.
Timeout
No response was received within the time-out set for the request.
TrustFailure
A server certificate could not be validated.
When the Status property is WebExceptionStatus.ProtocolError, a WebResponse that contains the response from the server is available. You can examine this response to determine the actual source of the protocol error. The following example shows how to catch a WebException; the invalid URL argument in the WebRequest.Create call will throw an exception: using using using using
System; System.Net; System.IO; System.Text;
class App { public static void Main(string[] args) { try { // Create a request instance // Note invalid URL will throw exception WebRequest myRequest = WebRequest.Create("http://localhost_bad_URL"); // Get the response. WebResponse myResponse = myRequest.GetResponse(); //Get a readable stream from the server. StreamReader sr = new StreamReader( myResponse.GetResponseStream(), Encoding.ASCII); int length = 1024; char [] Buffer = new char[1024]; int bytesread = 0; //Read from the stream and write data to Console. bytesread = sr.Read( Buffer, 0, length); while( bytesread > 0 ) { Console.Write( Buffer, 0, bytesread); bytesread = sr.Read( Buffer, 0, length); } sr.Close(); myResponse.Close(); }
(Code continued on the following page.)
Module 11: Internet Access
27
catch (WebException webExcp) { Console.WriteLine("A WebException has been caught"); // Write out the WebException message. Console.WriteLine(webExcp.ToString()); // Get the WebException status code. WebExceptionStatus status = webExcp.Status; // If status is WebExceptionStatus.ProtocolError, // there has been a protocol error and a // WebResponse should exist. // Display the protocol error. if (status == WebExceptionStatus.ProtocolError) { Console.Write( "The server returned protocol error "); Console.WriteLine(webExcp.Response.ToString()); } } catch (Exception e) { // Code to catch other exceptions goes here. } } }
Applications that use the Socket class throw instances of SocketException when errors occur on the Windows socket. The TcpClient, TcpListener, and UdpClient classes are built on top of the Socket class and also throw instances of SocketException. When a SocketException is thrown, the Socket class sets the ErrorCode property to the last operating system socket error that occurred.
28
Module 11: Internet Access
# Security Topic Objective
To introduce the topics in the section.
Lead-in
Your application can provide security for sending and receiving data over the Internet by using a Web proxy, SSL encryption, Internet authentication, and the NET Framework’s code access permissions.
!
Web Proxy
!
Secure Sockets Layer
!
Internet Authentication
!
Permissions
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Your application can provide security for sending and receiving data over the Internet by using a Web proxy, Secure Sockets Layer (SSL) encryption, Internet authentication, and the NET Framework’s code access permissions.
Module 11: Internet Access
29
Web Proxy Topic Objective
To show how to use a Web proxy to communicate with the Internet.
Lead-in
If your Web site uses a proxy to provide access to the Internet, you must configure a proxy instance to enable your application to communicate with the Web proxy.
!
Global Proxy for All Web Requests "
Proxy named webproxy using port 80
WebProxy WebProxy proxyObject proxyObject == new new WebProxy( WebProxy( "http://webproxy:80/"); "http://webproxy:80/"); GlobalProxySelection.Select GlobalProxySelection.Select == proxyObject; proxyObject; !
Overriding the Global Proxy Setting "
Request uses proxy named alternateproxy and port 80
WebRequest WebRequest req req == WebRequest.Create( WebRequest.Create( "http://www.contoso.com/"); "http://www.contoso.com/"); req.Proxy req.Proxy == new new WebProxy( WebProxy( "http://alternateproxy:80/"); "http://alternateproxy:80/");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** If your Web site uses a proxy to provide access to the Internet, you must configure a proxy instance to enable your application to communicate with the Web proxy.
Creating a Global Proxy Instance The following example shows how to create a global proxy instance that enables any WebRequest to use a proxy to communicate with the Internet. The example assumes that the proxy server is named webproxy and that it communicates on port 80, the standard HTTP port. WebProxy proxyObject = new WebProxy("http://webproxy:80/"); GlobalProxySelection.Select = proxyObject;
Overriding the Global Proxy Selection You can override the global proxy selection by assigning an instance that implements the IWebProxy interface to the Proxy property of the WebRequest. The following code sends a WebRequest to http://www.contoso.com. The WebRequest overrides the global proxy selection with a proxy server that is named alternateproxy on port 80. WebRequest req = WebRequest.Create("http://www.contoso.com/"); req.Proxy = new WebProxy("http://alternateproxy:80/");
30
Module 11: Internet Access
Secure Sockets Layer Topic Objective
To explain how SSL is used for secure network communication.
!
SSL Is Used Automatically If the URI Begins with https
Lead-in
The WebRequest and WebResponse classes use SSL automatically.
String String MyURI MyURI == "https://www.contoso.com/"; "https://www.contoso.com/"; WebRequest WebRequest wReq wReq == WebRequest.Create(MyURI); WebRequest.Create(MyURI);
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The WebRequest and WebResponse classes use SSL automatically. The WebRequest object decides to use SSL on the basis of the URI that it is assigned. If the URI begins with https:, SSL is used. If the URI begins with http:, SSL is not used. The following example illustrates the use of SSL: String MyURI = "https://www.contoso.com/"; WebRequest wReq = WebRequest.Create(MyURI);
Module 11: Internet Access
31
Internet Authentication Topic Objective
To introduce the client authentication mechanisms that are supported by the System.Net classes.
!
Lead-in
The System.Net classes support a variety of client authentication mechanisms, including the standard Internet authentication methods: basic, digest, negotiate, NTLM, and Kerberos authentication, and custom methods that you can create.
!
.NET Supports Various Kinds of Authentication "
Basic, digest, negotiate, NTLM, and Kerberos authentication
"
Users can also create their own authentication
Credentials Stored in Classes "
NetworkCredential – for a single Internet resource
"
CredentialCache – for multiple Internet resources
!
Authentication Managed by the AuthenticationManager
!
Some Schemes Allow Pre-Authentication to Save Time
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The System.Net classes support a variety of client authentication mechanisms, including the standard Internet authentication methods: basic, digest, negotiate, NTLM, and Kerberos authentication, and custom methods that you can create.
Classes and Interfaces Used for Authentication Authentication credentials are stored in the NetworkCredential and CredentialCache classes, which implement the ICredentialLookup interface. When one of these classes is queried for credentials, it returns an instance of the NetworkCredential class. The AuthenticationManager class manages the authentication process, while an authentication module class that implements the IAuthenticationModule interface performs the actual authentication process. You must register a custom authentication module with the AuthenticationManager before it can be used. Modules for the basic, digest, negotiate, NTLM, and Kerberos authentication methods are registered by default.
The NetworkCredential Class The NetworkCredential class stores a set of credentials, which is associated with a single Internet resource and that is identified by a URI, and returns them in response to any call to the GetCredential method. The NetworkCredential class is typically used by applications that access a limited number of Internet resources or by applications that use the same set of credentials in all cases.
The CredentialCache Class The CredentialCache class stores a collection of credentials for various Internet resources. When the GetCredential method is called, CredentialCache returns the proper set of credentials, as determined by the URI of the Internet resource and the requested authentication scheme. Because the CredentialCache class stores all of the credentials and provides them as requested, applications that use a variety of Internet resources with different authentication schemes benefit from using the CredentialCache class.
32
Module 11: Internet Access
The Authentication Process When an Internet resource requests authentication, the WebRequest.GetResponse method sends the WebRequest and the request for credentials to the AuthenticationManager. The request is then authenticated according to the following procedure: 1. The AuthenticationManager calls the Authenticate method on each of the registered authentication modules in the order that they were registered. 2. The AuthenticationManager uses the first module that does not return null to carry out the authentication process. The details of the process vary depending on the type of authentication module involved. 3. When the authentication process is complete, the authentication module returns an Authorization instance to the WebRequest that contains the information that is needed to access the Internet resource. Some authentication schemes can authenticate a user without first making a request for a resource. An application can save time by pre-authenticating the user with the resource, thus eliminating at least one roundtrip to the server. Alternatively, the application can perform authentication during program startup to be more responsive to the user later. Authentication schemes that can use pre-authentication set the CanPreAuthenticate property to true.
Basic and Digest Authentication The System.Net implementation of basic and digest authentication complies with RFC2617, “HTTP Authentication: Basic and Digest Authentication,” which is available on the World Wide Web Consortium (W3C) Web site at http://www.w3c.org. To use basic and digest authentication, an application must provide a user name and password in the Credentials property of the WebRequest object that it uses to request data from the Internet, as shown in the following example: Caution String literals in an application are stored and transported as clear text. Therefore, you should avoid putting sensitive information such as passwords in string literals. // variables named username and password // of type string have been previously assigned String MyURI = "http://www.contoso.com/"; WebRequest wReq = WebRequest.Create(MyURI); wReq.Credentials = new NetworkCredential( username, password);
Module 11: Internet Access
33
NTLM and Kerberos Authentication Default NTLM authentication and Kerberos authentication use the Microsoft Windows NT® user credentials that are associated with the calling application to attempt authentication with the server to pass the username, password, and domain to the host, as in the following example: // variables named username, password, and domain // of type string have been previously assigned String MyURI = "http://www.contoso.com/"; WebRequest wReq = WebRequest.Create(MyURI); wReq.Credentials = new NetworkCredential(username, password, domain);
Applications that need to connect to Internet services by using the credentials of the application user can do so with the user’s default credentials, as in the following example: String MyURI = "http://www.contoso.com/"; WebRequest wReq = WebRequest.Create(MyURI); wReq.Credentials = CredentialCache.DefaultCredentials;
The negotiate authentication module determines whether the remote server is using NTLM or Kerberos authentication and sends the appropriate response. Note NTLM authentication does not work through a proxy server.
Passport Authentication Passport authentication is a centralized authentication service provided by Microsoft that offers a single logon and core profile services for member sites. This benefits the user because it is no longer necessary to log on to access new protected resources or sites. If you want your site to be compatible with Passport authentication and authorization, this is the provider you should use. For more information, see the Passport documentation located at http://www.passport.com/business.
34
Module 11: Internet Access
Permissions Topic Objective
To explain which permission classes best suit which application types.
Lead-in
The WebPermissions and SocketPermissions classes provide Internet security for applications that use System.Net.
!
WebPermissions "
!
SocketPermissions "
!
!
Controls an application's right to request data from a URI or to serve a URI to the Internet Controls an application's right to accept data on a local port or to contact applications
Choose Permission Class Based on Application Use "
WebRequest and its descendents use WebPermissions
"
Socket-level access uses SocketPermissions
Both Classes Support Two Kinds of Permissions "
Accept – application can answer an incoming connection
"
Connect – application can initiate a connection
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The WebPermissions and SocketPermissions classes provide Internet security for applications that use System.Net. The WebPermissions class controls an application’s right to request data from a URI or to serve a URI to the Internet. The SocketPermissions class controls an application’s right to accept data on a local port or to contact applications through a transport protocol at another address that is based on the host, port number, and transport protocol. You should choose the permission class on the basis of your application type. Applications that use WebRequest and its descendents should use the WebPermissions class to manage permissions. Applications that use socket-level access should use the SocketPermissions class to manage permissions. WebPermissions and SocketPermissions define two permissions: accept and connect. Accept grants the application the right to answer an incoming connection from another party. Connect grants the application the right to initiate a connection to another party. For WebPermissions, accept means that an application can export a particular URI anywhere on the Internet. Connect means that an application can access that URI, whether it is remote or local. For SocketPermissions, accept means that an application can accept incoming connections on a local transport address. Connect means that an application can connect to a remote, or potentially local, transport address.
Module 11: Internet Access
35
Best Practices Topic Objective
To introduce best practices that will help students use the System.Net classes more effectively.
Lead-in
The following recommendations will help you use the classes that are contained in System.Net more effectively.
!
When Possible, Use WebRequest and WebResponse, Instead of Protocol-Specific Subclasses
!
For Better Performance, Use Asynchronous Methods
!
Tune Performance by Adjusting the Number of Connections "
ConnectionLimit property in the ServicePoint instance
!
When Possible, Use TcpClient or UdpClient, Instead of Writing Directly to a Socket
!
Use the CredentialCache Class If Credentials Are Required
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The following recommendations will help you use the classes that are contained in System.Net more effectively: !
Whenever possible, use WebRequest and WebResponse, instead of typecasting to descendent classes. Applications that use WebRequest and WebResponse can take advantage of new Internet protocols without extensive code changes.
!
When writing ASP.NET applications that run on a server that uses the System.Net classes, it is often better, from a performance standpoint, to use the asynchronous methods for GetResponse and GetResponseStream.
!
Set the ConnectionLimit property in the ServicePoint instance for your application. The number of connections opened to an Internet resource can have a significant effect on network performance and throughput. By default, System.Net uses two connections per application for each host. Setting the ConnectionLimit property in the ServicePoint instance for your application can increase this number.
!
When writing socket-level protocols, try to use the TcpClient or UdpClient classes, instead of writing directly to a socket. The TcpClient and UdpClient classes encapsulate the creation of TCP and UDP sockets without requiring you to handle the details of the connection.
!
When accessing sites that require credentials, use the CredentialCache class to create a cache of credentials, rather than supplying them with every request. The CredentialCache class will search the cache to find the appropriate credential to present with a request, thus relieving you of the responsibility of creating and presenting credentials based on the URI.
36
Module 11: Internet Access
Lab 11: Creating a DateTime Client/Server Application Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create a client application that uses the System.Net.Sockets .TcpClient class to connect to and obtain date and time information from a server. You will also create a server application that uses the System.Net.Sockets .TcpListener class to accept requests from and provide date and time information to clients.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: !
Create a client application that uses the System.Net.Sockets.TcpClient class to connect to and obtain date and time information from a server.
!
Create a server application that uses the System.Net.Sockets.TcpListener class to accept requests from and provide date and time information to clients.
Lab Setup Only solution files are associated with this lab. The solution files for this lab are in the folder \Labs\Lab11\Solution.
Scenario In this lab, you will create two Microsoft Visual Studio® .NET console applications: DateTimeClient and DateTimeServer. The DateTimeClient application will make a TCP connection to the DateTimeServer application and obtain a stream that contains date and time information. The DateTimeClient will read the stream and convert the stream’s ASCII data into a string that is then output to the console.
Estimated time to complete this lab: 45 minutes
Module 11: Internet Access
37
Exercise 1 Creating the DateTime Server In this exercise, you will create a server application that will provide date and time information to clients through TCP.
! Create the server application 1. In Visual Studio .NET, create a new C# console application project named DateTimeServer in \Labs\Lab11. 2. Rename the starting C# source file Datetimeserver.cs. 3. Add the following using statements: using System.Net; using System.Net.Sockets; using System.Text;
4. Rename the wizard generated Class1 to Server. 5. Modify the Main method in the following steps. 6. In the try section of a try/catch block: a. Instantiate a TcpListener object to listen on port 14. b. Start the TcpListener object. c. Write out the following message to the console: Waiting for clients to connect Press Ctrl+c to Quit...
d. Enter an infinite loop that will: i. Accept TCP client connections using the AcceptSocket method. ii. Call a DateTime method to get the current date and time. iii. Create a string that consists of the short version of the date, followed by the long version of the time. iv. Convert the string to an ASCII-encoded byte array. v. Send this byte array to the TCP client. vi. Close the socket. vii. Write out to the console a message that contains the string that was just sent. 7. In the catch section of the try/catch block: • Catch any exceptions of type SocketException and if the exception’s ErrorCode property has a value of 10048, then write to the console a message that states “Connection to this port failed. There is another server is listening on this port.” 8. Build the server application.
38
Module 11: Internet Access
Exercise 2 Creating the DateTime Client In this exercise, you will create a client application. The client application takes as its runtime argument the name of the computer on which the server is running. The client connects to the server to obtain date and time information through TCP. The client then displays the date and time information on the console.
! Create the client application 1. In Visual Studio .NET, create a new C# console application project named DateTimeClient in \Labs\Lab11. 2. Rename the starting C# source file Datetimeclient.cs. 3. Add the following using statements: using using using using
System.Net; System.Net.Sockets; System.IO; System.Text;
4. Rename the wizard generated Class1 to Client. 5. Make the static Main method capable of handling runtime-supplied arguments. 6. In the Main method, add code to: a. Create a TcpClient object. b. Create a byte array object of size 32 bytes. c. Check the number of runtime arguments. If the number of arguments is not one, print out an error message to the console, and exit. The message should state: Please specify a server name in the command line
d. In a try/catch block, verify that the named server computer exists by calling the GetHostByName method of the Dns class. Catch any SocketException exceptions and write out to a console window the message “Cannot find server: ”, followed by the name of the server and the exception’s data. Then exit the application. e. Connect to the named server using port 14. f. Declare a variable of type Stream. g. In a try/catch block, get the stream and assign it to the variable declared in the preceding step. Catch any InvalidOperationException exceptions and write out to a console window the message “Cannot connect to server ”, followed by the server’s name, and then exit the application. h. Read all of the bytes in the stream into the byte array that you created in step 6b, and store the number of bytes that are read in an integer. i. Convert the ASCII-encoded byte array into a string. j. Write out to a console window a message that states the number of bytes received and the current date and time string retrieved from the server.
Module 11: Internet Access
39
k. Close the TcpClient object. l. Write out to a console window the following message: Press Enter to exit. m. Do a read from the console to wait for the Enter key and exit the application. 7. Build the client application.
! Test the client and server applications 1. Ensure that there are no server applications running on the computer using port 14, for example, other instances of the server application. Tip You can display your computer’s current TCP/IP network connections by running the program netstat in a command prompt window. The following example shows that port 7 is in use by a process with PID 1504: C:\>netstat -o -n -a Active Connections Proto TCP
Local Address 0.0.0.0:7
Foreign Address 0.0.0.0:0
State PID LISTENING 1504 …
2. Run the server application in a Visual Studio .NET Command Prompt window or in the Visual Studio .NET debugger. 3. Run the client application in another Visual Studio .NET Command Prompt window or in another Visual Studio .NET debugger. Do not specify any runtime arguments. 4. Note that the client application returns with the proper error message, as follows: Please specify a server name in the command line
5. Run the client application as directed in step 3 with a runtime argument that contains a nonexistent server name. For example, assuming that there is no computer named foo on the network, enter the following text in a Visual Studio .NET Command Prompt window: datetimeclient foo
40
Module 11: Internet Access
6. Note that the client application returns with the proper error message, for example: Cannot find server: foo …
7. Run the client application as directed in step 3 with a runtime argument that contains the name of the computer on which the Datetimeserver application is running. If the server application is running on your current computer, specify the local computer as follows: datetimeclient localhost
Note that the client application outputs the number of bytes received and the current date and time string retrieved from the server.
Module 11: Internet Access
41
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
Internet Application Scenarios
!
The WebRequest and WebResponse Model
!
Application Protocols
!
Handling Errors
!
Security
!
Best Practices
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. Name the four parts of the following URI: http://www.microsoft.com/default.htm?foo=bar Scheme identifier
http
Server identifier
www.Microsoft.com
Path identifier
/default.htm
Query String
?foo=bar
2. Write the line of code that creates a WebRequest to the URI in question 1. WebRequest req = WebRequest.Create ("http://www.microsoft.com/default.htm?foo=bar");
3. Write the line of code that gets a WebResponse from the WebRequest in question 2. WebResponse resp = req.GetResponse();
4. Write the line of code that gets a Stream from the WebResponse in question 3. Stream respstrm = resp.GetResponseStream();
42
Module 11: Internet Access
5. Name the type of Web-specific exceptions that are thrown by the GetResponse method. WebException
6. State how a WebRequest can be made to use the Secure Socket Layer (SSL) protocol. URI begins with https.
7. Name at least three authentication methods that are supported by the .NET Framework. Basic Digest Negotiate NTLM Kerberos
Module 11: Internet Access
43
Course Evaluation Topic Objective
To direct students to a Web site to complete a course evaluation.
Lead-in
Between now and the end of the course, you can go to the Web site listed on this page to complete a course evaluation.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Your evaluation of this course will help Microsoft understand the quality of your learning experience. At a convenient time before the end of the course, please complete a course evaluation, which is available at http://www.metricsthatmatter.com/survey/. Microsoft will keep your evaluation strictly confidential and will use your responses to improve your future learning experience.
THIS PAGE INTENTIONALLY LEFT BLANK
Module 12: Serialization
Contents Overview
1
Serialization Scenarios
2
Serialization Attributes
4
Object Graph
5
Serialization Process
7
Serialization Example
9
Deserialization Example
10
Custom Serialization
12
Custom Serialization Example
14
Security Issues
17
Lab 12: Serialization
18
Review
27
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 12: Serialization
iii
Instructor Notes Presentation: 30 Minutes Lab: 45 Minutes
After completing this module, students will be able to: • Write an application that serializes and deserializes an object graph by using either a binary or Simple Object Access Protocol (SOAP) XML format.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft® PowerPoint® file 2349B_12.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Complete the lab.
iv
Module 12: Serialization
Module Strategy Use the following strategy to present this module: !
Serialization Scenarios Discuss briefly how serialization is used in scenarios such as persisting inmemory objects to disk and in remoting. Mention the Microsoft .NET Framework’s support for serialization and deserialization as an introduction to the rest of the module.
!
Serialization Attributes Explain how to mark a class with serialization attributes in C# by using the Serializable attribute. Also cover the NonSerialized attribute.
!
Object Graph Use the diagram on the Object Graph slide to discuss the object graph concept and the algorithm that is used to serialize or deserialize an object graph.
!
Serialization Process Introduce the classes that are used in the serialization process.
!
Serialization Example Discuss the code example on the Serialization Example slide in which default serialization is performed on a graph of objects, whose root is an ArrayList, and the serialized stream is written to a FileStream in binary format.
!
Deserialization Example Use the preceding serialization example to show how to create a clone of the graph by deserializing it.
!
Custom Serialization Discuss when to use custom serialization and the implementation details of using the ISerializable interface to perform custom serialization and deserialization.
!
Custom Serialization Example Show how to provide custom serialization for a class named ISerializableExample.
!
Security Issues Because the serialization engine handles both the public and private state of the objects that are passed to it, emphasize that streams with private data should be treated carefully, and that some form of encryption should be used for sensitive data, before that data is transmitted over the wire or persisted to disk.
Module 12: Serialization
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
!
Serialization Scenarios
!
Serialization Attributes
Lead-in
!
Object Graph
!
Serialization Process
!
Serialization Example
!
Deserialization Example
!
Custom Serialization
!
Custom Serialization Example
!
Security Issues
In this module, you will learn about serialization and learn how to write an application that serializes and deserializes an object graph by using a binary or SOAP XML format.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Serialization is the process of converting a graph of objects into a linear sequence of bytes. That sequence of bytes can be sent elsewhere, for example, to a remote computer, and be deserialized, thereby making a clone in the remote memory of the original graph of objects. After completing this module, you will be able to: • Write an application that serializes and deserializes an object graph by using either a binary or Simple Object Access Protocol (SOAP) XML format.
2
Module 12: Serialization
Serialization Scenarios Topic Objective
To show how serialization is used.
Lead-in
Serialization is used in some very common scenarios, such as persisting a graph of objects to disk or to objects in another process.
!
Persistence "
!
Store and retrieve a graph of objects to and from a file
Remoting "
Pass by value arguments that are transmitted between processes
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Serialization is used in some very common scenarios, such as persisting a graph of objects to disk or to objects in another process. The Microsoft® .NET Framework provides support for serialization and deserialization.
Persistence Consider a simple single-user desktop application, such as a two-dimensional drafting package that is built by using object-oriented techniques. In such an application, a drawing is composed of different kinds of graphical objects of various types. The application represents the drawing as a graph of in-memory objects. One object represents the root of the entire picture. For example, a simple round table could be represented by a graph that consists of a root object that is an instance of a circle class. This instance of the circle class has four children that are each instances of a line class. To save the entire drawing to a disk file so the drawing can be restored after rebooting the computer, you could force each class to implement a serialize and corresponding deserialize method. However, this approach is a potentially burdensome task for the application programmer.
Serialization in the .NET Framework The .NET Framework common language runtime reduces the amount of work that is involved in serialization. At run time, the common language runtime maintains metadata that allows serialization code to discover the types and values of all fields and properties that make up any object. Using the common language runtime, an application requires only a few lines of code to serialize a object, such as the drawing described in the preceding paragraphs, and write it to a file, or to deserialize such a file into an in-memory graph of objects.
Module 12: Serialization
3
Remoting In distributed computing, objects in one process may need to communicate with objects in another process. In the .NET Framework, the term remoting is typically used to refer to the process in which an object invokes a method in another object that is not in the same application domain. If the remote method takes as one of its arguments an object that lies at the root of a graph of objects, and if all of the objects in the graph are marked as remote-by-value, you must serialize a copy of the object graph and pass the graph to the remote object. The remote object must then deserialize the argument into an in-memory graph of objects. For more information about remoting, see Module 13, “Remoting and Web Services,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
4
Module 12: Serialization
Serialization Attributes Topic Objective
To explain how to mark a class with serialization attributes in C#.
Lead-in
If you are writing a class, you should be aware of serialization.
!
To Mark a Class, Use Serializable Attribute
[Serializable] [Serializable] public public class class MyClass MyClass {} {} !
To Skip Specified Members, Use NonSerialized Attribute
[Serializable] [Serializable] public public class class MyClass MyClass {{ [NonSerialized] [NonSerialized] int int _cashSize; _cashSize; //... //... }} !
To Provide Custom Serialization, Implement ISerializable
*****************************ILLEGAL FOR NON-TRAINER USE****************************** If you are writing a class, you should be aware of serialization. The common language runtime’s serialization services are built with the assumption that a type is not serializable unless the type is specifically marked as serializable. In the simplest case, all you need to do is mark a class as serializable because the runtime metadata understands everything about each object’s layout in memory, and its field and property definitions. To mark a type as serializable in C#, you use the Serializable attribute, which is a reserved custom attribute. All fields in classes with this attribute are serialized, even private fields. In the following example, MyClass is marked as serializable: [Serializable] public class MyClass {}
For slightly more complex classes that have state that is invalid to serialize, the runtime provides support for marking those fields and properties as transient. For example, the following code uses the NonSerialized attribute to ensure that the _cashSize member of MyClass is not serialized: [Serializable] public class MyClass { [NonSerialized] int _cashSize; //... }
The small set of classes that need to participate in their own serialization and deserialization can provide a custom implementation of the ISerializable interface. For more information about custom serialization, see Custom Serialization in this module.
Module 12: Serialization
5
Object Graph Topic Objective
33
To define an object graph and explain its function.
Dog
Lead-in
An object graph is a set of objects that share a set of references to each other.
44
Cat
Duck
77
99
Horse
11
Mouse
22
Duck
*****************************ILLEGAL FOR NON-TRAINER USE****************************** An object graph is a set of objects with references to each other. Serialization must provide a way to represent the links between the graph’s objects in the serialized stream that is created in the serialization process.
Understanding Object Graphs Serialization of an object graph must provide a way to represent the links between the graph’s objects in the serialized stream that it creates. The value that is held in the field of the in-memory object, which links to another object, is essentially a 32-bit address. This address has meaning only in the owner address space and may change during garbage collection. Therefore, serialization must allocate a unique number to each object in the stream. The illustration in the slide shows a graph of animal objects. Each object is represented as a box with its identification number inside the box and its class name to the right of the box. You can represent the graph of objects that is shown in this illustration with a serialized stream, as in the following example: Dog, 3, ref4, ref7, ref1 || Cat, 4, ref9 || Duck, 7 || Mouse, 1, ref9, ref2 || Horse, 9, ref4 || Duck, 2
The order in which you stream out the objects does not matter, nor does it matter what numbers you assign to the objects. What does matter is that no two objects are assigned the same number. The object numbers are significant only within a serialized stream. They are simply a way to represent the graph topology and to allow you to reconstruct a copy of that graph, perhaps on another computer.
6
Module 12: Serialization
Tracking Object References An algorithm that visits objects one at a time clearly must keep track of which objects it has already visited, for example, by using an internal list. Without due care, the algorithm may incorrectly serialize or deserialize an object graph. For example, in the object graph in the illustration, to avoid entering an infinite loop, you must detect the cycle in the graph that occurs because of the mutual references between Cat 4 and Horse 9. During serialization, you must note that the Cat 4 that is linked to by Dog 3 is the same Cat 4 that is linked to by Horse 9 to ensure that deserialization will result in both Dog 3 and Horse 9 referring to the same Cat 4 object and not to two different copies.
Module 12: Serialization
7
Serialization Process Topic Objective
!
To introduce the classes that are used in the serialization process.
Lead-in
The process of serializing an object graph involves identifying the individual objects in the graph and the relationships between them.
!
Classes Used by the Default Serialization Process "
ObjectIDGenerator – generates IDs for objects
"
ObjectManager – tracks objects as they are being deserialized
Examples of Classes Used with Serialized Streams "
!
FileStream, MemoryStream, NetworkStream
Formatter Class "
Writes or reads data in a specified format to the output or input streams
"
Runtime provides BinaryFormatter and SoapFormatter
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The process of serializing an object graph involves identifying the individual objects in the graph and the relationships between them.
Classes Used by the Default Serialization Process To create and track object ID numbers, serialization uses several classes, as follows: !
ObjectIDGenerator The ObjectIDGenerator class generates IDs for objects. It keeps track of objects that have already been seen, so when you ask for the ID of an object, the ObjectIDGenerator knows whether to return the existing ID, or to generate, and remember, a new ID.
!
ObjectManager The ObjectManager class keeps track of objects as they are being deserialized. During deserialization, queries are made to the ObjectManager to determine whether a reference to an object that is in the serialized stream refers to an object that has already been deserialized or to an object that has not yet been deserialized. A reference to an object that has already been deserialized is called a backward reference. A reference to an object that has not yet been serialized is called a forward reference.
8
Module 12: Serialization
Both the ObjectIDGenerator and ObjectManager classes are pluggable, so you can build your own alternatives. Note During deserialization, fields are returned in the order in which they are returned from reflection. Reflection does not guarantee that it will follow metadata ordering. You can serialize to many different kinds of streams, for example, to a FileStream, a MemoryStream, or a NetStream. The serialized stream’s format is determined by the formatter object that you instantiate. The .NET Framework runtime provides the following formatter classes: !
BinaryFormatter The BinaryFormatter class is used for a compact binary representation.
!
SoapFormatter The SoapFormatter class is used for an XML representation.
Because formatters are pluggable, you can also build your own formatters.
Module 12: Serialization
9
Serialization Example Topic Objective
class class SerializeExample{ SerializeExample{
To provide an example of serialization of an object graph.
Lead-in
This code sample shows how to perform default serialization of a graph of objects, whose root is an ArrayList.
}}
public public static static void void Main(String[] Main(String[] args) args) {{ // // create create the the object object graph graph ArrayList ArrayList ll == new new ArrayList(); ArrayList(); for (int x=0; x< for (int x=0; x< 100; 100; x++) x++) {{ l.Add (x); l.Add (x); }} // // create create the the filestream filestream FileStream FileStream ss == File.Create("foo.bin"); File.Create("foo.bin"); // // create create the the BinaryFormatter BinaryFormatter BinaryFormatter BinaryFormatter bb == new new BinaryFormatter(); BinaryFormatter(); // serialize the graph // serialize the graph to to the the stream stream b.Serialize(s, l); b.Serialize(s, l); s.Close(); s.Close(); }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The following code sample shows how to perform default serialization of a graph of objects, whose root is an ArrayList. The serialized stream is written to a FileStream in binary format. using using using using using
System; System.IO; System.Collections; System.Runtime.Serialization; System.Runtime.Serialization.Formatters.Binary;
class SerializeExample { public static void Main(String[] args) { // create the object graph ArrayList l = new ArrayList(); for (int x=0; x< 100; x++) { l.Add (x); } // create the filestream FileStream s = File.Create("foo.bin"); // create the BinaryFormatter BinaryFormatter b = new BinaryFormatter(); // serialize the graph to the stream b.Serialize(s, l); s.Close(); } }
10
Module 12: Serialization
Deserialization Example Topic Objective
To show how to create a clone of the graph by deserializing it.
Lead-in
The preceding Serialization Example shows how to perform default serialization of a graph of objects, whose root is an ArrayList, with a serialized stream that is written to a FileStream in binary format.
class class DeSerialize DeSerialize {{ public public static static void void Main(String[] Main(String[] args) args) {{ // // open open the the filestream filestream FileStream FileStream ss == File.OpenRead("foo.bin"); File.OpenRead("foo.bin");
}}
// // create create the the formatter formatter BinaryFormatter BinaryFormatter bb == new new BinaryFormatter(); BinaryFormatter(); // // deserialize deserialize ArrayList ArrayList pp == (ArrayList) (ArrayList) b.Deserialize(s); b.Deserialize(s); s.Close(); s.Close(); // // print print out out the the new new object object graph graph // see module text for // see module text for PrintValues’ PrintValues’ code code PrintValues(p); PrintValues(p);
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The preceding Serialization Example shows how to perform default serialization of a graph of objects, whose root is an ArrayList, with a serialized stream that is written to a FileStream in binary format. The following code sample shows how to create a clone of the graph by deserializing it. The root of the clone graph is called p. using using using using using
System; System.IO; System.Collections; System.Runtime.Serialization; System.Runtime.Serialization.Formatters.Binary;
class DeSerialize { public static void Main(String[] args) { // open the filestream FileStream s = File.OpenRead("foo.bin"); // create the formatter BinaryFormatter b = new BinaryFormatter(); // deserialize ArrayList p = (ArrayList) b.Deserialize(s); s.Close(); // print out the new object graph PrintValues(p); }
(Code continued the following page.)
Module 12: Serialization public static void PrintValues( IEnumerable myList ) { System.Collections.IEnumerator myEnumerator = myList.GetEnumerator(); while ( myEnumerator.MoveNext() ) Console.WriteLine( "{0}", myEnumerator.Current ); } }
11
12
Module 12: Serialization
Custom Serialization Topic Objective
To explain what is required to implement ISerializable for performing custom serialization.
Lead-in
This module has so far discussed the default serialization process. However, you may also want to customize the way data from a given object is serialized.
!
!
Customize Serialization by Implementing ISerializable: "
When some of the data is not valid after deserialization
"
When some of the data must be obtained by calculation
ISerializable Requires: "
GetObjectData method, called during serialization, which returns a PropertyBag of type SerializationInfo
"
PropertyBag, which contains the type of the object being serialized and the name/object pairs for the values being serialized
"
A constructor, called during deserialization, which uses SerializationInfo to reconstitute the state of the object
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This module has so far discussed the default serialization process. However, you may also want to customize the way that data from a given object is serialized. Custom serialization can be useful when some of the data that is associated with an object is no longer valid after deserialization. You may want to use custom serialization when working with pointers or hashcodes, or when you want to create data through calculations or other means that allow you to reconstruct the full state of the object during deserialization. To perform custom serialization, you should implement the ISerializable interface on the given object.
Implementation Details Required by ISerializable To implement the ISerializable interface, you implement the GetObjectData method on your object and add a constructor that takes a SerializationInfo and a StreamingContext, as shown in Custom Serialization Example in this module. When GetObjectData is called during serialization, you are responsible for populating a SerializationInfo object. A SerializationInfo object is a PropertyBag that contains the type of the object that is being serialized and the name/object pairs for the values that are being serialized. The Formatter emits the data out onto the wire in the method required by its particular format. You are free to serialize as few or as many fields as you want, but the data that is transmitted must be sufficient to reconstitute the entire state of the object. If the base object of the current class implements ISerializable, it is usually correct to call the base object’s ISerializable.GetObjectData and add any additional fields that are required for serializing the derived class to the returned SerializationInfo.
Module 12: Serialization
13
Deserialization Deserialization occurs during the call to the class’s constructor. If you need to create custom deserialization of an object, you use the object’s SerializationInfo, which has been populated with the type of the object and the name/object pairs that were transmitted over the stream. You are responsible for completely reconstituting the state of the object from this information. If the base class also implements ISerializable, you are responsible for calling the base class’s constructor. The serialization infrastructure will delay the call on this constructor until the entire SerializationInfo has been completed. If, for example, the SerializationInfo that is transmitted references objects A, B, and C, the SerializationInfo that is passed to the constructor will have been populated with references to objects A, B, and C. However, there is no guarantee that any of the objects that are referenced by A, B, or C has been completed. Because there is no guarantee that any of the objects that are referenced by A, B, or C have been completed, you cannot safely call any code on A, B, or C that may require the objects to which they refer. For some objects, this code may include code as simple as GetHashCode. If your code requires you to perform any execution that is based on the value of data that is contained in the objects that are referenced, it is usually best to cache the SerializationInfo and then implement IDeserializationCallback.
14
Module 12: Serialization
Custom Serialization Example [Serializable] [Serializable] public public class class ExampleFoo ExampleFoo :: ISerializable ISerializable {{ public public int int i, i, j, j, k; k; public public ExampleFoo() ExampleFoo() {} {} internal internal ExampleFoo(SerializationInfo ExampleFoo(SerializationInfo si, si, StreamingContext StreamingContext context) context) {{ //Restore //Restore our our scalar scalar values. values. ii == si.GetInt32("i"); si.GetInt32("i"); jj == si.GetInt32("j"); si.GetInt32("j"); kk == si.GetInt32("k"); si.GetInt32("k"); }} public public void void GetObjectData(SerializationInfo GetObjectData(SerializationInfo si, si, StreamingContext StreamingContext context) context) {{ //SerializationInfo //SerializationInfo -- essentially essentially aa property property bag bag //Add //Add our our three three scalar scalar values; values; si.AddValue("i", si.AddValue("i", i); i); si.AddValue("j", si.AddValue("j", j); j); si.AddValue("k", k); si.AddValue("k", k); Type Type tt == this.GetType(); this.GetType(); si.AddValue("TypeObj", si.AddValue("TypeObj", t); t); }} }}
Topic Objective
To provide an example of custom serialization.
Lead-in
This example shows how to provide custom serialization for a class named ISerializableExample.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The following example shows how to provide custom serialization for a class: using using using using using
System; System.IO; System.Runtime.Serialization; System.Runtime.Serialization.Formatters; System.Runtime.Serialization.Formatters.Soap;
public class Sample { public static void Main() { ToFile(); FromFile(); }
(Code continued the following page.)
Module 12: Serialization
15
public static void ToFile() { Console.WriteLine("ToFile"); ExampleFoo fooOut = new ExampleFoo(); fooOut.i = 1; fooOut.j = 20; fooOut.k = 50; Console.WriteLine("i: {0}", fooOut.i); Console.WriteLine("j: {0}", fooOut.j); Console.WriteLine("k: {0}", fooOut.k); IFormatter objFormatterToStream = new SoapFormatter(); Stream toStream = new FileStream("myFoo.xml", FileMode.Create, FileAccess.Write, FileShare.None); objFormatterToStream.Serialize(toStream, fooOut); toStream.Close(); } public static void FromFile() { Console.WriteLine("FromFile"); //Then you can read it back in with code like this: IFormatter objFormatterFromStream = new SoapFormatter(); Stream fromStream = new FileStream("myFoo.xml", FileMode.Open, FileAccess.Read, FileShare.Read); ExampleFoo fooIn = (ExampleFoo) objFormatterFromStream.Deserialize(fromStream); fromStream.Close(); Console.WriteLine("i: {0}", fooIn.i); Console.WriteLine("j: {0}", fooIn.j); Console.WriteLine("k: {0}", fooIn.k); } }
[Serializable] public class ExampleFoo : ISerializable { public int i, j, k; public ExampleFoo() { } internal ExampleFoo(SerializationInfo si, StreamingContext context) { //Restore our scalar values. i = si.GetInt32("i"); j = si.GetInt32("j"); k = si.GetInt32("k"); }
(Code continued on the following page.)
16
Module 12: Serialization public void GetObjectData(SerializationInfo si, StreamingContext context) { //SerializationInfo is essentially a property bag. //Add our three scalar values; si.AddValue("i", i); si.AddValue("j", j); si.AddValue("k", k); Type t = this.GetType(); si.AddValue("TypeObj", t); } }
Outputs: ToFile i: 1 j: 20 k: 50 FromFile i: 1 j: 20 k: 50
Module 12: Serialization
17
Security Issues Topic Objective
To alert students to the need for security when serializing objects.
Lead-in
The serialization engine handles both the public and private state of the objects that are passed to it.
!
Serialization Handles an Object’s Private Data "
If private data is sensitive, consider encrypting the stream before transmitting or saving to a disk
"
System.Security.Cryptography namespace contains classes to perform cryptography The CryptoStream class can be used to encrypt streams of serialized data
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The serialization engine handles both the public and private state of the objects that are passed to it. When serializing an object to a stream, you must remember that the stream now contains the public and private data of the object. If the private data is sensitive, you should treat the stream with particular care. For example, the stream should not be transmitted over the wire or persisted to disk without some form of encryption.
18
Module 12: Serialization
Lab 12: Serialization Topic Objective
To introduce the lab.
Lead-in
In this lab, you will write a client/server application that uses serialization.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: • Create an application that uses serialization as it is implemented by the .NET Framework, to persist an object graph to and from a disk file in both binary and SOAP XML format.
Lab Setup Starter and solution files are associated with this lab. The starter files are in the folder \Labs\Lab12\Starter, and the solution files are in the folder \Labs\Lab12\Solution.
Scenario In this lab, you will create a Microsoft Visual Studio® .NET console application that uses the common language runtime’s ability to serialize an object graph in memory to disk. You will create binary and SOAP formatter implementations of the application. In the first exercise, you will create a singly-linked linked list, which you will fill with values and serialize to a file on disk. You will then deserialize the list from the file on disk, thus restoring the list to an object graph in memory. During deserialization, the elements within the list are swapped multiple times. In the second exercise, you will modify the application to demonstrate the ability of the .NET Framework’s serialization mechanism to handle object graphs that contain multiple references to the same object and that contain objects that have mutual references, which can create cycles in the graph.
Estimated time to complete this lab: 45 minutes
Module 12: Serialization
19
Exercise 1 Creating the Basic Serialization Application In this exercise, you will modify the Serialization application to provide methods to serialize and deserialize a linked list.
! To create the basic Serialization application in binary format 1. In the \Labs\Lab12\Starter directory, open the Serialization project in Visual Studio .NET and examine the Serialize.cs and LinkedList.cs source files. 2. In Serialize.cs, locate the SaveListToDisk method, and add code to: a. Create a Stream object that is initialized to a file named Linkedlist.bin by using the static File.Create method. b. Create a new BinaryFormatter object. c. Invoke the method of the BinaryFormatter that serializes the LinkedList parameter to the stream. d. Close the file. 3. In Serialize.cs, locate the LoadListFromDisk method and add code to: a. Create a Stream object that is initialized to a file named Linkedlist.bin by using the static File.OpenRead method. b. Create a new BinaryFormatter object. c. Invoke the method of the BinaryFormatter that deserializes the stream into a LinkedList named list2. d. Close the file. e. Output the contents of list2 to console by calling the LinkedList method Draw(). f. Return list2. 4. Build the Serialization application.
20
Module 12: Serialization
5. Step through the application in the Visual Studio .NET debugger, and note that due to the random swapping your output may vary slightly from the following: Entering Scope 1 Creating and filling List .. List: 1 2 3 4 5 6 7 8 9 Serializing LinkedList to file .. Leaving Scope 1 Entering Scope 2 Deserializing LinkedList from binary file .. List: 1 2 3 4 5 6 7 8 9 Swapping Entries Swapping 1 and 2 Swapping 3 and 4 Swapping 5 and 6 Swapping 7 and 8 List: 2 1 4 3 6 5 8 7 9 Serializing LinkedList to file .. Leaving Scope 2 Entering Scope 3 Deserializing LinkedList from binary file .. List: 2 1 4 3 6 5 8 7 9 Swapping Random Entries Swapping 2 and 8 Swapping 4 and 3 Swapping 4 and 3 Swapping 6 and 5 Swapping 1 and 4 Swapping 1 and 8 Swapping 2 and 4 Swapping 3 and 5 Swapping 7 and 2 Swapping 5 and 4 Swapping 6 and 2 Swapping 9 and 5 Swapping 1 and 7 Swapping 7 and 8 Swapping 8 and 2 List: 2 1 7 4 3 8 9 6 5 Serializing LinkedList to file .. Leaving Scope 3 Entering Scope 4 Deserializing LinkedList from binary file .. List: 2 1 7 4 3 8 9 6 5 Removing Entries Removing 1 Removing 2 Removing 3 List: 7 4 8 9 6 5 Serializing LinkedList to file .. Leaving Scope 4
Module 12: Serialization
21
Note You can open a binary file using Visual Studio .NET by running Microsoft Windows® Explorer, Explorer.exe, and navigating to the binary file’s icon. Then either right-click on the icon and choose Open With Microsoft Visual Studio .NET, or drag and drop the icon into a running Visual Studio .NET application’s left-hand File View pane. 6. Using Visual Studio .NET, open and visually examine the contents of the Linkedlist.bin file in the bin\Debug subdirectory, and note the serialized list data’s format, structure, and size.
! To create the basic Serialization application in SOAP format 1. Add code to the SaveListToDisk and LoadListFromDisk methods to use the SoapFormatter to write to and read the LinkedList parameter to and from a file named Linkedlist.soap. 2. Build and execute the application, and confirm that the console output remains correct. 3. Using Visual Studio .NET, open and visually examine the contents of the Linkedlist.soap file in the bin\Debug subdirectory, and note the serialized list data’s format and size. Compare this list data’s format, structure, and size to the Linkedlist.bin list data’s format, structure, and size.
22
Module 12: Serialization
Exercise 2 Handling Complex Object Graphs In this exercise, you will modify the Serialization application to create and manipulate an object graph that has multiple references to the same object and has reference cycles.
! To create the complex Serialization application in SOAP format 1. In Serialize.cs, locate the Ser class and add a static method named SaveArrayToDisk that returns a void and that takes as its single argument an array of type Node. Within SaveArrayToDisk, add code to: a. Iterate through the array of nodes, and, for each node, print out the array’s index, the node’s value, and the value of the Node object in the node’s NextNode field. b. Write out a message to the console that states that the application is serializing the array to a file. c. Create a stream object that is initialized with the file named Array.soap by using the static File.Create method. d. Create a SoapFormatter object. e. Serialize the node array to the file. f. Close the file. 2. In the Ser class, add a static method named LoadArrayFromDisk that takes no arguments and returns an array of type Node. Within LoadArrayFromDisk, add code to: a. Write out a message to the console that states that the application is deserializing an array from the file. b. Create a stream object that is initialized with the file named Array.soap by using the static File.OpenRead method. c. Create a SoapFormatter object. d. Using the SoapFormatter object, deserialize the stream into an array of type Node. e. Close the file. f. Iterate through the array of nodes, and, for each node, print out the array’s index, the node’s value, and the value of the Node object in the node’s NextNode field. g. Return the array of type Node.
Module 12: Serialization
23
3. In the Ser class add a static public method named Scope5 that takes no arguments and returns void. Within Scope5, add code to: a. Create two objects of type Node named n0 and n1 that are initialized to values 0 and 1 respectively. b. Write out a message to the console that states that the application is entering Scope5 and creating a graph cycle. c. Assign n1 to the NextNode field of n0. d. Assign n0 to the NextNode field of n1. e. Create an array of type Node that is initialized to contain references to the three objects: n0, n1, and n0. Note that index 0 and 2 refer to the same object. f. Call the SaveArrayToDisk method and pass in the array of type Node. g. Write out a message to the console that states that the application is leaving Scope5. 4. In the Ser class, add a static public method named Scope6 that takes no arguments and returns void. Within Scope6, add code to: a. Write out a message to the console that states that the application is entering Scope6 and creating a graph cycle. b. Assign to a variable named nodes of type array of Node the array that is returned by the LoadArrayFromDisk method. The array references the objects: n0, n1, and n0 that were created in step 1. Note that index 0 and 2 refer to the same object. c. Write out a message to the console that states that the value of n0 is changing to 42 and that this change should result in a change in the value in both locations in the array. d. Assign the integer 42 to the Value field of nodes[0]. e. Write out a message to the console that states that the array’s structure should be preserved during serialization and deserialization. f. Call SaveArrayToDisk to persist nodes to the disk. g. Assign to a variable named nodes2 of type array of Node the array that is returned by the LoadArrayFromDisk method. h. Write out a message to the console that states that the value of n0 is being incremented. i. Increment by 1 the value of the first element of nodes2. j. Call SaveArrayToDisk, and pass nodes2. k. Write out a message to the console that states that the application is leaving Scope6. 5. In the Main method of Ser, and after the call to the Scope4 method, add calls to Scope5 and Scope6. 6. Build the Serialization application. 7. Step through the application in the Visual Studio .NET debugger, and note console output similar to the following output:
24
Module 12: Serialization Entering Scope 1 Creating and filling List .. List: 1 2 3 4 5 6 7 8 9 Serializing LinkedList to file .. Leaving Scope 1 Entering Scope 2 Deserializing LinkedList from soap file .. Deserializing LinkedList from binary file .. List: 1 2 3 4 5 6 7 8 9 Swapping Entries Swapping 1 and 2 Swapping 3 and 4 Swapping 5 and 6 Swapping 7 and 8 List: 2 1 4 3 6 5 8 7 9 Serializing LinkedList to file .. Leaving Scope 2 Entering Scope 3 Deserializing LinkedList from soap file .. Deserializing LinkedList from binary file .. List: 2 1 4 3 6 5 8 7 9 Swapping Random Entries Swapping 8 and 7 Swapping 7 and 5 Swapping 6 and 5 Swapping 2 and 1 Swapping 1 and 8 Swapping 4 and 2 Swapping 2 and 3 Swapping 3 and 5 Swapping 9 and 4 Swapping 9 and 4 Swapping 6 and 8 Swapping 2 and 4 Swapping 2 and 1 Swapping 9 and 7 Swapping 1 and 5 List: 6 5 1 4 3 9 8 2 7 Serializing LinkedList to file .. Leaving Scope 3 Entering Scope 4 Deserializing LinkedList from soap file .. Deserializing LinkedList from binary file .. List: 6 5 1 4 3 9 8 2 7 Removing Entries Removing 1 Removing 2 Removing 3 List: 6 5 4 9 8 7 Serializing LinkedList to file .. Leaving Scope 4
(Code continued on the following page.)
Module 12: Serialization
25
Entering Scope 5 Creating a circular reference: n0's NextNode is n1 and n1's NextNode is n0 Also adding in a third node, n2, that is another reference to n0 Node 0 Value: 0 Value of NextNode Ref: 1 Node 1 Value: 1 Value of NextNode Ref: 0 Node 2 Value: 0 Value of NextNode Ref: 1 Serializing Array to file .. Leaving Scope 5 Entering Scope 6 Deserializing Array from file .. Node 0 Value: 0 Value of NextNode Ref: 1 Node 1 Value: 1 Value of NextNode Ref: 0 Node 2 Value: 0 Value of NextNode Ref: 1 changing value of node n0 to 42 should change value in both locations in array array's structure should be preserved during serialization and deserialization
Node 0 Value: 42 Value of NextNode Ref: 1 Node 1 Value: 1 Value of NextNode Ref: 42 Node 2 Value: 42 Value of NextNode Ref: 1 Serializing Array to file .. Deserializing Array from file .. Node 0 Value: 42 Value of NextNode Ref: 1 Node 1 Value: 1 Value of NextNode Ref: 42 Node 2 Value: 42 Value of NextNode Ref: 1 incrementing value of node n0 Node 0 Value: 43 Value of NextNode Ref: 1 Node 1 Value: 1 Value of NextNode Ref: 43 Node 2 Value: 43 Value of NextNode Ref: 1 Serializing Array to file .. Leaving Scope 6
26
Module 12: Serialization
8. Using Visual Studio .NET, open and visually examine the Array.soap file in the bin\Debug subdirectory, and note the serialized array data’s format, structure, and size.
Module 12: Serialization
27
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
Serialization Scenarios
!
Serialization Attributes
!
Object Graph
!
Serialization Process
!
Serialization Example
!
Deserialization Example
!
Custom Serialization
!
Custom Serialization Example
!
Security Issues
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. Declare a serializable class named Foo with two integer fields F1 and F2 in which F2 is transient and should not be serialized. [Serializable] public class Foo { int F1; [NonSerialized] int F2;
2. Name and describe the two kinds of formatters that the .NET Framework provides. BinaryFormatter for a compact binary SoapFormatter for XML representation
3. Describe what a class should do to provide custom serialization. The class should inherit from the ISerializable interface, implement the interface’s GetObjectData method, and provide a constructor that takes SerializationInfo and StreamingContext parameters.
4. What kind of data that is not normally accessible by clients can be made visible by serialization? Private object state
THIS PAGE INTENTIONALLY LEFT BLANK
Module 13: Remoting and XML Web Services Contents Overview
1
Remoting
2
Remoting Configuration Files
19
Demonstration: Remoting
22
Lab 13.1: Building an Order-Processing Application by Using Remoted Servers
28
XML Web Services
36
Lab 13.2: Using an XML Web Service
48
Review
54
Course Evaluation
56
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 13: Remoting and XML Web Services
Instructor Notes Presentation: 120 Minutes Lab: 105 Minutes
After completing this module, students will be able to: !
Write and configure distributed applications that use .NET Remoting.
!
Create an XML Web service by using Microsoft® Visual Studio® .NET and ASP.NET.
!
Consume an XML Web service by using the Web Services Description Language tool (Wsdl.exe).
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft PowerPoint® file 2349B_13.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Practice the demonstrations.
!
Complete the lab.
iii
iv
Module 13: Remoting and XML Web Services
Demonstrations This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for the student notes.
Remoting In this demonstration, you will show students how a client application uses .NET Remoting to make a method call on an object in a server application. The code for this demonstration is contained in one project and is located in \Democode\Mod13\Demo13.1. In addition, the code for the individual demonstration is provided in the student notes.
Using Visual Studio .NET to Create an XML Web Service In this demonstration, you will show students how to use Visual Studio .NET to create an XML Web service. The code for this demonstration is contained in one project and is located in \Democode\Mod13\Demo13.2. In addition, the code for the individual demonstration is provided in the student notes. In both of the preceding demonstrations, use the debugger to step through the code while you point out features.
Module 13: Remoting and XML Web Services
Module Strategy Use the following strategy to present this module: !
Remoting Use the diagram on the Remoting Overview slide to introduce the services that are provided by the Microsoft .NET Framework for use with remoting. You will cover each of these services in more detail in the subsequent slides in this section. Explain how channels and formatters are used to transmit data. Discuss how the .NET Framework supports server-side and client-side activation of remote objects and describe the differences between server-side and clientside activation. Explain how to control the lifetime of client-activated remote objects by using a leasing mechanism. Discuss how objects are marshaled in .NET Remoting. Explain how to register and activate a remote object from the server side and the client side. Conclude this section with a brief discussion of client compilation techniques.
!
Remote Configuration Files Discuss the use of configuration files in remoting. Do not spend time on the .NET Remoting configuration file format; instead refer students to the .NET Framework Software Developer’s Guide (SDK) documentation. Because the module is long, conclude this part of the lecture and instruct students to do Lab 13.1.
!
XML Web Services Explain how to use Visual Studio .NET to implement an ASP.NET XML Web service and how to access the XML Web service from a Web browser and a client application. Use the Using Visual Studio .NET to Create an XML Web Service demonstration to illustrate the concepts that are covered in this section. Introduce the XML Web service discovery process and the tools that are available for discovery.
v
Module 13: Remoting and XML Web Services
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn about distributed applications that use .NET Remoting.
!
Remoting
!
Remoting Configuration Files
!
XML Web Services
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Microsoft® .NET Remoting provides a framework that allows objects to interact across remoting boundaries, for example, across application domains (AppDomain). XML Web services is a term for communication that uses industry-standard HTTP and Simple Object Access Protocol (SOAP) protocols. You can implement XML Web services by using remoting and by using ASP.NET. After completing this module, you will be able to: !
Write and configure distributed applications that use .NET Remoting.
!
Create an XML Web service by using Microsoft Visual Studio® .NET and ASP.NET.
!
Consume an XML Web service by using the Web Services Description Language tool (Wsdl.exe).
2
Module 13: Remoting and XML Web Services
" Remoting Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
This section shows how .NET Remoting supports communication in various scenarios.
!
Remoting Overview
!
Channels and Formatters
!
Activation and Proxies
!
Lease-Based Lifetime
!
Object Marshaling
!
Server Side
!
Client Side
!
Client Compilation Techniques
*****************************ILLEGAL FOR NON-TRAINER USE****************************** .NET Remoting supports communication in the following scenarios: !
Between objects in different application domains
!
In different processes
!
On different computers
The common language runtime remoting infrastructure provides a rich set of classes that enable you to ignore most of the complexities of deploying and managing remote objects. Even with applications that run under different runtime environments, the process of calling methods on remote objects is almost identical to the process of calling local methods.
Module 13: Remoting and XML Web Services
3
Remoting Overview Topic Objective
To provide an overview of .NET Remoting.
Lead-in
Client AppDomain
The .NET Framework provides several services that are used in remoting.
Client Object Server Proxy
Formatter
Channel
Server AppDomain Formatter
Channel
Server Object
Remoting Boundary *****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework provides several services that are used in remoting: !
Communication channels that are responsible for transporting messages to and from remote applications
!
Formatters that encode and decode messages before they are transported by the channel
!
Proxies that forward remote method calls to the proper object
!
Remote object activation and lifetime support
Note Because the .NET context mechanism is beyond the scope of this course, this module does not cover remoting calls between contexts.
4
Module 13: Remoting and XML Web Services
Channels and Formatters Topic Objective
To explain how channels and formatters are used to transmit data.
!
Channels Transport Messages To and From Remote Objects
!
Client Selects a Channel That Is Registered on the Server #
Lead-in
#
Channels are used to transport messages to and from remote objects. Formatters are used to encode and serialize data into messages before transmission over a channel.
!
Before calling a remote object, the client registers the channel Channels are registered on a per application domain basis One computer cannot have multiple channels listening to same port
.NET Provides Implementation of HTTP and TCP Channels #
HTTP Channel default: SOAP protocol to transport XML messages
#
TCP Channel default:TCP protocol to transport binary messages Faster than HTTP SOAP Web Services but not as open
!
Example: Programmatic Registration of TCP Channel on Port 8085
using using System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels; using using System.Runtime.Remoting.Channels.Tcp; System.Runtime.Remoting.Channels.Tcp; TcpChannel TcpChannel chan chan == new new TcpChannel(8085); TcpChannel(8085); ChannelServices.RegisterChannel(chan); ChannelServices.RegisterChannel(chan);
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Channels are used to transport messages to and from remote objects. When a client calls a method on a remote object, the parameters, as well as other details that are related to the call, are transported through the channel to the remote object. Any results from the call are returned back to the client in the same way. Formatters are used to encode and serialize data into messages before they are transmitted over a channel.
Channel Selection Because a client can use any of the channels that are registered on the server to communicate with a remote object, you can select the channels that best suit your needs. You can also customize any existing channel or build new ones that use different communication protocols. Channel selection is subject to the following rules: !
Channels must be registered before objects are registered. At least one channel must be registered with the remoting infrastructure before a remote object can be called.
!
Channels are registered on a per application domain basis. A single process may contain multiple application domains. When a process dies, all channels that it registers are automatically destroyed.
!
It is not valid to register a channel that listens on the same port on which another channel is currently listening. Though channels are registered on a per application domain basis, different application domains on one computer cannot register the same channel that listens on the same port.
Module 13: Remoting and XML Web Services
5
Clients can communicate with a remote object by using any registered channel. The remoting framework ensures that the remote object is connected to the proper channel when a client attempts to connect to it. The client is responsible for specifying a channel before it attempts to communicate with a remote object. To specify a channel, you can use a .NET Remoting configuration file, or you can call the RegisterChannel method on the ChannelServices class. The .NET Framework provides support for HTTP, TCP, and SMTP channels. Because .NET Remoting channels are pluggable, you can write and plug in additional channels with unique transport and encoding requirements.
Formatters Used to Encode and Decode Messages Each channel provides a default formatter. However you can specify the formatter that you wish to use. Note A Uniform Resource Identifier (URI) is a compact representation of a resource that is available to your application through the Internet. You may be more familiar with the term URL, which stands for Uniform Resource Locator. URLs form a subset of the more general URI naming scheme. A URL identifies an Internet resource that has a Web page address.
HTTP Channel By default, the HTTP channel uses the SOAP protocol to transport messages to and from remote objects. All messages are passed through the SOAP formatter, where the message is changed into XML and serialized, and the required SOAP headers are added to the stream. Alternatively, you can specify the binary formatter, which results in a binary data stream. In either case, the data stream is then transported to the target Uniform Resource Identifier by using the HTTP protocol. You can create industry-standard XML Web services by using the HTTP channel with the default SOAP formatter.
TCP Channel By default, the TCP channel uses a binary formatter to serialize all messages to a binary stream and transports the stream to the target Uniform Resource Identifier by using the TCP protocol. Alternatively, you can specify the SOAP formatter, which results in an XML data stream. You can obtain better performance by using the TCP channel with the default binary formatter than you can by using XML Web services.
6
Module 13: Remoting and XML Web Services
Code Example The following code example shows how to programmatically register a TCP Channel on port 8085 by using ChannelServices.RegisterChannel: using System; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; //... TcpChannel chan = new TcpChannel(8085); ChannelServices.RegisterChannel(chan); //...
Security Issues If you have a choice between using the HttpChannel and the TcpChannel, it is recommended that you use the HttpChannel and host your remote objects in Internet Information Services (IIS), no matter what the user authentication and authorization models are. IIS hosting provides support for wire-level protection using Secure Sockets Layer (SSL) and authentication using Integrated Windows Authentication (formerly NTLM authentication) or Kerberos. For configuring SSL and authentication, see the IIS documentation. The TcpChannel, as an implementation of the Transmission Control Protocol (TCP), does not have default support for some of the robust authentication standards that the HTTP standard does. Within a secured environment (one that has wire-level protection such as IPSec), the high-speed TcpChannel can be used, but it is not recommended over the Internet or a nonsecure intranet. You can use cryptography to protect data from being viewed or modified and thereby provide secure channels of communication over otherwise insecure channels. The System.Security.Cryptography namespace contains a set of classes that allow you to perform both symmetric and asymmetric cryptography, create hashes, and provide random number generation. Successful cryptography is the result of combining these tasks. See the .NET SDK for documentation about the System.Security.Cryptography namespace.
Module 13: Remoting and XML Web Services
7
Activation and Proxies Topic Objective
To show how the remoting framework supports serverside and client-side activation of remote objects and to describe the differences between serverside and client-side activation.
!
Before Using a Remote Object, the Client Must Activate It #
!
!
Lead-in
The remoting framework supports server-side and client-side activation of remote objects.
!
By calling new, Activator.CreateInstance, or Activator.GetObject
Activation Returns Proxy Used by Client to Access Remote Object #
Proxy represents remote object in client’s AppDomain
#
Proxy forwards client’s calls, and returns results and exceptions
Server-Side Activation – Automatic Instantiation by Server #
Single call object handles only one request (stateless)
#
Singleton object services multiple clients and requests (stateful)
Client-Side Activation – Instantiation by Explicit Client Call #
State maintained between method calls for specific client instance
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The remoting framework supports server-side and client-side activation of remote objects. Server-side activation means that when a client attempts to access the object, the object is instantiated on the server automatically. Client-side activation, on the other hand, means that the object is instantiated in response to a deliberate activation request from a client. You can select the best activation model to control the instantiation and lifetime of a remote object.
Methods of Remote Object Activation Before a client can use a remote object, the remote object must be activated, and the client must obtain a proxy to access the remote object. You can activate a remote object by calling new or by calling the following methods of the Activator class: !
Activator.CreateInstance Used to create an object instance
!
Activator.GetObject Generally used to connect to an object that is already running at a specified Uniform Resource Identifier
8
Module 13: Remoting and XML Web Services
The Role of Proxies in Remote Object Interaction When a client activates a remote object, the client obtains a proxy to the class instance on the server. Any interaction with a remote object occurs by reference through the proxy. The proxy object acts as a representative of the remote object and ensures that all calls that are made on the proxy are forwarded to the correct remote object instance. All methods that are called on the proxy are automatically forwarded to the remote class, and any results are returned to the client. From the client’s perspective, this process is identical to the process of making a local call. Any exceptions that are thrown by the remote object are automatically returned to the client. Because those exceptions are returned to the client, the client can use try/catch blocks around sections of code to trap and handle exceptions.
Server-Side Activation Server-side activation supports single call and singleton modes of activation.
Single Call Objects A single call object services only one request. Single call objects are useful in scenarios in which: !
The overhead of creating an object is not significant.
!
Objects are configured in a load-balanced fashion.
!
State information is usually not needed between calls.
Because single call objects cannot hold state information between method calls, they are sometimes referred to as stateless objects.
Singleton Objects A singleton object services multiple clients and multiple requests. Therefore, a singleton object can store state information between client invocations. Singleton objects are useful when you want to share data explicitly between clients and method invocations, and when the overhead of creating and maintaining objects is substantial. Because singleton objects can maintain their state over a prolonged period of time, they are sometimes referred to as stateful objects.
Module 13: Remoting and XML Web Services
9
Client-Side Activation Client-activated objects are activated on a request from the client. This method of activating server objects is similar to the classic COM coclass activation. The activation process is as follows: 1. When the client requests a server object, an activation request message is sent to the remote application. 2. The server then creates an instance of the requested class and returns an ObjRef object to the client application that invoked it. 3. A proxy is then created on the client side by using the ObjRef object. A client-activated object can store state information between method calls for its specific client. However, state information is not shared between multiple client-activated objects. Each request for a remote object instance returns a proxy to an independent instance of the server type. A useful function of client-activated objects is that constructor arguments can be passed by the local application to the constructor of the object in the remote application.
10
Module 13: Remoting and XML Web Services
Lease-Based Lifetime Topic Objective
To explain how using a leasing mechanism can extend the lifetime of clientactivated remote objects.
!
A Leasing Mechanism Controls the Lifetime of a ClientActivated Remote Object
Lead-in
!
An Object’s Lease Time Can Be Extended
!
When an Object’s Lease Time Reaches Zero
You can control the lifetime of client-activated remote objects by using a leasing mechanism.
#
The object is disconnected from remoting infrastructure
#
The object may be garbage-collected
#
A lease provides an alternative to reference counting
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can control the lifetime of client-activated remote objects by using a leasing mechanism. When an object is first created, it is given a lease time. When the lease time of the object reaches zero, the object is disconnected from the remoting infrastructure. After the references to the object from within the object’s application domain have been freed, the object may be collected when the next garbage collection occurs. You can extend the lease on an object by using a number of mechanisms. For more information about extending the lease on an object, see “Lease-Based Lifetime Concepts” in the .NET Framework Software Development Kit (SDK) documentation. You can use leases to manage the lifetime of remote objects as an alternative to reference counting, which tends to be complex and inefficient over unreliable network connections. A potential disadvantage of leasing is that the lifetime of a remote object may be extended for longer than is required. However, the advantages of reducing network traffic that is devoted to reference counting and the pinging of clients outweigh the disadvantage of the extended lifetimes of remote objects.
Module 13: Remoting and XML Web Services
11
Object Marshaling Topic Objective
To explain how objects are marshaled in .NET Remoting.
!
Objects Instantiated Remotely Are Returned by Reference and Accessed by the Client Through a Proxy
Lead-in
!
Remote Call Parameters, Return Values, and Fields Can Be:
Because calls to a remote object can cause other objects to be passed across a remoting boundary, you should understand how objects are marshaled in .NET Remoting.
#
#
#
Marshal-by-value objects – A copy of the object is passed from one AppDomain to another - Value types and classes that are serializable Marshal-by-reference objects – A reference to the object is passed from one AppDomain to another - Classes that derive from the System.MarshalByRefObject class Not-marshaled objects – Objects suitable for local use only - Any class that is not Marshal-By-Value or Marshal-By-Reference
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Because calls to a remote object can cause other objects to be passed across a remoting boundary, you should understand how objects are marshaled in .NET Remoting. Inside the remoting boundary of the caller, for example, inside a single application domain, objects are passed by reference, and primitive data types are passed by value. Application domains are hard boundaries. Applications running in different application domains share no information, no global variables, and no static fields on classes. As mentioned in Remoting in this module, .NET Remoting is used to communicate between objects in different application domains. All objects that are instantiated remotely are returned by reference. When a client instantiates a remote object, it receives a proxy to the class instance on the server. All methods that are called on the proxy are automatically forwarded to the remote class, and any results are returned to the client. The following examples show scenarios where objects are passed across remoting boundaries because of calls to remote objects: !
Object parameters in a method call, such as myObj in: public int myRemoteMethod (MyRemoteObject myObj)
!
Object return values of method calls, such as MyRemoteObject instances in: public MyRemoteObject myRemoteMethod(String myString)
!
Objects that result from property or field access of a remote object, such as instances that are accessed in the myNestedObject field of myObj in: myObj.myNestedObject
All objects in the .NET Framework fall into three general remoting categories: marshal-by-value, marshal-by-reference, and not-marshaled.
12
Module 13: Remoting and XML Web Services
Marshal-By-Value Objects Marshal-by-value objects include value types and classes that are serializable. A copy of such marshal-by-value objects is passed from one application domain to another. You should use marshal-by-value objects when you need to move the complete state of the object with the execution to the target application domain for performance or processing purposes. In many scenarios, marshal-by-value objects reduce boundary crossing, such as network, process, and application domain roundtrips. Marshal-by-value objects have no distributed identity, and no proxy is ever created to reference them, as shown in the following example: [Serializable] class Foo1A { //. . . }
To participate in its own serialization when it is marshaled across application domain boundaries, an object can expose the ISerializable interface, as in the following example: using System.Runtime.Serialization; //... [Serializable] class Foo1B : ISerializable { //. . . }
Marshal-By-Reference Objects References to marshal-by-reference objects are made when the object reference (ObjRef) is passed from one application to another. When the object reference arrives in the remote application, it is converted to a proxy back to the original object. The original object remains in the application domain in which it was created. A marshal-by-reference object’s class must derive from the System.MarshalByRefObject class. Use marshal-by-reference objects when an object’s state should remain in the application domain in which it was created and when only references to that object should be marshaled at the time that the object is remoted. For example, make a file object, whose internal representation contains a field that is an operating system handle, application domain-bound. In this case, the operating system handle would not be useful or appropriate in another application domain, process, or computer. All operations on a marshal-by-reference object are appropriately indirected so that the common language runtime can intercept and forward them. This indirection applies to fields, properties, and methods of marshal-by-reference objects. For this reason, the performance overhead of marshal-by-reference objects is greater than the performance overhead of marshal-by-value objects.
Module 13: Remoting and XML Web Services
13
The following example declares a class Foo2 that is marshal-by-reference: class Foo2 : MarshalByRefObject { //. . . }
Not-Marshaled Objects Not-marshaled objects are the default for all objects that do not derive from System.MarshalByRefObject and that do not have the [Serializable] custom attribute. The use of not-marshaled objects is appropriate when an object should not leave the application domain because the object was designed for local use only, as in the following example: class Foo3 { //. . . }
14
Module 13: Remoting and XML Web Services
Server Side Topic Objective
To explain how to register and activate a remote object from the server side.
Lead-in
Remote objects must be registered with the remoting framework before clients can access them.
!
Register the Channel
!
Register Remote Objects by Using: #
The RegisterWellKnownServiceType call
RemotingConfiguration.RegisterWellKnownServiceType( RemotingConfiguration.RegisterWellKnownServiceType( typeof(HelloServer), typeof(HelloServer), "SayHello", "SayHello", WellKnownObjectMode.SingleCall); WellKnownObjectMode.SingleCall); #
Or a configuration file
RemotingConfiguration.Configure("MyHello.exe.config"); RemotingConfiguration.Configure("MyHello.exe.config");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** All remote objects must be registered with the remoting framework before clients can access them. Object registration is usually performed by a hosting application that starts up, registers one or more channels with ChannelServices, registers one or more remote objects with RemotingServices, and then waits until it is terminated. Note The registered channels and objects are available only while the process that registered them is alive. When the process terminates, all channels and objects that are registered by this process are automatically removed from the remoting services where they were registered. The following information is required when you register a remote object with the remoting framework: !
The type name of the remote object
!
The object Uniform Resource Identifier that clients will use to locate the object
!
For server-side activation, the object mode that is required • The object mode can be single call or singleton.
Module 13: Remoting and XML Web Services
15
Methods of Remote Object Registration You can register a remote object by calling RemotingConfiguration.RegisterWellKnownServiceType, and passing the information that is described in the preceding list as parameters, or by storing that information in a configuration file and then calling RemotingConfiguration.Configure and passing the name of the configuration file as a parameter. RegisterWellKnownServiceType and Configure perform the same function, but Configure is more convenient to use because the contents of the configuration file can be altered without recompiling the host application.
RegisterWellKnownServiceType The following example code shows how to register the HelloServer class as a SingleCall remote object by using the RegisterWellKnownServiceType method. RemotingConfiguration.RegisterWellKnownServiceType( typeof(HelloServer), "SayHello", WellKnownObjectMode.SingleCall);
In the preceding example, RemotingSamples.HelloServer is the name of the class, and SayHello is the object Uniform Resource Identifier.
Configuration File The following example code shows how to register the HelloServer class as a SingleCall remote object by using the Configure method: RemotingConfiguration.Configure("MyHello.exe.config");
In the preceding example, the configuration file, MyHello.exe.config stores the same registration information that is stored in the parameters of the preceding RegisterWellKnownServiceType method. The format of the .NET Remoting configuration file is described in Remoting Configuration Files in this module. When the remote object in the preceding example is registered, the remoting framework creates an object reference and then extracts the required metadata about the object from the assembly. The object’s required metadata, the Uniform Resource Identifier, and the assembly name are then stored in the object reference, which is filed in a remoting framework table that is used for tracking registered remote objects. Note The remote object itself is not instantiated by the registration process. Instantiation occurs only when a client attempts to call a method on the object or activates the object from the client side.
16
Module 13: Remoting and XML Web Services
Client Side Topic Objective
To explain how to register and activate a remote object from the client side.
Lead-in
On the client side, a client registers the channel and activates the remote object.
!
Register the Channel
ChannelServices.RegisterChannel(new ChannelServices.RegisterChannel(new TcpChannel()); TcpChannel()); !
Activate Remote Object by Using: #
Activator.GetObject
HelloServer HelloServer obj obj == (HelloServer)Activator.GetObject( (HelloServer)Activator.GetObject( typeof(RemotingSamples.HelloServer), typeof(RemotingSamples.HelloServer), "tcp://localhost:8085/SayHello"); "tcp://localhost:8085/SayHello"); #
new and a configuration file
RemotingConfiguration.Configure("MyHello.exe.config"); RemotingConfiguration.Configure("MyHello.exe.config"); HelloServer HelloServer obj obj == new new HelloServer(); HelloServer();
*****************************ILLEGAL FOR NON-TRAINER USE****************************** On the client side, a client registers the channel and activates the remote object.
Registering the Channel A client must first register the channel by calling ChannelServices.RegisterChannel. As previously mentioned in Server Side in this module, the server must first have registered the selected channel. The following code example shows how to register a TCP Channel: ChannelServices.RegisterChannel(new TcpChannel());
Important You do not specify a port number when you register the client channel.
Activating Remote Objects After the client registers the channel, it can then activate the remote object by using GetObject or the new operator. It is important to note that the object is not instantiated when either of these calls is made. Actually, no network calls are generated at all. The remoting framework obtains enough information from the metadata to create the proxy without connecting to the remote object. A network connection is only established when the client calls a method on the proxy. When the call arrives at the server, the remoting framework extracts the Uniform Resource Identifier from the message, examines the remoting framework tables to locate the reference for the object that matches the Uniform Resource Identifier, and then instantiates the object if necessary, forwarding the method call to the object. If the object is registered as SingleCall, it is destroyed after the method call is completed. For each method that is called, a new instance of the object is created.
Module 13: Remoting and XML Web Services
17
The only difference between GetObject and new is that GetObject allows you to specify a Uniform Resource Identifier as a parameter, while new obtains the Uniform Resource Identifier from the configuration file. You can use CreateInstance or new for client-activated objects. Both CreateInstance and new allow you to instantiate an object by using constructors with parameters. The lifetime of client-activated objects is controlled by the leasing service that is provided by the remoting framework.
Using Activator.GetObject The following example code shows how to obtain a server-activated object by using Activator.GetObject: HelloServer obj = (HelloServer)Activator.GetObject( typeof(RemotingSamples.HelloServer), "tcp://localhost:8085/SayHello");
In the preceding example, "tcp://localhost:8085/SayHello" specifies that a connection should be made to the remote object at the SayHello endpoint by using TCP on port 8085.
Using the new Operator To use the new operator, you must load a configuration file with the remote object’s information. For example, for a file called MyHello.exe.config, you load the configuration file as follows: RemotingConfiguration.Configure("MyHello.exe.config");
After the configuration file has been loaded, the client can activate the object as follows: HelloServer obj = new HelloServer();
18
Module 13: Remoting and XML Web Services
Client Compilation Techniques Topic Objective
To explain how type information is provided to the compiler.
!
When the Client Is Compiled, the Compiler Needs Server Class Data
!
Class Information Can Be Provided by:
Lead-in
When compiling the client code, the compiler requires type information about the HelloServer class.
#
A reference to the assembly where the class is stored
#
Splitting the remote object into an implementation class and an interface type
#
Using Wsdl.exe to extract the required metadata directly from the endpoint
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When compiling the client code, the compiler requires type information about the HelloServer class. This type information is discussed in the preceding topic. You can provide type information in one of the following ways: !
Provide a reference to the assembly in which the HelloServer class is stored. Providing a reference is useful when the client and server components are developed at the same site.
!
Split the remote object into an implementation class and an interface type, and use the interface as a reference when compiling the client. Splitting the remote object into an implementation class and an interface type is useful when the client and server components are not developed at the same site. The interface or interfaces can be compiled to a DLL and shipped to the client sites when necessary. According to COM guidelines, you should avoid changing the published interface.
!
Use the Web Services Description Language tool (Wsdl.exe) to extract the required metadata directly from the endpoint. The Web Services Description Language tool lets you connect to the endpoint that is provided, extract the metadata, and generate source code that can then be used to compile the client. The Web Services Description Language tool is useful when client and server components are developed at different sites and when no interface classes are available. To use the Web Services Description Language tool, point the tool at a remote Uniform Resource Identifier and generate the required metadata.
Note The Web Service Utility extracts only metadata and does not generate the source for the remote object.
Module 13: Remoting and XML Web Services
19
Remoting Configuration Files Topic Objective
To explain how to configure objects with configuration files for use in remoting.
Lead-in
Programmatic registration is a simple process, but it is not practical for use in reallife situations where large numbers of remote objects must be managed on a corporate network.
!
Configure Method on RemotingConfiguration
RemotingConfiguration.Configure(foo.exe.config); RemotingConfiguration.Configure(foo.exe.config); !
An Application Configuration File is an XML Document #
Configuration files are case-sensitive
#
Can be specified on a machine or on an application level Application level configuration takes priority over machine level configuration
#
For more information see “Remoting Configuration File Format” in the .NET Framework SDK documentation
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Programmatic registration is a simple process, but it is not practical for use in real-life situations where large numbers of remote objects must be managed on a corporate network. Remoting configuration solves this problem by using a configuration file, a simple mechanism, to register an object. To configure an object with a configuration file, call the Configure method on RemotingConfiguration, as shown in the following example: using System; using System.IO; using System.Runtime.Remoting; public class MyHost { public static void Main(String[] args) { if (args.Length == 0) { // Perform a default configuration, throw an exception // or display usage information to the user } else { RemotingConfiguration.Configure (args[0]); } // The program should pause here till the // registered objects are no longer required } }
20
Module 13: Remoting and XML Web Services
The Configure method loads the configuration file into memory, parses the contents, and calls the relevant methods to register the channels and objects that are described in the file. You can also use configuration files to store such settings as binding policy and security. The name of the configuration file includes the full module name and the extension, with .config appended to that extension. For example, the configuration file name for Foo.exe is Foo.exe.config. Although .NET Remoting does not mandate how you name a configuration file, you should use the naming convention that is described in the preceding paragraph to ensure that specific security and binding policies are picked up when an application is executed. The Configure call on RemotingConfiguration only reads the relevant sections in the configuration file that apply to remoting, while the rest of the information is ignored.
Module 13: Remoting and XML Web Services
21
An application configuration file is an XML document that contains sections for various feature areas. The general format is a follows: Note Configuration files are case-sensitive. <system.runtime.remoting> <SOAPinterop>
All configuration information can be specified on a machine or on an application level. Application level configuration takes priority over machine level configuration. For more information about the .NET Remoting configuration file format, see “Remoting Configuration File Format” in the .NET Framework SDK documentation.
22
Module 13: Remoting and XML Web Services
Demonstration: Remoting Topic Objective
To demonstrate how a client application uses .NET Remoting to make a method call on an object in a server application.
Lead-in
This demonstration shows how a client application uses .NET Remoting to make a method call on an object in a server application.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This demonstration shows how a client application can use .NET Remoting to make a method call on an object in a server application. Delivery Tip
Although this demonstration includes detailed instructions, this is not a guided practice. Present the demonstration and suggest that the students try the demonstration for themselves later.
Important Before running the application, ensure that all currently running server applications that may be using the same port number have been closed. Running more than one server that uses the same port number will generate an error. Tip You can display your machine’s current TCP/IP network connections by running the program netstat in a command prompt window. The following example shows that port 7 is in use by a process with PID 1504: C:\>netstat -o -n -a Active Connections Proto TCP
Local Address 0.0.0.0:7
Foreign Address 0.0.0.0:0
State PID LISTENING 1504 …
Module 13: Remoting and XML Web Services
23
Server-Side The following sample code is the server source code, which is named Server.cs: using using using using using
System; System.Runtime.Remoting; System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels.Tcp; System.Runtime.Remoting.Channels.Http;
namespace RemotingSamples { public class HelloServer : MarshalByRefObject { public int callCounter = 0; public static int Main(string [] args) { TcpChannel chan1 = new TcpChannel(8085); HttpChannel chan2 = new HttpChannel(8086); ChannelServices.RegisterChannel(chan1); ChannelServices.RegisterChannel(chan2); /* RemotingConfiguration.RegisterWellKnownServiceType ( typeof(HelloServer), "SayHello", WellKnownObjectMode.Singleton ); */ RemotingConfiguration.RegisterWellKnownServiceType( typeof(HelloServer), "SayHello", WellKnownObjectMode.SingleCall );
System.Console.WriteLine("Press Enter key to exit"); System.Console.ReadLine(); return 0; }
(Code continued on the following page.)
24
Module 13: Remoting and XML Web Services public HelloServer() { Console.WriteLine("HelloServer activated"); } public String HelloMethod(String name, out int counter) { counter = ++callCounter; Console.WriteLine( "Server Hello.HelloMethod : {0} Counter :{1}", name, callCounter); return "Hi there " + name; } } }
The RemotingSamples.HelloServer class is derived from MarshalByRefObject, so HelloServer is remotable. When the server is started, you create and register a TCP channel that listens for clients to connect on port 8085 and an HTTP channel that listens for clients to connect on port 8086. You also register the remote object with the remoting framework by calling RegisterWellKnownServiceType. The parameters for this call include the following: !
The full type of the object that is being registered, such as RemotingSamples.HelloServer in the preceding example
!
The name of the endpoint where the object will be published To connect to the object, clients must know the name of the endpoint. Any string can be used. In the preceding example, you use SayHello. You can also connect to remote objects through ASP.NET. In the preceding example, if you were connecting to remote objects through ASP.NET, the endpoint would be RemotingSamples/HelloServer.soap.
!
The object mode, which can be SingleCall or Singleton In the preceding example, you initially specify SingleCall. The object mode specifies the lifetime of the object when it is activated on the server. In the case of SingleCall objects, a new instance of the class is created for each call that is made from a client, even if the same client calls the same method more than once. Singleton objects, on the other hand, are created only once, and all clients communicate with the same object.
Module 13: Remoting and XML Web Services
25
Client-Side The following sample code is the client source code, which is named Client.cs: using using using using using using
System; System.Runtime.Remoting; System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels.Tcp; System.Runtime.Remoting.Channels.Http; System.IO;
namespace RemotingSamples { public class Client { public static int Main(string [] args) { int counter; TcpChannel chan1 = new TcpChannel(); ChannelServices.RegisterChannel(chan1); HelloServer obj1 = (HelloServer)Activator.GetObject( typeof(RemotingSamples.HelloServer), "tcp://localhost:8085/SayHello"); if (obj1 == null) { System.Console.WriteLine( "Could not locate TCP server"); return 1; }
HttpChannel chan2 = new HttpChannel(); ChannelServices.RegisterChannel(chan2); HelloServer obj2 =(HelloServer)Activator.GetObject( typeof(RemotingSamples.HelloServer), "http://localhost:8086/SayHello"); if (obj2 == null) { System.Console.WriteLine( "Could not locate HTTP server"); return 1; }
(Code continued on the following page.)
26
Module 13: Remoting and XML Web Services try { Console.WriteLine( "Client1 TCP HelloMethod {0} Counter {1}", obj1.HelloMethod("Caveman", out counter), counter); Console.WriteLine( "Client2 HTTP HelloMethod {0} Counter {1}", obj2.HelloMethod("Caveman", out counter), counter); } catch (IOException ioExcep) { Console.WriteLine("Remote IO Error" + "\nException:\n" + ioExcep.ToString()); return 1; } return 0; } } }
When the client starts up, it registers a TCP channel and an HTTP channel and proceeds to activate an object on each channel by calling the GetObject method on the Activator class. The parameters for this call are the type of the name of the class that you need to activate, RemotingSamples.HelloServer, and the endpoint Uniform Resource Identifier. For the client’s TCP connection, the Uniform Resource Identifier is tcp://localhost:8085/SayHello. For the client’s HTTP connection, the Uniform Resource Identifier is http://localhost:8086/SayHello. Important The Uniform Resource Identifier includes the protocol, computer name, and port number, as well as the endpoint. If the server is deployed on a host that is named Sunshine, clients can connect to the server that is using TCP by specifying tcp://sunshine:8085/SayHello. When you run the client, it locates and connects to the server, retrieves a proxy for the remote objects, and calls the HelloMethod on the remote objects, passing the string Caveman as a parameter and the counter as an out parameter. The server returns Hi there Caveman and the count of the number of times that the server object’s method has been called.
Module 13: Remoting and XML Web Services
27
Building and Executing the Server and Client To build the server and the client in the same directory, type the following command at the Microsoft Visual Studio .NET Command Prompt command prompt: Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window: on the Start menu point to All Programs, point to Microsoft Visual Studio .NET, Visual Studio .NET Tools, and then click Visual Studio .NET Command Prompt. csc server.cs csc /r:server.exe client.cs
To execute the application, start the server application from a console window, and then start the client application from another console window. Note The counter values on the two client calls should have the same value because the server object’s activation mode is SingleCall.
Changing the Activation Mode To change the server’s activation mode from SingleCall to Singleton, change the RegisterWellKnownServiceType call in the server as follows: RemotingConfiguration.RegisterWellKnownServiceType( typeof(HelloServer), "SayHello", WellKnownObjectMode.Singleton);
Rebuild and execute as in the preceding example. Note the counter values on both calls. They should increase after each call.
28
Module 13: Remoting and XML Web Services
Lab 13.1: Building an Order-Processing Application by Using Remoted Servers Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create an XML Web service hosted in a .NET executable file, create a TCP server, and create a client that uses .NET Remoting to access an XML Web service and a TCP server.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: !
Create an XML Web service hosted in a .NET executable file.
!
Create a TCP server.
!
Create a client that uses .NET Remoting to access both an XML Web service and a TCP server.
Lab Setup Only solution files are associated with this lab. The solution files for this lab are in the folder \Labs\Lab13.1\Solution.
Module 13: Remoting and XML Web Services
29
Scenario This lab is based on a scenario of a simple distributed order-processing application, in which a customer specifies a customer ID and an item number for an item that the customer wants to purchase, and the application processes the order. The order processing involves authenticating the customer’s ID and arranging order fulfillment that is to say, having the ordered item sent to the customer. In this lab, you will create a distributed solution that uses .NET Remoting. You will use .NET Remoting support of SOAP over HTTP to create a server application that exports an authenticate method as an open standards-based XML Web service. You will also use .NET Remoting support of TCP with binary formatting to create a remote order fulfillment server application that trades off the advantages of the open standards-based flexible XML Web services protocol for improved performance. In addition, you will create a simple test client to exercise these servers. Because the focus of this lab is on .NET Remoting, the functionality that is specific to authentication and fulfillment will be minimal.
Estimated time to complete this lab: 50 minutes
30
Module 13: Remoting and XML Web Services
Exercise 1 Creating an Authentication XML Web Service In this exercise, you will create an XML Web service that provides simple authentication. Simple authentication will consist of using a random-number generator to authenticate the user successfully 50 percent of the time.
! Create the authentication server 1. Using the editor of your choice, create a C# source file named Authenticate.cs, and save it in the Lab13.1 directory. 2. To simplify coding, add using statements to import the namespaces System, System.Runtime.Remoting, System.Runtime.Remoting.Channels, and System.Runtime.Remoting.Channels.Http. 3. Specify the namespace Lab13, and in it, create a public class named AServer that is marshal-by-reference. 4. Add a field named aRandom to hold a reference to a random number generator object of class System.Random. 5. In the Main method of AServer: a. Create and register a channel that supports the protocol and format for an XML Web service. Specify port 8086. b. Invoke the RemotingConfiguration.RegisterWellKnownServiceType method to register the authentication server whose full type name is Lab13.AServer, and whose endpoint is DoAuthentication. To retain the object’s state, that is to say, the random-number generator object, over multiple client calls, the well-known object’s mode should be stateful. c. Print to the console a message that tells the user to press any key so the server exits. The program should wait for this user action before continuing. 6. Add a public default constructor that creates a new instance of the Random class, assigns it to the field that was created in step 4, and prints a message to the console that states that AServer is running.
Module 13: Remoting and XML Web Services
31
7. Add a public method named Authenticate that takes a customer ID of type int and returns a bool value. a. Assign to a variable named aRandomNumber of type double a random number that is greater than or equal to 0 and is less than 1, by using the following code: double aRandomNumber =
aRandom.NextDouble();
b. Using the value from step a, assign to a variable of type bool named passed the value true for 50 percent of the time and the value false for 50 percent of the time. c. Print to the console a message that identifies the customer and states whether the customer is authenticated on the basis of the value of passed. d. Return passed. 8. In a Visual Studio .NET Command Prompt window, build Authenticate.exe. Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window: on the Start menu point to All Programs, point to Microsoft Visual Studio .NET, Visual Studio .NET Tools, and then click Visual Studio .NET Command Prompt.
32
Module 13: Remoting and XML Web Services
Exercise 2 Creating a Fulfillment Server In this exercise, you will create a server that provides simple order fulfillment functionality by using TCP and a binary format to provide fast communication. Simple fulfillment will consist of using a random-number generator to successfully fulfill the order 50 percent of the time.
! Create the fulfillment server 1. Using the editor of your choice, create a C# source file that is named Fulfillment.cs, and save it in the Lab13.1 directory. 2. To simplify coding, add using statements to import the namespaces System, System.Runtime.Remoting, System.Runtime.Remoting.Channels, and System.Runtime.Remoting.Channels.Tcp. 3. Specify the namespace Lab13, and in it, create a public class named FServer that is marshal-by-reference. 4. Add a field named aRandom to hold a reference to a random-number generator object of class System.Random. 5. In the Main method of FServer: a. Create and register a channel that uses the TCP protocol and binary formatting. Specify port 8085. Note You cannot have two channels on one computer that are using the same port number; therefore you cannot use port 8086. b. Invoke the RemotingConfiguration.RegisterWellKnownServiceType method to register the fulfillment server whose full type name is Lab13.FServer, and whose endpoint is DoFulfillment. To retain the object’s state, that is to say, the random-number generator object, over multiple client calls, the well-known object’s mode should be stateful. c. Print to the console a message that tells the user to press any key so the server exits. The program should wait for this user action before continuing. 6. Add a public default constructor that creates a new instance of the Random class, assigns it to the field that was created in step 4, and prints a message to the console that states that FServer is running.
Module 13: Remoting and XML Web Services
33
7. Add a public method named Fulfill that takes a customer ID of type int, an item number of type int, and returns a bool value. a. Assign to a variable named aRandomNumber of type double a random number that is greater than or equal to 0 and less than 1, by using the following code: double aRandomNumber =
aRandom.NextDouble();
b. Using the value from step a, assign to a variable of type bool named shipped the value true for 50 percent of the time and the value false for 50 percent of the time. c. Print to the console a message that identifies the customer and the item number and states whether the item was shipped on the basis of the value of shipped. d. Return shipped. 8. In a Visual Studio .NET Command Prompt window, build Fulfillment.exe.
34
Module 13: Remoting and XML Web Services
Exercise 3 Creating a Test Client In this exercise, you will create a test client application to test the orderprocessing servers. The client calls the authentication XML Web service. If the user is authenticated, the client calls the order fulfillment server.
! Create the test client 1. Using the editor of your choice , create a C# source file named Testclient.cs, and save it in the Lab13.1 directory. 2. To simplify coding, add using statements to import the namespaces System, System.Runtime.Remoting, System.Runtime.Remoting.Channels, System.Runtime.Remoting.Channels.Http, System.Runtime.Remoting.Channels.Tcp, and System.IO. 3. Specify the namespace Lab13, and in it, create a class named TestClient. In subsequent steps of this exercise, you will code the Main method. 4. In the Main method of TestClient: Create and register an HTTP channel and a TCP channel. 5. Invoke the Activator.GetObject method to create an AServer instance of type Lab13.AServer named aServer at the following URL: http://localhost:8086/DoAuthentication
6. Invoke the Activator.GetObject method to create an FServer instance of type Lab13.FServer named fServer at the URL: tcp://localhost:8085/DoFulfillment
7. In the try section of a try/catch block: a. Invoke the Authenticate method of the aServer object, and use 1234 as the customer’s ID. Print a message with the result to the console. b. If authentication passes, call the fServer object’s Fulfill method. Use 1234 as the customer’s ID and 5678 as the item number. Print a message with the result to the console. 8. In the catch section of the try/catch block: a. Catch any exceptions of type IOException, and print out their content. b. Return a value of 1. 9. In a Visual Studio .NET Command Prompt window, build Testclient.exe. Important Do not forget to reference Authenticate.exe, and Fulfillment.exe in the command line.
Module 13: Remoting and XML Web Services
35
10. Test the order system. a. Ensure that no server application is running and listening on ports 8085 and 8086. Tip You can display your machine’s current TCP/IP network connections by running the program netstat in a command prompt window. The following example shows that port 7 is in use by a process with PID 1504: C:\>netstat -o -n -a Active Connections Proto TCP
Local Address 0.0.0.0:7
Foreign Address 0.0.0.0:0
State PID LISTENING 1504 …
b. Open two separate console windows. In one window, run Fulfillment.exe, and in the other window, run Authenticate.exe. c. Open a third console window and repeatedly run Testclient.exe. The console output will vary because the random-number generator values cause authentication and fulfillment to succeed or fail 50 percent of the time. A typical series of test client invocations would produce output similar to the following: >testclient Authentication Server Customer 1234 Authorization: False >testclient Authentication Server Customer 1234 Authorization: False >testclient Authentication Server Customer 1234 Authorization: False >testclient Authentication Server Customer 1234 Authorization: True Fulfillment Server Customer 1234 Item Number 5678 Shipped: False >testclient Authentication Server Customer 1234 Authorization: True Fulfillment Server Customer 1234 Item Number 5678 Shipped: False >testclient Authentication Server Customer 1234 Authorization: True Fulfillment Server Customer 1234 Item Number 5678 Shipped: True
36
Module 13: Remoting and XML Web Services
" XML Web Services Topic Objective
To provide an overview of the topics covered in this section.
Lead-in
In this section, you will learn how to use Visual Studio .NET to implement an ASP.NET XML Web service and how to access the XML Web service from a Web browser and a client application.
!
ASP.NET XML Web Services Overview
!
ASP.NET Features
!
Consuming an XML Web Service
!
XML Web Service Discovery
*****************************ILLEGAL FOR NON-TRAINER USE****************************** XML Web services can be provided by and accessed from applications that reside in a variety of hosts. Hosts include, but are not limited to, ASP.NET, Microsoft Internet Explorer, executable files, Microsoft Windows® NT® Server, and Microsoft Windows 2000 Component Services, also known as COM+ Services. In the preceding sections, you have learned how to use .NET Remoting to create executable files that host XML Web services by using the default HTTP channel with SOAP formatting. In this section, you will learn how to use Visual Studio .NET to implement an ASP.NET XML Web service and how to access this XML Web service from both a Web browser and a client application.
Module 13: Remoting and XML Web Services
37
ASP.NET XML Web Services Overview Topic Objective
To provide an overview of ASP.NET XML Web services.
!
ASP.NET Files with .asmx Extensions Are Web Services
!
Example of HelloWorld .asmx File
using using System; System; using using System.Web.Services; System.Web.Services; public public class class HelloWorld HelloWorld :: WebService WebService {{ [WebMethod] [WebMethod] public public String String SayHelloWorld() SayHelloWorld() {{ return return "Hello "Hello World"; World"; }} }}
Lead-in
ASP.NET provides support for XML Web services with the .asmx file, which is a text file that is similar to an .aspx file.
!
If File Is Placed on Server Foo Inside a Virtual Directory Bar #
Access service by using:
http://Foo/Bar/HelloWorld.asmx http://Foo/Bar/HelloWorld.asmx #
Access WSDL of service by using:
http://Foo/Bar/HelloWorld.asmx?wsdl http://Foo/Bar/HelloWorld.asmx?wsdl
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This section introduces some of the basic features of ASP.NET that enable developers to host and use XML Web services. The Internet is quickly evolving from its present stage where Web sites deliver user interface (UI) pages to Web browsers to a next generation of programmable Web sites that directly link organizations, applications, services, and devices. These programmable Web sites are more than passively accessed sites; they are reusable, intelligent XML Web services.
Using .asmx Files ASP.NET provides support for XML Web services with the .asmx file. An .asmx file is a text file that is similar to an .aspx file. An .asmx file can be part of an ASP.NET application that includes .aspx files. Like .aspx files, .asmx files are automatically compiled by the ASP.NET runtime when a request to the service is made. Subsequent requests are serviced by a cached pre-compiled type object. Like .aspx files, .asmx files are then Uniform Resource Identifieraddressable. Delivery Tip
You can demonstrate the SayHelloWorld service by cutting and pasting the HelloWorld.asmx code into Notepad and saving it as a file named HelloWorld.asmx in the source directory for an existing IIS virtual directory. You then type the appropriate URL in the Internet Explorer Address bar. To make the Address bar visible, click the View menu, point to Toolbars, and then click Address Bar.
The following example shows the code that is contained in the HelloWorld.asmx file. It is a simple example of a ASP.NET-hosted XML Web service. using System; using System.Web.Services; public class HelloWorld : WebService { [WebMethod] public String SayHelloWorld() { return "Hello World"; } }
38
Module 13: Remoting and XML Web Services
The HelloWorld.asmx file starts with an ASP.NET directive, WebService, and sets the language to C#. You can also set the language to Microsoft Visual Basic®. The class HelloWorld contains the XML Web service and therefore is derived from the base class WebService in the System.Web.Services namespace. All of the methods that will be accessible as part of the service must have the custom attribute, [WebMethod], in front of their signatures. In the preceding example, the SayHelloWorld method is the service’s only method.
Using a Virtual Directory To create an ASP.NET application, you can use an existing virtual directory or create a new one. For example, if you installed Microsoft Windows 2000 Server and Internet Information Server (IIS) on a computer, that computer probably now has the directory C:\InetPub\WWWRoot. To configure IIS, start the Internet Information Services tool. Click Start, click Control Panel, Performance and Maintenance, Administrative Tools, and then click Internet Information Services. In the Internet Information Services tool, you can create a new virtual directory or promote an existing directory. !
To create a new virtual directory, right-click an existing directory, and then click New.
!
To promote an existing directory, right-click a virtual directory, click Properties and then set the Local Path.
If you place the HelloWorld.asmx file on a server called Foo inside a virtual directory called Bar, you can use a URL in Internet Explorer to test the application. For example, if you type http://Foo/Bar/HelloWorld.asmx in the Address bar, the resulting page shows the public methods for this XML Web service and the protocols, such as SOAP or HTTP GET, that you can use to invoke these methods. The public methods for the XML Web service are marked with the [WebMethod] attribute. The Web Services Description Language XML file for this service is produced when you type http://Foo/Bar/HelloWorld.asmx?WSDL in the Internet Explorer Address bar. This WSDL file is important and can be used by clients to access the service.
Module 13: Remoting and XML Web Services
39
ASP.NET Features !
Topic Objective
To explain how to create an XML Web service by exposing a .NET class that inherits from the WebService class, and to introduce advanced ASP.NET features.
ASP.NET Can Expose Web Services Defined in a .NET Class #
Class derives from WebService, method attribute [WebMethod]
namespace namespace MyNameSpace MyNameSpace {{ public public class class HelloWorld HelloWorld :: WebService WebService {{ [WebMethod] [WebMethod] public public String String SayHelloWorld() SayHelloWorld() {{ return "Hello World"; } return "Hello World"; } }} # }}
Lead-in
#
In ASP.NET, you can create an XML Web service by simply exposing a .NET class that inherits from the WebService class.
Class source file is compiled into a library DLL and placed in \Bin
csc csc /out:bin\helloworld.dll /out:bin\helloworld.dll /t:library /t:library helloworld.cs helloworld.cs #
The .asmx file contains a single line that names the class
!
Advanced ASP.NET Features #
Data sets, Global.asax, Session and Application objects, pattern matching
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In ASP.NET, you can create an XML Web service by simply exposing a .NET class that inherits from the WebService class. You compile the class’s source file into a library DLL and place this DLL in the ASP.NET application’s \Bin subdirectory. You then create in the ASP.NET application’s directory an .asmx file that contains only the following single line of code:
The HelloWorld.cs source code defines a HelloWorld service with an exported SayHelloWorld method in the MyNameSpace namespace, as shown in the following example: using System; using System.Web.Services; namespace MyNameSpace { public class HelloWorld : WebService { [WebMethod] public String SayHelloWorld() { return "Hello World"; } } }
To compile this HelloWorld.cs file in the ASP.NET application directory, you type the following command in a Visual Studio .NET Command Prompt window: >csc /out:bin\helloworld.dll /t:library helloworld.cs
40
Module 13: Remoting and XML Web Services
You then create the file named HelloWorld.asmx in the ASP.NET application directory. HelloWorld.asmx contains the following single line of code:
The only methods that are exposed from a service are those class methods that are flagged with a [WebMethod] custom attribute. Without this attribute, the method is not exposed from the service. Not exposing a method from a service is useful when you want to hide implementation details that are called by public XML Web service methods or when the WebService class is also used in local applications. Note A local application can use any public method, but only [WebMethod] methods are remotely accessible through SOAP.
Advanced ASP.NET Features ASP.NET is also useful for building complex XML Web services. Advanced ASP.NET features include: !
Data sets Data sets are a powerful new XML-based technique to represent disconnected data. Data sets can be returned from an XML Web service method. Because Data sets can store complex information and relationships in an intelligent structure, they enable you to take full advantage of XML Web services. When you expose Data sets through a service, you can limit the database connections to your data server.
!
Global.asax file The Global.asax file adds application-level logic and event-handling code to Web applications.
!
Session and Application objects You can use ASP.NET intrinsics, such as the Session and Application objects, to manage an ASP.NET application’s state.
!
Text Pattern Matching Text Pattern Matching is a technology that can be used to address any Uniform Resource Identifier that returns text as if it were an XML Web service.
Further discussion of these and other advanced ASP.NET features is beyond the scope of this course.
Module 13: Remoting and XML Web Services
41
Demonstration: Using Visual Studio .NET to Create an XML Web Service Topic Objective
To demonstrate how to use Visual Studio .NET to create an XML Web service.
Lead-in
In this demonstration, you will learn how to create a service called MathService that has a single method named SalesTax.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you have learned how to create an XML Web service by using ASP.NET. Visual Studio .NET provides an even easier way to build ASP.NEThosted XML Web services. In this demonstration, you will see how to create a service called MathService that has a single method named SalesTax. The SalesTax method takes two parameters: The first parameter represents the sales amount; and the second parameter represents the fractional sales tax rate. For example, a sales tax rate of 8.5 percent is represented as .085. SalesTax returns a total amount that is equal to the sales amount plus the sales tax.
! To create a service called MathService that has a single method named SalesTax
Delivery Tip
Although this demonstration includes detailed instructions, it is not a guided practice. Present the demonstration and suggest that students try the demonstration for themselves later.
1. Start Visual Studio, and create a new project. a. In the New Project dialog box, select Visual C# Projects as the type. b. Select ASP.NET Web Service as the template. You may need to scroll down to see ASP.NET Web Service. c. In the Location box, type the name of the Web server, http://localhost/MathService. The grayed out Name box will now contain the text MathService. Then click OK. 2. On the View menu, click Code. 3. Because the XML Web service in this demonstration is simple, you can work on the code directly. For more complex XML Web services, you can use the Service1.asmx.cs Design palette that is displayed when you create a new project of type XML Web service. The Service1.asmx.cs Design palette enables the drag-and-drop operation of rapid application development that can make complex services easier to set up. 4. In the Service1 class, examine the HelloWorld method in the commentedout WEB SERVICE EXAMPLE.
42
Module 13: Remoting and XML Web Services
5. In Service1.asmx.cs, implement the SalesTax method by inserting the following code after the comments for the HelloWorld method. You should leave the HelloWorld code commented out. [WebMethod] public float SalesTax( float salesAmount, float fractionalTaxRate) { return salesAmount + (salesAmount * fractionalTaxRate); }
6. On the Build menu, click Build Solution to create the XML Web service. 7. Use Internet Explorer to access the XML Web service at the following URL: http://localhost/MathService/Service1.asmx
Tip To use Internet Explorer to view an XML Web service from within the Visual Studio environment, right-click the .asmx file in the Solution Explorer window, and then click View in Browser. 8. In the Internet Explorer page that is returned in step 7, click the link for the Service1 operation labeled SalesTax. 9. In the Internet Explorer page that is returned in step 8, enter some parameters, and then click Invoke to verify that the correct value is returned and displayed in SOAP/XML format. 10. Use Internet Explorer to access and verify that the XML Web service’s description is obtained from the following URL: http://localhost/MathService/Service1.asmx?wsdl
Module 13: Remoting and XML Web Services
43
Consuming an XML Web Service Topic Objective
To explain how XML Web services are consumed and how to use the Web Services Description Language tool.
Lead-in
In addition to providing technology that enables you to create XML Web services, the .NET Framework provides a sophisticated set of tools and code to consume XML Web services.
!
To Consume a Web Service, Use Wsdl.exe #
Identify the object or service URI to be used
http://localhost/MathService/Service1.asmx http://localhost/MathService/Service1.asmx #
Run Wsdl.exe on the service’s URI
wsdl wsdl http://localhost/MathService/Service1.asmx?wsdl http://localhost/MathService/Service1.asmx?wsdl #
Write the client code that calls the remote object
Service1 Service1 salesTaxService salesTaxService == new new Service1(); Service1(); float float totalAmount totalAmount == salesTaxService.SalesTax( salesTaxService.SalesTax( amount,taxRate); amount,taxRate); #
Build the client and proxy code
csc csc salestaxclient.cs salestaxclient.cs service1.cs service1.cs
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In addition to providing technology that enables you to create XML Web services, the .NET Framework provides a sophisticated set of tools and code to consume XML Web services. Consuming an XML Web service means accessing it as a client. Because XML Web services are based on open protocols, such as SOAP and HTTP, this client technology can also be used to consume XML Web services that are not built by using the .NET Framework.
Using the Web Services Description Language Tool The .NET Framework SDK provides a tool that is called the Web Services Description Language tool (Wsdl.exe). You can use this tool to download the WSDL description of an XML Web service and create a proxy class that addresses this service. For XML Web services that are created by using ASP.NET, the proxy class is similar to the class that is defined in the .asmx file. However, the proxy class contains only methods with the [WebMethod] custom attribute. An application that uses an XML Web service creates an instance of the service’s proxy class and invokes the service’s methods as it would with any local object. You compile and build your code with this proxy class included. Alternatively, you can use features of the Visual Studio environment to build applications that consume XML Web services. These Visual Studio features are beyond the scope of this course.
44
Module 13: Remoting and XML Web Services
The following process shows how to create a simple application that uses the XML Web service that is created in the Using Visual Studio to Create an XML Web service demonstration in this module. Delivery Tip
The use of the Web Services Description Language tool (Wsdl.exe) is covered in detail because the students will need this information to do Lab 13.2, Using an XML Web service. If you feel that students may benefit from a full demonstration, you can demonstrate the steps here.
! To use the Service1 Web Service 1. Identify the object and the service that should be used. The Salestaxclient application will use the SalesTax method of the Service1 service whose URL is: http://localhost/MathService/Service1.asmx
Note Typically a client uses an XML Web service that is located on a remote computer. If Service1 were located in a MathService virtual directory on a Web server named foo, the URL for the service would be: http://foo/MathService/Service1.asmx
2. Run the WSDL tool, Wsdl.exe, and point it at the URL where the server object is located. You can request that the Web Service Utility retrieves the schema and, from it, generates a source file that contains a proxy for the service’s class. To generate a proxy for the Service1 class in a file named Service1.cs, type the following command in a Visual Studio .NET Command Prompt window: wsdl http://localhost/MathService/service1.asmx?wsdl
Module 13: Remoting and XML Web Services
45
The proxy, Service1.cs, contains code similar to the following: //... using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel; using System.Web.Services; //... [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Web.Services.WebServiceBindingAttribute! (Name="Service1Soap", Namespace="http://tempuri.org/")] public class Service1 : System.Web.Services.Protocols.SoapHttpClientProtocol { //... public Service1() { this.Url = "http://localhost/mathservice/service1.asmx"; } //... [System.Web.Services.Protocols.SoapDocumentMethodAttribute ! ("http://tempuri.org/SalesTax", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle= System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public System.Single SalesTax(System.Single salesAmount, System.Single fractionalTaxRate) { object[] results = this.Invoke("SalesTax", new object[] {salesAmount, fractionalTaxRate}); return ((System.Single)(results[0])); } //...
46
Module 13: Remoting and XML Web Services
3. Write the client code that calls the remote object. Create a text file named Salestaxclient.cs in the folder \DemoCode\Mod13\ Demo13.2 containing the following code: using System; public class Class1 { public Class1() {} public static int Main(string[] args) { Console.WriteLine("Enter Sales Amount:"); float amount = Single.Parse(Console.ReadLine()); Console.WriteLine( "Enter Fractional Sales Tax Rate:"); float taxRate = Single.Parse(Console.ReadLine()); Service1 salesTaxService = new Service1(); float totalAmount = salesTaxService.SalesTax(amount,taxRate); Console.WriteLine( "Total: {0}",totalAmount.ToString()); return 0; } }
4. Build the client executable file by using the client code and proxy code. You must include references to the assemblies that are used by the client and proxy code. 5. To build Salestaxclient.exe, type the following command in a Visual Studio .NET Command Prompt window: csc salestaxclient.cs service1.cs
6. Run the client, and enter parameters as prompted. Running Salestaxclient.exe produces output that should be similar to the following: salestaxclient Enter Sales Amount: 100 Enter Fractional Sales Tax Rate: .08 Total: 108
Module 13: Remoting and XML Web Services
47
XML Web Service Discovery Topic Objective
To introduce the Web Service discovery process and the tools that are available for discovery.
Lead-in
Web Service discovery is the process of dynamically locating and interrogating Web Service descriptions, which is a preliminary step for accessing an XML Web service.
!
Discovery – The Process of Locating and Interrogating XML Web Service Descriptions
!
Discovery Services Are Evolving and Changing Rapidly
!
Disco – Microsoft XML Web Services Discovery Tool #
#
!
Disco file is an XML document that links to descriptions of XML Web Services Disco file is currently created and used by Visual Studio
Universal Description, Discovery, and Integration Project #
Open framework for describing services, discovering businesses, and integrating business services that use the Internet
#
Cross-industry support and platform independence
#
For more information, see http://www.uddi.org
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Web Service discovery is the process of dynamically locating and interrogating Web Service descriptions, which is a preliminary step for accessing an XML Web service. The discovery process allows an XML Web service client that may not know the Web Service’s Uniform Resource Identifier to locate an XML Web service, to determine the capabilities of that Web Service, and to properly interact with it. Web Service discovery is currently evolving, and the information that is presented in this topic is subject to change.
Disco – Microsoft Web Services Discovery Tool In the current version of the .NET Framework, you can enable programmatic discovery when an XML Web service publishes a file with a .disco extension. Disco is the Microsoft XML Web services discovery tool. A .disco file is an XML document that contains links to other resources that describe the XML Web service. Currently, when you create an ASP.NET-hosted XML Web service with Visual Studio .NET, .disco files are automatically generated and used. For more information about Disco, see the Visual Studio .NET and .NET Framework SDK documentation.
Universal Description, Discovery, and Integration The Universal Description, Discovery and Integration (UDDI) project is a cross-industry project that is dedicated to creating a platform-independent, open framework for describing Web Services, discovering businesses, and integrating business services that use the Internet. The project is driven by all of the major platform and software providers, marketplace operators, and e-business leaders. For more information about UDDI, see the UDDI Web site at http://www.uddi.org/.
48
Module 13: Remoting and XML Web Services
Lab 13.2: Using an XML Web Service Topic Objective
To introduce the lab.
Lead-in
In this lab, you will access an XML Web service by creating a proxy using the Web Services Description Language tool.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to: • Access an XML Web service by creating a proxy using the Web Services Description Language tool.
Lab Setup Starter and solution files are associated with this lab. The starter files are in the folder \Labs\Lab13.2\Starter. The solution files for this lab are in the folder \Labs\Lab13.2\Solution.
Module 13: Remoting and XML Web Services
49
Scenario This lab is similar to the scenario used in Lab 13.1, Building an OrderProcessing Application by Using Remoted Servers. That scenario was based on a simple distributed order-processing application, in which a customer specifies a customer ID and an item number for an item that the customer wants to purchase, and the application processes the order. The order processing involves authenticating the customer’s ID and arranging order fulfillment that is to say, having the ordered item sent to the customer. This lab extends Lab 13.1 by having the client application calculate the cost of the ordered item. Your client will use an XML Web service to calculate the item’s cost including sales tax. This scenario simulates the case where you do not have access to the XML Web service’s source code or assembly. You will build a MathService XML Web service to calculate the total cost of an item given the pretax cost and fractional tax rate. You will then use the Web Services Description Language tool (Wsdl.exe) to access and create a .NET proxy class for this XML Web service. Then you will build a client called Testclient2.exe that uses this proxy to access the XML Web service’s Service1.SalesTax method to calculate the cost of the ordered item.
Estimated time to complete this lab: 55 minutes
50
Module 13: Remoting and XML Web Services
Exercise 1 Creating the XML Web Service Proxy In this exercise, you will create the MathService XML Web service and use the Web Services Description Language tool (Wsdl.exe) to create a proxy class to this XML Web service.
! Create the MathService XML Web service using Visual Studio .NET 1. Start Visual Studio, and create a new project. a. In the New Project dialog box, select Visual C# Projects as the type. b. Select ASP.NET Web Service as the template. You may need to scroll down to see ASP.NET Web Service. c. In the Location box, type the name of the Web server, http://localhost/MathService. The grayed out Name box will now contain the text MathService. Then click OK. 2. On the View menu, click Code. Because the XML Web service in this demonstration is simple, you can work on the code directly. For more complex XML Web services, you can use the Service1.asmx.cs Design palette that is displayed when you create a new project of type XML Web service. The Service1.asmx.cs Design palette enables the drag-and-drop operation of rapid application development that can make complex services easier to set up. 3. In the Service1 class, examine the HelloWorld method in the commentedout WEB SERVICE EXAMPLE. 4. In Service1.asmx.cs, implement the SalesTax method by inserting the following code after the comments for the HelloWorld method. You should leave the HelloWorld code commented out. [WebMethod] public float SalesTax( float salesAmount, float fractionalTaxRate) { return salesAmount + (salesAmount * fractionalTaxRate); }
5. On the Build menu, click Build Solution to create the XML Web service. 6. Use Internet Explorer to access the XML Web service at the following URL: http://localhost/MathService/Service1.asmx
Tip To use Internet Explorer to view an XML Web service from within the Visual Studio environment, right-click the .asmx file in the Solution Explorer window, and then click View in Browser.
Module 13: Remoting and XML Web Services
51
7. In the Internet Explorer page that is returned in step 6, click the link for the Service1 operation labeled SalesTax. 8. In the Internet Explorer page that is returned in step 7, type some parameters, and then click Invoke to verify that the correct value is returned and displayed in SOAP/XML format. 9. Use Internet Explorer to access and verify that the XML Web service’s description is obtained from the following URL: http://localhost/MathService/Service1.asmx?wsdl
! Create the XML Web service proxy Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window: on the Start menu, point to All Programs, point to Microsoft Visual Studio .NET, Visual Studio .NET Tools, and then click Visual Studio .NET Command Prompt. • In a Visual Studio .NET Command Prompt window, from the Lab 13.2 Starter directory, create a proxy to the authentication XML Web service named Service1.cs by running the following command: wsdl http://localhost/MathService/service1.asmx?wsdl
52
Module 13: Remoting and XML Web Services
Exercise 2 Creating the Test Client In this exercise, you will create a test client. The client calls the authentication server. If the user is authenticated, then the client calls the order fulfillment server. If the order is fulfilled, then the client calls the MathService XML Web service to calculate the total cost including sales tax. For simplicity, the client will assume that all items cost $100 and that the sales tax rate is always 0.1.
! Create the test client 1. Use the editor of your choice to examine the C# source file that is named Testclient2.cs located in the Lab13.2 Starter directory. 2. Add code to Testclient2.cs after the following command that outputs the results from calling the fulfillment service: Console.WriteLine( "Fulfillment Server Customer 1234 Item Number 5678 Shipped: {0}", shipped.ToString() );
The code should check the status of fulfillment. If shipped is true, then perform the following steps: a. Instantiate a new Service1 object named salesTaxService. b. Call the salesTaxService.SalesTax method, passing 100.0F as its first argument and 0.1F as its second argument. c. Print out to the console the returned total cost value. 3. Build Testclient2.exe in a Visual Studio .NET Command Prompt window as follows: a. Reference Fulfillment.exe, and Authenticate.exe. b. Specify the C# source files, Service1.cs and Testclient2.cs. c. Specify that the output file should be named Testclient2.exe.
Module 13: Remoting and XML Web Services
53
4. Test the order fulfillment system, by performing the following steps: a. In a console window, from the Lab 13.2 Starter directory, run Authenticate.exe. b. In a second console window, from the Lab 13.2 Starter directory, run Fulfillment.exe. c. In a third console window, repeatedly run Testclient2.exe. The console output will vary because the random-number generator values cause authentication and fulfillment to succeed or fail 50 percent of the time. A typical series of test client invocations would produce output similar to the following: >testclient2 Authentication Server Customer 1234 Authorization: True Fulfillment Server Customer 1234 Item Number 5678! Shipped: True Total Cost: 110 >testclient2 Authentication Server Customer 1234 Authorization: True Fulfillment Server Customer 1234 Item Number 5678! Shipped: False
54
Module 13: Remoting and XML Web Services
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
Remoting
!
Remoting Configuration Files
!
XML Web Services
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. Can two application domains that are on the same computer each have a channel that listens on the same port number? Ports are a machine-wide resource; therefore, on one computer, it is illegal to register multiple channels that listen on the same port number, even if the channels are registered in different application domains.
2. What is the purpose of a proxy? The proxy object acts as a representative of the remote object and ensures that all calls that are made on the proxy are forwarded to the correct remote object instance. All methods that are called on the proxy are automatically forwarded to the remote class, and any results are returned to the client.
3. Can a remotely instantiated object be returned by value? No. All objects that are instantiated remotely are returned by reference.
4. What determines whether a remotely instantiated object’s parameters and return values are passed by reference or by value? Objects whose classes are marked with the SerializableAttribute are marshal-by-value, and objects that inherit from System.MarshalByRefObject are marshal-by-reference.
Module 13: Remoting and XML Web Services
55
5. What file extension is typically used by ASP.NET-hosted XML Web services? ASP.NET provides support for XML Web services with the .asmx file.
6. In ASP.NET, how do you specify that a service is defined in a prebuilt assembly, and where should that assembly’s DLL be located in relation to the ASP.NET application? The .asmx file should contain the single line: The assembly library DLL should be in the application’s \Bin subdirectory.
7. How can a client invoke an XML Web service that is not implemented by using the .NET Framework or in which the XML Web service’s assembly or source code is not available? The Web Services Description Language tool (Wsdl.exe) can be used to read the WSDL description of an XML Web service and create a proxy class. The client can use the proxy class to invoke the methods of the XML Web service.
56
Module 13: Remoting and XML Web Services
Course Evaluation Topic Objective
To direct students to a Web site to complete a course evaluation.
Lead-in
Between now and the end of the course, you can go to the Web site listed on this page to complete a course evaluation.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Your evaluation of this course will help Microsoft understand the quality of your learning experience. To complete a course evaluation, go to http://www.metricsthatmatter.com/ survey/. Microsoft will keep your evaluation strictly confidential and will use your responses to improve your future learning experience.
THIS PAGE INTENTIONALLY LEFT BLANK
Contents
Module 14 (Optional): Threading and Asynchronous Programming
Overview
1
Introduction to Threading
2
Using Threads in .NET
9
Thread Safety
29
Special Thread Topics
51
Asynchronous Programming in .NET
74
Lab 14: Working With Multithreaded Applications
92
Review
108
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, places or events is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 14 (Optional): Threading and Asynchronous Programming
Instructor Notes Module 14 Presentation: 150 Minutes Lab: 60 Minutes
This module provides students with knowledge about the support that the Microsoft® .NET Framework provides for working with multithreaded applications and asynchronous programming. After completing this module, students will be able to: !
Create and manage threads.
!
Create thread-safe code.
!
Create and use timers.
!
Create threads using thread pools.
!
Create managed threads that interact well with COM components.
!
Create Microsoft Windows® Forms applications with background threads.
!
Make asynchronous calls using delegates.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft PowerPoint® file 2349B_14.ppt.
Preparation Tasks To prepare for this module: !
Read all of the materials for this module.
!
Practice the demonstrations.
!
Complete the lab.
iii
iv
Module 14 (Optional): Threading and Asynchronous Programming
Demonstrations This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for the student notes. The code for each of the following demonstrations is contained in project folders that are located in \Democode\Mod14. Use the debugger to step through the code while you point out features and ask students what they think will happen next.
Managing Threads In this demonstration, you will show students how to create and manage threads in the .NET Framework by using some of the classes and methods that were covered in Using Threads in .NET. The code for this demonstration is contained in one project and is located in \Democode\Mod14\Demo14.1.
Interrupt and Abort In this demonstration, you will show students how to terminate threads by using the Thread.Interrupt and Thread.Abort methods. The code for this demonstration is contained in one project and is located in \Democode\Mod14\Demo14.2.
Using Synchronization Contexts and Synchronized Code Regions to Provide Thread Safety In this demonstration, you will use a series of tests to show students how to achieve thread safety through the use of synchronization contexts and synchronized code regions. The code for this demonstration is contained in one project and is located in \Democode\Mod14\Demo14.3.
Using Synchronization Techniques and Thread Pooling In this demonstration, you will show students how to perform synchronization by using the C# lock keyword and manual synchronization primitives, and also how to obtain multiple threads by using a thread pool. The code for this demonstration is contained in one project and is located in \Democode\Mod14\Demo14.4.
Module 14 (Optional): Threading and Asynchronous Programming
Windows Forms Threading In this demonstration, you will show students how to use a background thread in a Windows Forms application. The code for this demonstration is contained in one project and is located in \Democode\Mod14\Demo14.5.
Asynchronous File Stream Read In this demonstration, you will show students how to use synchronous and asynchronous read methods on a file stream. For the asynchronous case, you will show four different ways to complete the operation: callback, poll, end method call, and wait with timeout. The code for this demonstration is contained in one project and is located in \Democode\Mod14\Demo14.6.
Using a Delegate In this demonstration, you will show students how to use a delegate object to make asynchronous calls. The code for this demonstration is contained in one project and is located in \Democode\Mod14\Demo14.7.
Multimedia Presentation This section lists the multimedia items that are part of this module. Instructions for launching and playing the multimedia are included with the relevant slides.
Asynchronous Programming This animation illustrates the .NET Framework common language runtime support for asynchronous programming using Delegate objects.
v
vi
Module 14 (Optional): Threading and Asynchronous Programming
Module Strategy Use the following strategy to present this module: !
Introduction to Threading Provide a general introduction to the concept of threads and discuss advantages and disadvantages of using them. As a simple illustration of the concept of threading, use Microsoft Internet Explorer to show how you can still do work while waiting for a download operation to complete. Provide a high level overview of the System.Threading namespace and introduce the asynchronous design pattern, which you will cover in more detail in Asynchronous Programming in .NET. Explain how application domains play a key role in .NET threading architecture.
!
Using Threads in .NET Focus on the classes in the System.Threading namespace that are used to start, manage, and terminate threads. In addition to the preceding operations, discuss the role of managed thread local storage.
!
Thread Safety Focus on the issues that students may encounter in multithreaded programming from sharing data and resources between threads, as a result of thread synchronization. Introduce strategies that the .NET Framework provides for dealing with synchronization, in particular classes and interfaces in System.Threading.
!
Special Thread Topics Introduce the Thread.Timer class, which provides a mechanism for executing methods at specified intervals. Explain how a TimerCallback delegate is used in conjunction with a Timer. Discuss the use of thread pools in making multiple threads operate more efficiently. Discuss how managed threads call into a COM object. Outline best practices for implementing thread-safe code.
Module 14 (Optional): Threading and Asynchronous Programming !
vii
Asynchronous Programming in .NET Provide a brief definition of the concept of asynchronous programming as the ability to issue method calls to other components, and to carry on with other work without waiting for the operation to complete. Introduce the asynchronous design pattern, and emphasize that one of its innovations is that the caller can decide whether a particular call should be asynchronous. Spend most of the time in this section on the Asynchronous File Stream Read Example. In addition to the code on the slides and information in the Student Notes, there is an accompanying demonstration. Discuss the use of Asynchronous delegates to call a synchronous method in an asynchronous manner. Play the Asynchronous Programming animation to illustrate the .NET Framework common language runtime support for asynchronous programming using Delegate objects.
Module 14 (Optional): Threading and Asynchronous Programming
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn about the support that the .NET Framework provides for working with multithreaded applications and asynchronous programming.
!
Introduction to Threading
!
Using Threads in .NET
!
Thread Safety
!
Special Thread Topics
!
Asynchronous Programming in .NET
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this module, you will learn about the support that the Microsoft® .NET Framework provides for working with multithreaded applications and asynchronous programming. The common language runtime abstracts much of the threading support into classes that greatly simplify most threading tasks. Even if you do not create your own threads explicitly, you need to understand how your code should handle multiple threads if it is run in a multithreaded environment. You will also learn how to handle thread synchronization to maintain application responsiveness and avoid potential data corruption and other problems. In the .NET Framework, asynchronous programming is a feature that is supported by Remoting, Networking: HTTP, TCP, File I/O, ASP.NET, and Microsoft Message Queue Server (MSMQ). Because asynchronous programming is a core concept, the .NET Framework provides a common design pattern for handling asynchronous execution. This module introduces the .NET Framework asynchronous Design Pattern and gives examples of its use. After completing this module, you will be able to: !
Create and manage threads.
!
Create thread-safe code.
!
Create and use timers.
!
Create threads using thread pools.
!
Create managed threads that interact well with COM components.
!
Create Microsoft Windows® Forms applications with background threads.
!
Make asynchronous calls using delegates.
2
Module 14 (Optional): Threading and Asynchronous Programming
" Introduction to Threading Topic Objective
To provide an overview of the topics that you will cover in this section.
Lead-in
In this section, you will learn about the advantages and disadvantages of using threads and the support for threads that is provided by the .NET Framework.
!
Overview of Threads
!
Overview of Support for Threading in .NET
!
Thread Architecture in .NET
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section, you will learn about the advantages and disadvantages of using threads and the support for threads that is provided by the .NET Framework.
Module 14 (Optional): Threading and Asynchronous Programming
3
Overview of Threads Topic Objective
To define threads in the context of general multitasking and state the primary advantages and disadvantages of using threads.
!
!
Threads
!
The basic unit to which an operating system allocates processor time
#
Enable multiple activities to appear to occur simultaneously
Advantages of using multiple threads
Lead-in
Whether you are developing for computers with one processor or several, you want your application to provide the most responsive interaction with the user, even if the application is currently doing other work.
#
#
Application does background processing while keeping the UI responsive
#
Distinguish tasks of varying priority
#
Communicate over a network, to a Web server, and to a database
Potential disadvantages of using threads #
#
Diminished performance due to increased operating system overhead, for example, thread context switching Controlling code execution with many threads is complex, and can be a source of many difficult to find and fix bugs
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Whether you are developing for computers with one processor or several, you want your application to provide the most responsive interaction with the user, even if the application is currently doing other work. Using multiple threads of execution is one of the most powerful ways to keep your application responsive to the user and at the same time make use of the processor in between or even during user events.
Threads Delivery Tip
You can quickly illustrate how multiple threads are used to create nonblocking UI by running the Microsoft Internet Explorer Web browser. Start the browser and type a URL address or click a link to a site that takes some time to download. Show how you can still interact with the application during this download operation, for example, canceling the current download by clicking Stop.
Threads are the basic unit to which an operating system allocates processor time, and more than one thread can execute code inside a process. Each thread maintains exception handlers, a scheduling priority, and a set of structures that the system uses to save the thread context until it is scheduled. The thread context includes all of the information that the thread needs to smoothly resume execution, including the thread’s set of CPU registers and stack, in the address space of the thread’s host process. Operating systems use processes to separate the different applications that they are executing. The .NET Framework further subdivides an operating system process into lightweight, managed subprocesses, called application domains, represented by System.AppDomain. One or more managed threads, represented by System.Threading.Thread, can run in one or any number of application domains within the same process. A preemptive multitasking operating system allocates a processor time slice to each thread that it executes. Because each time slice is small, multiple threads appear to execute at the same time.
4
Module 14 (Optional): Threading and Asynchronous Programming
Advantages of using multiple threads Threads enable multiple activities to appear to occur simultaneously in an application. For example, a user can edit a worksheet while another thread recalculates other parts of the worksheet within the same application. Threading maintains the responsiveness of the user interface while background processing is occurring. Threads can be used to enable users to receive notification of a task’s progress, and even to cancel the task at any time. You can also use threads to distinguish tasks of varying priority. For example, you can use a high-priority thread to manage time-critical tasks, and a lowpriority thread to perform other tasks. Threads are also useful when an application must wait for an event, such as user input or a read from the network or from a file, before continuing to execute. Multiple threads enable the processor to handle a separate task while waiting for the completion of the event.
Potential disadvantages of using threads In some circumstances, threading may cause application performance to degrade. On a single-processor computer, a compute-bound task, such as calculating a series of values by using multiple threads, would be slower because of the overhead caused by thread-switching. Keeping track of a large number of threads consumes significant processor time. If there are too many threads, most of them will not make significant progress. Controlling code execution with many threads is complex, and can be a source of many bugs. You run the risk of data corruption or other problems, such as deadlocks and race conditions. To protect an application’s data from possible corruption, you must ensure that access to shared data is properly synchronized. For more information about deadlocks and race conditions, see Thread Safety in this module. Note While this module focuses on threading, there are other ways of achieving concurrency that include using multiple processes/AppDomains, messaging, and database stored procedures.
Module 14 (Optional): Threading and Asynchronous Programming
5
Overview of Support for Threading in .NET Topic Objective
To provide a high-level overview of the System.Threading namespace and introduce the asynchronous design pattern.
Lead-in
Before examining how threads work in the .NET Framework, let’s briefly review important ways in which the .NET Framework provides support for threading.
!
Basic thread namespace #
!
System.Threading
Standard design pattern for asynchronous programming #
Hides thread creation and synchronization details
#
Supported by .NET Framework delegate classes and/or the IAsyncResult interface
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To provide support for multithreaded programming, the .NET Framework supplies its own namespace for thread creation and management and a programming model to handle asynchronous operations.
Basic thread namespace The System.Threading namespace provides classes and interfaces that enable multithreaded programming. System.Threading includes classes that are used to create and manage threads, protect shared data, and improve system responsiveness. The System.Threading.Thread class is an abstraction of a managed thread that executes within the runtime. This includes threads that are created by the runtime and those that are created outside the runtime but that interact with the runtime environment to execute some managed code. The System.Threading namespace includes classes that assist with thread synchronization and protection of shared data. For example, the System.Threading.Interlocked class provides atomic operations for variables that are shared by multiple threads. The System.Threading.Monitor class provides a synchronization mechanism to ensure that where multiple threads access a shared resource, only one thread can access the resource at a particular time.
6
Module 14 (Optional): Threading and Asynchronous Programming
Standard design pattern for asynchronous programming The standard asynchronous design pattern in the .NET Framework provides an efficient model to manage asynchronous operations and provide a consistent programming model. The .NET Framework provides support for asynchronous programming using the IAsyncResult interface and delegate classes that enable a programmer to avoid some of the implementation details of threading. The asynchronous design pattern is covered in more detail later in this module. For more information about the System.Threading namespace and its classes, see System.Threading Namespace in the .NET Framework SDK documentation.
Module 14 (Optional): Threading and Asynchronous Programming
7
Thread Architecture in .NET Topic Objective
To explain thread architecture in .NET in terms of application domains.
Example of a Process Hosting AppDomains AppDomain A
AppDomain B
Lead-in
Let’s look at the different architectural components that make up a single .NET process.
Shared SharedData Data
Shared SharedData Data
Thread 1
Thread 2
Thread 3
Thread Thread Specific Specific Data Data
Thread Thread Specific Specific Data Data
Thread Thread Specific Specific Data Data
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Application domains, also known as AppDomains, play a key role in how threads work in the .NET Framework. An application domain is a runtime representation of a logical process within a physical process. A single process can contain multiple application domains, each of which is completely isolated from other application domains within this or any other process. One or more threads run in an application domain. Although hosting threads within application domains is conceptually similar to the COM threading model with its use of apartments, there is an important difference: application domains are managed types whereas the COM threading model is built on an unmanaged architecture. For more information about application domains, see Module 2, “Introduction to a Managed Execution Environment,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
8
Module 14 (Optional): Threading and Asynchronous Programming
Mapping from Win32 Threading to Managed Threading The following table maps Microsoft Win32® threading elements to their approximate runtime equivalent. Note that this mapping does not represent identical functionality. For example, TerminateThread does not execute finally clauses or free up resources, and cannot be prevented. However, Thread.Abort executes all of your rollback code, reclaims all of the resources, and can be denied by using ResetAbort. Be sure to read the .NET Framework SDK closely before making assumptions about functionality. In Win32
In the common language runtime
CreateThread
Combination of new Thread() and Thread.Start
TerminateThread
Thread.Abort
SuspendThread
Thread.Suspend
ResumeThread
Thread.Resume
Sleep
Thread.Sleep
WaitForSingleObject on the thread handle
Thread.Join
ExitThread
No equivalent
GetCurrentThread
Thread.CurrentThread
SetThreadPriority
Thread.Priority
No equivalent
Thread.Name
No equivalent
Thread.IsBackground
Close to CoInitializeEx (Ole32.dll)
Thread.ApartmentState
Module 14 (Optional): Threading and Asynchronous Programming
9
" Using Threads in .NET Topic Objective
To provide an overview of the topics that you will cover in this section.
Lead-in
Having briefly reviewed how an operating system uses threads to perform multitasking, let’s look at how threading works in the .NET Framework.
!
Starting Threads
!
Thread Properties and Parameters
!
Managing Threads
!
Thread Local Storage
!
Interrupting and Terminating Threads
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this section you will learn how to start, manage, and terminate threads. The System.Threading namespace includes classes for starting managing, and terminating threads. Being able to perform basic threading operations is only the first requirement in creating multithreaded applications. On a more advanced level, you must consider issues of data protection and performance. These issues are covered in the topic Thread Safety in this module.
10
Module 14 (Optional): Threading and Asynchronous Programming
Starting Threads Topic Objective
To explain how to instantiate and start a new thread by using the .NET Framework classes.
!
Create a new instance of a Thread Object #
Constructor takes a ThreadStart delegate as its only parameter
#
ThreadStart references the method that will be executed by the new thread
Lead-in
Creating a new instance of a Thread class creates a new managed thread. !
Thread is not executed until the Thread.Start method is invoked
Thread Thread tt == new new Thread(new Thread(new ThreadStart(MyClass.AStaticMethod)); ThreadStart(MyClass.AStaticMethod)); t.Start(); t.Start();
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Creating a new instance of a Thread class creates a new managed thread. The constructor for Thread takes, as its only parameter, a ThreadStart delegate that references the method that will be executed by the new thread. The Thread function does not begin executing until the Start method is called.
Module 14 (Optional): Threading and Asynchronous Programming
11
The following code shows how to create and start new threads: using System; using System.Threading; class MyClass { //… public static void AStaticMethod() { //… } public void AnInstanceMethod() { //… } } class App { static void Main() { // Create and start thread on an instance method MyClass aMyClass = new MyClass(); Thread t1 = new Thread(new ThreadStart(aMyClass.AnInstanceMethod)); t1.Start(); // Create and start thread on a static method Thread t2 = new Thread(new ThreadStart(MyClass.AStaticMethod)); t2.Start(); //… } }
In the preceding example, the calls to t1.Start and t2.Start place the t1 and t2 threads in the running state, and the operating system can schedule them for execution. The Start method submits an asynchronous request to the system, and the call returns immediately, possibly before the new thread has started. The thread’s execution begins at the first line of the method that is referred to by the thread delegate. Calling Start more than once on the same thread causes the runtime to throw a ThreadStateException.
12
Module 14 (Optional): Threading and Asynchronous Programming
Thread Properties and Parameters Topic Objective
To describe some key Thread class properties and how to pass parameters to threads.
!
Use the Thread.Name and Thread.Priority properties to get or set the name and priority of the thread
Lead-in
!
Designate a thread as a background or a foreground thread by setting the Thread.IsBackground property
To get or set the name of a thread and its priority, use the Thread.Name and Thread.Priority properties.
#
A background thread will not keep the managed execution environment alive
t.Name t.Name == "My "My Background Background Thread"; Thread"; t.Priority = ThreadPriority.AboveNormal; t.Priority = ThreadPriority.AboveNormal; t.IsBackground t.IsBackground == true; true; !
Encapsulate thread parameters in an object
*****************************ILLEGAL FOR NON-TRAINER USE****************************** To get or set the name of a thread and its priority, use the Thread.Name and Thread.Priority properties. Thread.Priority gets or sets the following values that indicate the scheduling priority of a thread: !
Highest
!
AboveNormal
!
Normal
!
BelowNormal
!
Lowest
For example, to set the name of a thread t to “My Background Thread” and make its priority AboveNormal, you use the following code: t.Name = "My Background Thread"; t.Priority = ThreadPriority.AboveNormal;
Note Because the details of scheduling algorithms vary with each operating system, operating systems are not required to honor the priority of a thread.
Module 14 (Optional): Threading and Asynchronous Programming
13
Background and Foreground Threads A managed thread runs as a background thread or a foreground thread. Background threads are identical to foreground threads except that a background thread does not keep the managed execution environment alive. After all foreground threads have been stopped in a managed process (where the .exe file is a managed assembly), the system stops all background threads and shuts down. You can designate a thread as a background or foreground thread by setting the Thread.IsBackground property. For example, to designate a thread as a background thread, you set Thread.IsBackground to true. Likewise, to designate a thread as foreground thread, set IsBackground to false. All threads that enter the managed execution environment from unmanaged code are marked as background threads. All threads that are generated by creating and starting a new Thread object are foreground threads. If you create a thread that you want to listen for some activity, such as a socket connection, you should set Thread.IsBackground to true, so that your process can terminate. For example, to set a thread t to be a background thread: t.IsBackground = true;
14
Module 14 (Optional): Threading and Asynchronous Programming
Encapsulating thread parameters in an object It is sometimes important to supply parameters to a thread. However, the ThreadStart delegate takes as its only parameter a ThreadStart delegate. The following code demonstrates how to use an object to encapsulate thread parameters: using System; using System.Threading; class MyClassWithThreadState { int sleepTime; public MyClassWithThreadState(int sleepTime) { this.sleepTime = sleepTime; } public void ThreadMethod() { // method can reference sleepTime Thread.Sleep(sleepTime); } } class Class1 { static void Main() { int sleepTime = 1000; // time for the thread to sleep MyClassWithThreadState myClassWithThreadState = new MyClassWithThreadState(sleepTime); Thread ts = new Thread(new ThreadStart(myClassWithThreadState.ThreadMethod)); ts.Start(); // … } }
Module 14 (Optional): Threading and Asynchronous Programming
15
Managing Threads Topic Objective
To describe the classes that the runtime uses to pause, resume, and force threads to wait.
!
!
Lead-in
Let’s look at how the runtime provides classes to manage threads.
Thread.Sleep causes the current thread to block
Thread.Sleep(3000); Thread.Sleep(3000); // // blocks blocks for for 33 seconds seconds Suspend and Resume methods are not generally useful #
!
Can result in serious application problems like deadlocks
Thread.Join waits for another thread to stop
t.Start(); t.Start(); t.Join(); t.Join(); // // Wait Wait for for the the thread thread to to exit exit !
Thread.WaitHandle methods wait for one or more events
WaitHandle.WaitAll(waitEvents); WaitHandle.WaitAll(waitEvents); ! Thread.ThreadState property - bit mask of the thread's state
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Typically, thread management includes tasks such as suspending a thread for a period of time, waiting until another thread completes, or waiting until one or more specific events occur.
Pausing and Resuming Threads After starting a thread, you sometimes need to pause that thread for a fixed period of time. Calling Thread.Sleep causes the current thread to immediately block for the number of milliseconds you pass to Sleep, yielding the remainder of its time slice to another thread. One thread cannot call Sleep on another thread. For example, to cause the current thread to suspend for three seconds, call the static method of Thread.Sleep as follows: Thread.Sleep(3000);
Calling Thread.Sleep(Timeout.Infinite) causes a thread to sleep until it is interrupted by another thread that calls Thread.Interrupt or is aborted by Thread.Abort.
Using Thread.Suspend to pause a thread You can also pause a thread by calling Thread.Suspend. When a thread calls Thread.Suspend on itself, the call blocks until the thread is resumed by another thread. When one thread calls Thread.Suspend on another thread, the call is a nonblocking call that causes the other thread to pause. Calling Thread.Resume breaks another thread out of the suspended state and causes the thread to resume execution, regardless of how many times Thread.Suspend was called. For example, if you call Thread.Suspend five consecutive times and then call Thread.Resume, the thread resumes execution immediately following the call to Resume.
16
Module 14 (Optional): Threading and Asynchronous Programming
Unlike Thread.Sleep, Thread.Suspend does not cause a thread to immediately stop execution. The runtime must wait until the thread has reached a safe point before it can suspend the thread. A thread cannot be suspended if it has not been started or if it has stopped. For more information about safe points, see the .NET Framework SDK. The Suspend and Resume methods are not generally useful for applications, and you should not confuse them with synchronization mechanisms. Because Suspend and Resume do not rely on the cooperation of the thread that is being controlled, they are highly intrusive and can cause serious application problems. For example, if you suspend a thread that holds a resource needed by another thread, this causes a deadlock condition. Some applications do need to control the priority of threads for better performance. To do this, you should use Thread.Priority rather than Thread.Suspend in your application.
Using Thread.Join to pause a thread You can force a thread to wait for another thread to stop by calling Thread.Join, as shown in the following code: using System; using System.Threading; class MyApp {//… static void MyThreadMethod() { //… } static void Main() { // create, start and join a simple background thread // MyThreadMethod is the secondary thread's entry point. Thread t = new Thread(new ThreadStart(MyThreadMethod)); // Start the thread t.Start(); // Wait for the thread to exit t.Join(); } }
Module 14 (Optional): Threading and Asynchronous Programming
17
Using the WaitHandle class to wait for events You can force a thread to wait for one or more events to occur by calling methods in the Thread.WaitHandle class. The following table describes some of the methods in the WaitHandle class. Method
Description
WaitAll
This method waits for all of the elements in the specified array to receive a signal.
WaitAny
This method waits for any of the elements in the specified array to receive a signal.
WaitOne
This method blocks the current thread until the current WaitHandle receives a signal.
The WaitHandle class encapsulates Win32 synchronization handles. While WaitHandle objects represent operating system synchronization objects and expose advanced functionality, they are less portable than the Monitor.Wait method, which is fully managed and, in some circumstances, is more efficient in its use of operating system resources.
Using Classes that are derived from WaitHandle Examples of classes that derive from WaitHandle include AutoResetEvent, ManualResetEvent, and Mutex. You use the AutoResetEvent class to make a thread wait until some event puts it in the signaled state by calling AutoResetEvent.Set. Unlike ManualResetEvent, AutoResetEvent is automatically reset to nonsignaled by the system after a single waiting thread has been released. If no threads are waiting, the event object’s state remains signaled. The AutoResetEvent corresponds to a Win32 CreateEvent call, false specified by the bManualReset argument.
18
Module 14 (Optional): Threading and Asynchronous Programming
For example, the following code shows how to force the main thread to wait until the threads that it has created signal that they have finished executing: using System; using System.Threading; class MyClassWithThreadState { AutoResetEvent done; public MyClassWithThreadState(AutoResetEvent done) { this.done = done; } public void ThreadMethod() { //… done.Set(); // signal that we are finished } } class MyApp { //… static void Main() { //… int numberOfThreads = 3; // number of threads to create AutoResetEvent[] waitEvents = new AutoResetEvent[numberOfThreads]; Thread ts; MyClassWithThreadState myClassWithThreadState; for (int i=0; i
valuetype<MyStruct>
MyEnum
ByRef VALUETYPE<MyEnum>
valuetype<MyEnum>
MyInterface *
ByRef CLASS <MyInterface>
Class<MyInterface>
MyCoClass
ByRef CLASS
class
MyType
You can apply the System.Runtime.InteropServices.MarshalAsAttribute to a method parameter, class field, or return value to change the marshaling behavior. For example, a string is converted to a BSTR type when marshaled from managed to unmanaged code, unless you explicitly apply the MarshalAsAttribute to marshal the string to another type, such as LPWSTR. You can apply this attribute to a parameter, field, or return value within the source of the type definition, as shown in the following examples where it is specified that msg is to be marshaled as a null-terminated buffer of Unicode characters (LPWStr). Apply the MarshalAsAttribute to a parameter: public void M1 ([MarshalAs(UnmanagedType.LPWStr)]String msg);
Apply the MarshalAsAttribute to a field within a class: class MsgText { [MarshalAs(UnmanagedType.LPWStr)] Public String msg; }
Apply the MarshalAsAttribute to a return value: [return: MarshalAs(UnmanagedType.LPWStr)] public String GetMessage();
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
35
Although the import process generally produces accurate interop assemblies, there are cases when you can (or must) modify the interop assembly to produce a custom RCW. There are several reasons why you might customize an RCW, such as: !
One or more types require additional marshaling information.
!
A type library contains many specialized types that are unrecognizable to the marshaler.
!
A large type library can include types that are unnecessary for an application. You can eliminate the need to deploy unnecessary types by creating an interop assembly from managed source code.
If you must customize the runtime callable wrapper with additional or different marshaling instructions, you can either: 1. Edit the interop assembly, searching for problematic syntax and replacing it with alternative syntax. This option is best for minor marshaling changes. 2. Create a wrapper manually, based on an existing Interface Definition Language (IDL) file or type library. Declaring COM types manually is a difficult activity that requires working knowledge of the Type Library Importer (Tlbimp.exe), the default behavior of the interop marshaler, and COM. This approach is best used when you have an entire library of specialized types or require the RCW source code. For more information about implementing these choices, see the .NET Framework Software Development Kit (SDK) documentation.
36
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
Performance and Security Issues Topic Objective
To list some performance issues that arise from running unmanaged code from managed code.
!
Lead-in
Running unmanaged code from managed code decreases the performance of your application.
Recognize that transitions that occur when managed code must interact with unmanaged code result in an overhead of 10 to 40 instructions per call. #
Limit the number of transitions that appear in your code
#
Instead, use API calls that perform several actions at once
!
Eliminate the creation of the proxy and stub by setting the ApartmentState on the thread before creating a COM object
!
For trusted code, suppress security checks that are performed when managed code calls unmanaged code
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Transitions occur when managed code must interact with unmanaged code. A slight overhead is associated with every transition. These overheads require 10 to 40 instructions per call. The best practice is to limit the number of transitions that appear in your code. If you must use transitions, use API calls that perform several actions from the same call instead of ones that only perform few actions and must be repeatedly called. Threading models that are used in managed and unmanaged code affect the performance of applications. Whenever a COM object and a managed thread are in incompatible apartments, all calls on the object are made through a COM-created proxy. To eliminate the use of proxies and stubs, and significantly enhance performance, set the ApartmentState on the thread before creating the object. Security checks that are performed when unmanaged code is run, also lead to a decrease in application performance. You can suppress these security checks and increase the performance of your application. When managed code calls unmanaged COM code by using COM interoperability services, a demand for the UnmanagedCode permission is made to ensure that all callers have the necessary permission to allow the call to proceed. You can suppress the demand for the UnmanagedCode permission at run time by using the SuppressUnmanagedCodeSecurityAttribute attribute. The demand for the UnmanagedCode permission will still occur at link time.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
37
The SuppressUnmanagedCodeSecurityAttribute attribute helps improve code performance by suppressing asserts and demands at run time. These run time asserts and demands are the usual security checks performed when calling unmanaged code. Only code that has been granted the UnmanagedCode permission can use the SuppressUnmanagedCodeSecurityAttribute attribute, however. Using this attribute in a class or module applies to all methods contained in the class or module. When you use the SuppressUnmanagedCodeSecurityAttribute attribute in a class, code that does not have permission to access unmanaged code can call methods in the class to access unmanaged code. Therefore, you must make sure that when you use the SuppressUnmanagedCodeSecurityAttribute attribute, you write secure code. Otherwise, your code can be misused by malicious code.
38
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
Best Practices Topic Objective
To list some best practices to be followed when calling COM components from managed code.
!
The MSIL Disassembler can be used on the generated assembly to determine the exact names of the namespace and classes that are generated
!
Any parameter passed by reference in C++ or Visual Basic 6.0 will generate a parameter that needs to be passed by using the ref keyword in C#
Lead-in
Let’s look at some best practices that you should use when calling COM components from managed code.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** If you create your runtime callable wrapper assembly by adding a reference to a COM object in Visual Studio .NET or by using the Type Library Importer, the namespace and class names that are generated might not always be obvious. To view these names, you can run the MSIL Disassembler on the particular assembly. If you create the runtime callable wrapper assembly with Visual Studio .NET, you must attempt to build the project once to generate the assembly. Parameters passed by reference in unmanaged code frequently require the use of the ref keyword when that code is called by using C#. For C or C++, this includes most parameters passed as pointers. For Visual Basic 6.0, keep in mind that, by default, all parameters are passed by reference. The most notable exception to this is for passing string types, which can usually be passed as String or StringBuilder objects from managed code. For an example in C or C++ of passing a string type, see Marshaling in this module.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
39
Lab 15.2: Calling COM Objects Topic Objective
To introduce the lab.
Lead-in
In this lab, you will call a COM object from managed code.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives After completing this lab, you will be able to call a COM object from managed code.
Estimated time to complete this lab: 30 minutes
40
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
Exercise 1 Calling a COM Object From Managed Code In this exercise, you will call a COM object from managed code. This includes registering the COM DLL, adding a reference to the COM object in a Visual Studio .NET project, and then adding code to call the COM object. Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window, click Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.
! Register the COM object 1. At the command prompt, change the current folder to \Labs\ Lab15\Lab15.2\Starter. 2. To register the COM object, run regsvr32 VBUnmanagedCalculator.dll. 3. A dialog box appears informing you that the registration succeeded. Click OK to dismiss the dialog box.
! Open the Visual Studio .NET project for this exercise and examine the application UI
1. In Windows Explorer, move to \Labs\Lab15\Lab15.2\ Starter. 2. Double-click CallingComFromDotNet.csproj to open the project for this exercise in Visual Studio .NET. 3. In the Solution Explorer pane, double click CalcUI.cs. The UI design for the Windows Forms application appears. Examine the UI for a moment to become familiar with it. 4. Press F7 to edit the code that implements the Windows Form application.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
41
! Add the reference to the COM object and attempt to build the project 1. On the Project menu, click Add Reference. 2. In the Add Reference dialog box, click the COM tab. In the list of registered COM objects, click VBUnmanagedCalculator. 3. To add VBUnmanagedCalculator to the Selected Components window, click Select. 4. To add the reference, click OK. Notice that the VBUnmanagedCalculator DLL is added to the project references. 5. On the Build menu, click Build Solution. Your application does not build successfully yet, but the runtime callable wrapper assembly for the VBUnmanagedCalculator.dll is generated. 6. In a Visual Studio .NET Command Prompt window, change the current folder to \Labs\Lab15\Lab15.2\ Starter\bin\Debug. 7. To view the metadata in the runtime callable wrapper assembly, at the command prompt, type ildasm /adv Interop.VBUnmanagedCalculator.dll Use the MSIL Disassembler to view the namespace and classes created for the COM object.
! Modify the code in the project to call the COM object 1. In the CalcUI.cs file, find the first ‘TODO’ comment that instructs you to add a using statement for VBUnmanagedCalculator. Add the correct using statement. Notice that you can use the Task List tab in the lower-left hand pane to find the ‘TODO’ comments. Click View, select Show Tasks, and then click All if the ‘TODO’ comments do not appear in the task list. 2. Find the next ‘TODO’ comment that instructs you to instantiate a CalcEngine object. Add the code to instantiate a CalcEngine object and assign it to the calcEngine private member variable. 3. Scroll down to the next ‘TODO’ comment that instructs you to call GetVersion in CalcEngine. Add the code to make the call described and assign the results of the call to the VersionInfo text box. 4. Scroll down to the KeyDate_Click method. Complete the code in this method by replacing the ‘TODO’ comment with code to obtain the date by calling the GetDate method in CalcEngine and assign the results of the call to the OutputDisplay text box. Then, add code to reset the engine for the next equation by calling the CalcReset method in CalcEngine. 5. In the KeyClear_Click method, complete the code by replacing the ‘TODO’ comment with code that calls the CalcReset method in CalcEngine and clears the OutputDisplay text box.
42
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
! Build and test your application 1. On the File menu, click Save All. 2. On the Build menu, click Build Solution. Your application should build successfully. The following statement should appear as the last line of the output window: Build: 1 succeeded, 0 failed, 0 skipped.
3. On the Debug menu, click Start. When the calculator appears, check the version number. It should read “Calculator – VB6 Unmanaged Component”. Enter some equations to test the application. 4. Close the application. Note If you wish to test the solution project, you will first need to follow the steps in the “Register the COM object”, “Open the Visual Studio .NET project for this exercise and examine the application UI”, and “Add the reference to the COM object and attempt to build the project” procedures above.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
43
" Calling .NET Objects from COM Objects Topic Objective
To introduce how COM callable wrappers are used to expose .NET Framework classes to COM clients.
Lead-in
In this section, you will learn how to create COM callable wrappers to use .NET Framework types for a COM client.
!
COM Callable Wrappers
!
The Export Process
!
Registering a .NET Framework Class with the System Registry
!
Using the ClassInterfaceAttribute
!
Using .NET Framework Types from COM
!
Managed and Unmanaged Events
!
Best Practices
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework allows existing COM applications to use managed code transparently by generating COM callable wrappers for .NET Framework classes. These wrappers are generated at run time. COM callable wrappers are proxy objects that are generated from the metadata that is stored in an assembly, so COM applications can use the .NET Framework classes in the same manner as they would use COM components.
44
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
COM Callable Wrappers Topic Objective
To describe how the runtime uses COM callable wrappers to expose managed classes to COM clients.
Lead-in
To allow COM clients to use a managed class, the .NET Framework creates a COM callable wrapper.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** COM clients cannot reference a .NET Framework object directly. When a COM client calls a .NET Framework object, the common language runtime creates the managed object and a proxy for the managed object called the COM callable wrapper. The purpose of a COM callable wrapper is to marshal calls between managed and unmanaged code, and to manage the identity and lifetime of the managed object it wraps. COM callable wrappers are invisible to other classes running within the .NET Framework. Unlike the managed object it wraps, the COM callable wrapper is allocated memory from a non-collected heap. This makes it possible for COM clients to reference the wrapper directly. COM callable wrappers are reference-counted in the traditional COM fashion. When the reference count on the wrapper reaches zero, the wrapper releases its reference on the managed object. A managed object with no remaining references is collected during the next garbage-collection cycle. The runtime creates one COM callable wrapper for a managed object, regardless of the number of COM clients requesting its services. The COM callable wrapper, in turn, holds a single reference to the managed object that implements the interface. Both COM clients and .NET Framework clients can make requests on the same managed object simultaneously. When you compile your managed code into an assembly, metadata describing each type in the assembly is generated. The Type Library Exporter can be used to create a type library file from the metadata for your assembly. COM clients can then import the type library and use the managed object like any other COM object. When the COM client tries to create and access a managed object from the assembly, the runtime uses the assembly’s metadata to generate a COM callable wrapper. The wrapper marshals the call between the COM client and the managed object.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
45
You can customize the COM callable wrapper by applying interop-specific attributes to your managed source code and compiling the source code into an assembly. Caution If you plan to export a managed object, be aware that the COM client determines the apartment of the object. A managed object called by a COM client initialized in an MTA must ensure thread safety.
46
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
Demonstration: Using the Type Library Exporter Topic Objective
To describe the use of the Type Library Exporter.
Lead-in
The Type Library Exporter is a command-line tool that exports the type information to a type library so that it can be used from a COM client.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The Type Library Exporter (Tlbexp.exe ) is a command-line tool that builds a type library for .NET Framework classes so that they can be used from unmanaged clients. The Type Library Exporter uses the metadata that is stored in the assembly to generate the type library. The type library contains definitions of the types defined in an assembly. Applications that use unmanaged code can use the generated type library to bind to the .NET Framework types defined in the assembly. Note You cannot use the Type Library Exporter to produce a type library from an assembly that was imported by using the Type Library Importer (Tlbimp.exe). Instead, you should refer to the original type library that was imported with the Type Library Importer. You can export a type library from an assembly that references assemblies that were imported by using the Type Library Importer. You must use the Type Library Exporter to generate a type library for an entire assembly and not just a subset of the types that are defined in the assembly. A single assembly may cause several type libraries to be generated, and these type library files are placed in the current working directory or the directory specified for the output file when running the Type Library Exporter. The Type Library Exporter generates a type library but does not register the types it exports with the system registry. To generate and register a type library with COM, use the Assembly Registration tool (Regasm.exe). For information about using the Assembly Registration tool to register a type library, see Registering a .NET Framework class with the System Registry in this module.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
47
The Export Process Topic Objective
To describe how the export process converts classes and interfaces.
Lead-in
The export process creates definitions for the classes and interfaces that are defined in an assembly.
!
Every public class in an assembly is converted to a coclass in a type library
!
Interfaces that are explicitly implemented are exported to the type library
Managed Code public public interface interface IShape IShape {{ void Draw(); void Draw(); void void Move(int Move(int x, x, int int y); y); }} class class Circle Circle :: IShape IShape {{ void Draw(); void Draw(); void void Move(int Move(int x, x, int int y); y); void void Enlarge(int Enlarge(int x); x); }}
Type Library [[ uuid(…), uuid(…), dual, dual, odl, odl, oleautomation oleautomation ]] interface interface IShape IShape :: IDispatch IDispatch {{ HRESULT HRESULT Draw(); Draw(); HRESULT HRESULT Move(int Move(int x, x, int int y); y); }} [[ uuid(…) uuid(…) ]] coclass coclass Circle Circle {{ interface interface IShape; IShape; }} }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When you run the Type Library Exporter on your assembly to generate a type library, the export process converts the classes and interfaces defined in your assembly, and creates representations for them in the type library. The export process converts each public class in an assembly to a coclass in a type library, and gives the coclass the same name as the managed class. All the interfaces that are explicitly implemented by the managed class are exported to the type library. Methods and properties are not exported. When the class in the example in the slide is exported, the type library will contain a representation for the IShape interface and a coclass definition for the Circle class. The Draw, Move, and Enlarge methods of the Circle class will not be exported. When an assembly is exported, all managed interfaces and their methods and properties are converted to COM interfaces. By default, the COM interface that is generated in the type library file is a dual interface. You can use the ClassInterfaceAttribute to determine the type of interface that is defined when the type library is exported. Each coclass can implement one other interface, called the class interface, which the export process generates automatically. The class interface exposes all methods and properties available in the original managed class, thereby enabling COM clients to access them by calling through the class interface.
48
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
Registering a .NET Framework Class with the System Registry Topic Objective
To describe the procedure for registering a .NET Framework class with the system registry.
!
To register a .NET Framework class with the system registry, use the Assembly Registration tool (Regasm.exe)
!
Registering a .NET Framework class creates the following entries in the system registry:
Lead-in
Before you can use a .NET Framework class from unmanaged code, you must register the class with the system registry.
#
#
The CLSID of the .NET Framework class under the Hkey_Classes_Root\CLSID registry key Two sub keys: Implemented Categories and InprocServer32 under the HKCR\CLSID\ key
!
The default value of the HKCR\CLSID\ key is set to the ProgID of the class
!
The default value of the InprocServer32 is set to the name of the DLL that contains the common language runtime (Mscoree.dll)
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Before you can use a .NET Framework class from unmanaged code, you must register the class with the system registry. To register a .NET Framework class with the system registry, you use the command-line Assembly Registration tool (Regasm.exe). The Assembly Registration tool adds information about the class to the system registry so COM clients can use the .NET Framework class transparently. You can also use the RegistrationServices class from within your managed code, to register your .NET Framework class with the system registry. When you register a .NET Framework class, the following entries are made in the system registry: !
A new entry for the class identifier (CLSID) of the .NET Framework class is created under the HKEY_CLASSES_ROOT\CLSID registry key. After the CLSID is generated for the .NET Framework class, the Assembly Registration tool always uses the same CLSID, regardless of the number of times it is used on the class.
!
Under the HKEY_CLASSES_ROOT\CLSID\ key, are two sub keys: ImplementedCategories and InprocServer32. The default value of the HKEY_CLASSES_ROOT\CLSID\ key is set to the ProgID of the class. The default value of the InprocServer32 is set to the name of the DLL that contains the common language runtime (Mscoree.dll).
When a COM client attempts to load the assembly in which the class is implemented, the runtime reads the assembly value from the registry and passes it on to the runtime assembly resolver. The assembly resolver attempts to locate the assembly based on the assembly information, such as the name and version number. Before the assembly resolver can locate the assembly, however, the assembly must be signed and installed in the global assembly cache, or it must be in the application’s root directory or a subdirectory of the application’s root directory.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
49
Using the ClassInterfaceAttribute Topic Objective
To describe the use of the ClassInterfaceAttribute to control the type of interface that is created for a managed class.
!
The class interface is an interface that exposes all public methods, properties, fields, and events that are explicitly exposed on the .NET Framework object
Lead-in
!
The [ClassInterface(ClassInterfaceType.None)] attribute prevents the class interface from being created
!
The [ClassInterface(ClassInterfaceType.AutoDispatch)] attribute creates a dispatch interface
!
The [ClassInterface(ClassInterfaceType.AutoDual)] attribute creates a dual interface
When used with a .NET Framework class, the ClassInterfaceAttribute can be used to control and modify the type of interface that is generated when that .NET Framework class is exported.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The class interface is an interface that exposes all public methods, properties, fields, and events that are explicitly exposed on a .NET Framework object. This interface can be a dual or dispatch-only interface. The class interface receives the name of the .NET Framework class, preceded by an underscore. When you use the Type Library Exporter to generate a type library for a managed class, a dispatch-only interface for each class you export is created by default. You can use the ClassInterfaceAttribute with your class to prevent or modify the automatic creation of this interface. When you use the ClassInterfaceAttribute, you must add the Sytem.Runtime.InteropServices namespace to your code. You use the ClassInterfaceAttribute and pass the value ClassInterfaceType.None to the constructor, to prevent the class interface from being generated when the class metadata is exported to a type library, as shown in the following example: [ClassInterface(ClassInterfaceType.None)] public class LoanApp : IExplicit { void M(); }
In the preceding example, COM clients can access the LoanApp class only through the IExplicit interface.
50
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
To avoid breaking late-bound COM clients when using the class interface, set the Value property of the ClassInterfaceAttribute to ClassInterfaceType.AutoDispatch. This value implements a dispatch-only class interface, but omits the interface description from the type library. Without an interface description, clients are unable to cache dispatch identifiers (DISPIDs) at compile time. Although this is the default interface type for the class interface, you can apply the attribute value explicitly, as shown in the following example: [ClassInterface(ClassInterfaceType.AutoDispatch)] public class LoanApp : IAnother { void M(); }
To get the DISPID of an interface member at run time, COM clients can call IDispatch.GetIdsOfNames. To invoke a method on the interface, pass the returned DISPID as an argument to IDispatch.Invoke. A dual interface that is automatically generated (ClassInterface.AutoDual) might be appropriate in rare cases. However, a dual interface that is automatically generated often creates version-related complexities. For example, COM clients that are using the class interface of a derived class can easily break when there are changes to the base class. When a third party provides the base class, the layout of the class interface is out of your control. Further, unlike a dispatch-only interface, a dual interface provides a description of the class interface in the exported type library. Such a description encourages late-bound clients to cache DISPIDs at run time.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
51
Using .NET Framework Types from COM Topic Objective
To describe the procedure for using .NET Framework types from COM clients.
!
#
Lead-in
After you have generated a type library for your .NET assembly, you can use the assembly from unmanaged code.
To Use a .NET Framework Class from Unmanaged Code Set a reference to the type library file that describes the interferences of the .NET Framework class
!
In Visual Basic 6.0, Use the References Dialog Box to Set the Reference
!
In Visual C++ 6.0, Use the #import Directive to Reference to the Type Library
!
Create and Use Objects of the Classes That Are Defined in the Type Library
*****************************ILLEGAL FOR NON-TRAINER USE****************************** After you have exported the metadata from an assembly to a type library file, you can use the .NET Framework classes that are implemented in the assembly as you would create and use a COM object.
! To use a .NET Framework class that has been exported to a type library from a Visual Basic 6.0 application
1. In Visual Studio 6.0, on the Project menu, click References. The References dialog box appears. 2. To add the reference to your project, scroll down Available References list, click the reference you want to add, and then click OK. 3. Create an object of the .NET Framework class (as you would create a COM object), as shown in the following example: Dim Server As New ExcelServer.ExcelServer
4. Use the methods and properties of the class, as shown in the following example: Dim Count As Integer Count = Server.Count
To use a .NET Framework class from a Microsoft Visual C++® 6.0 application, use the #import directive to reference the type library, as shown in the following example: #import "c:\Samples\SampleClassLibrary\SampleClassLib.tlb"
52
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
Managed and Unmanaged Events Topic Objective
To understand at a high level interoperability between .NET and COM events
!
.NET event model differs from COM event model #
Lead-in
Let’s look at how the NET Framework event model differs from the traditional COM event model.
!
Managed events are based on delegates, whereas unmanaged events (in COM) are based on connection points.
See the .NET SDK for details on the topics: #
Handling Events Raised by a COM Source
#
Raising Events Handled by a COM Sink
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework event model differs from the traditional COM event model. Managed events are based on delegates, whereas unmanaged events in COM are based on connection points. Both models are tightly coupled event systems, because the client (event receiver) and server (event sender) must run simultaneously. The SDK explains in detail how to bridge the managed and unmanaged event systems, enabling objects to send and receive events across the interoperation boundary. Topics include Handling Events Raised by a COM Source and Raising Events Handled by a COM Sink.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
53
Best Practices Topic Objective
To list some best practices when using managed classes from COM clients.
!
Define an Explicit Interface for COM Clients to Use Rather Than Generating the Class Interface
!
Avoid Caching Dispatch Identifiers
!
Restrict Using the Dual Interface Option for the Class Interface
Lead-in
When exposing managed code to COM clients, follow the best practices listed on the slide.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When exposing managed code to COM clients, use the following best practices: !
Define an explicit interface for COM clients to use rather than generating the class interface. Because COM interop services generate class interfaces automatically, postversion changes to your class can alter the layout of the class interface exposed by the common language runtime. Since COM clients are typically unprepared to handle changes in the layout of an interface, they break if you change the member layout of the class. To reduce the risk of breaking COM clients by inadvertently reordering the interface layout, isolate all changes to the class from the interface layout by explicitly defining interfaces. Set the Value property of ClassInterfaceAttribute to ClassInterfaceType.None to disengage the automatic generation of the class interface and implement an explicit interface for the class.
!
Avoid caching dispatch identifiers (DISPIDs). Using the class interface is an acceptable option for scripted clients, Visual Basic 6.0 clients, or any late-bound client that does not cache the DISPIDs of interface members. DISPIDs identify interface members and enable late binding. For the class interface, generation of DISPIDs is based on the position of the member in the interface. If you change the order of the member and export the class to a type library, you will alter the DISPIDs generated in the class interface.
54
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
To avoid breaking late-bound COM clients when using the class interface, set the Value property of the ClassInterfaceAttribute to ClassInterfaceType.AutoDispatch. This value implements a dispatch-only class interface, but it omits the interface description from the type library. Without an interface description, clients are unable to cache DISPIDs at compile time. Although this is the default interface type for the class interface, you can apply the attribute value explicitly. !
Restrict using the dual interface option for the class interface. Dual interfaces enable early and late binding to interface members by COM clients. At design time and during testing, you might find it useful to set the class interface to dual. For a managed class and its base classes that will never be modified, this option is also acceptable. In all other cases, avoid setting the class interface to dual.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
55
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
Integration Services
!
Platform Invoke
!
Calling COM Components from Managed Code
!
Calling .NET Objects from COM Objects
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. List the steps for calling an API function that is implemented in a DLL. Declare the API function with the static and extern C# keywords, attach the DllImport attribute to the function specifying the name of the DLL that exports the unmanaged function, and optionally specify marshaling information.
2. What is pinning? Pinning is a technique in which data, in its current memory location, is temporarily locked to keep it from being relocated by the common language runtime’s garbage collector.
3. List the tasks that are performed when the Type Library Importer is run on a type library file. The Type Library Importer converts unmanaged COM coclasses to C# classes with a constructor (that does not have parameters) and no other methods, converts unmanaged COM vtable interfaces to C# interfaces, and converts unmanaged COM structures to C# structures with public fields.
4. Which attribute must you use to suppress runtime security checks that are preformed when managed code calls unmanaged code? SuppressUnmanagedCodeSecurityAttribute
56
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code
5. List some best practices when using .NET code from COM clients. Define an explicit interface for COM clients to use rather than generating the class interface, and avoid caching dispatch identifiers (DISPIDs).
6. Which must you use to prevent the class interface from being generated? Set the Value property of the ClassInterfaceAttribute to ClassInterfaceType.None
Contents Overview Overview of ADO.NET
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 1 2
Connecting to a Data Source
10
Accessing Data with DataSets
12
Using Stored Procedures
26
Lab 16: Using ADO.NET to Access Data
34
Accessing Data with DataReaders
42
Binding to XML Data
50
Review
56
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, places or events is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
iii
Instructor Notes Presentation: 120 Minutes
This module focuses on using ADO.NET to access data from various data sources.
Lab: 60 Minutes
After completing this module, students will be able to: !
Describe the ADO.NET object model.
!
Connect to a data source by using ADO.NET.
!
Retrieve data from a database by using DataReaders and DataSets.
!
Display the data from a database on the client by using DataGrid controls.
!
Use stored procedures to read data from a data source.
!
Read data from an XML file into DataSets.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need the Microsoft® PowerPoint® file 2349B_16.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Complete all of the demonstrations.
!
Complete the lab.
!
Go through the animation.
iv
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Multimedia Presentation This section provides multimedia presentation procedures that do not fit in the margin notes or are not appropriate for the student notes.
Using ADO.NET to Access Data ! To present the animation Action
Say this
Start animation
There are two ways to access data from a database by using ADO.NET: by using a DataSet or by using a DataReader. This animation demonstrates how these two methods work and highlights their differences.
Click Start Click DataSet
The DataSet method is a disconnected way to access data from a database. In this method, when a user requests data from a database, the DataAdapter object is used to create a DataSet, which is basically a collection of data tables from the database that also retains the relationships between these tables. Notice that, after a DataSet is populated, it is disconnected from the database. To display the data from the DataSet, you set up a DataView for the desired table. The DataView is then bound to a listbound control for displaying purposes. You can use any of the three list-bound controls, DataGrid, Repeater, or DataList, to display data. The data in the list-bound control is then displayed on the client. An important point to make here is that the use of a DataView to display data is necessary only in ASP.NET Beta 1. From the Beta 2 version onward, you can directly bind the DataSet to a list-bound control.
Click DataReader
This method is similar to the Microsoft ActiveX® Data Objects (ADO) way of accessing data by using recordsets. In this method, when a user requests data from a database, the Command object retrieves the data into a DataReader. A DataReader is a read-only/forward-only view of the data. A DataReader works similarly to a Recordset in ADO, allowing you to simply loop through the records. Like the ADO Recordset, the DataReader is connected to the database. You must explicitly close the connection when you are finished reading data.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
v
Module Strategy Use the following strategy to present this module: !
Overview of ADO.NET This section provides students with an overview of ADO.NET. The section begins a description of the objects used when connecting to a database both with a DataReader and a DataSet. Point out to students that there are Microsoft SQL Server™ and ADO versions of many of these objects. After describing the process of accessing data through a DataReader and a DataSet, show the animation. Because students may be familiar with ADO, this may be an ideal time to discuss some of the main differences between ADO and ADO.NET. When talking about using namespaces, explain their significance to the students.
!
Connecting to a Data Source From this point onward, students will actually start working with ADO.NET. Tell them that all of the examples in this module use SqlConnection objects rather than OleDbConnection objects. Direct the students to the Microsoft .NET Framework software development kit (SDK) documentation for more information.
!
Accessing Data with DataSets ADO.NET provides two ways to access data, the DataSet and the DataReader. This section focuses on accessing data by using the DataSet. The DataSet represents a new concept, so spend additional time on this section. The demonstrations actually show every aspect of data access with ADO.NET. Go through the demonstrations carefully, and make sure that the students understand the details.
!
Using Stored Procedures Most students who have worked with a SQL Server database and ADO will have experience with using stored procedures. This section provides the students with information about how to use stored procedures and parameterized stored procedures with ADO.NET.
!
Lab16: Using ADO.NET to Access Data The lab for this module is encountered in the middle of the module. This is because the module is long and also because the lab does not use material from the last two sections in the module.
vi
Module 16 (Optional): Using Microsoft ADO.NET to Access Data !
Accessing Data with DataReaders This section focuses on accessing data by using a DataReader. Point out to the students that, when they use a DataReader, the database connection is always open. When they are finished reading data, they must explicitly close the connection.
!
Binding to XML Data XML is fast emerging as the most popular language for exchanging data. This section provides students with information on how to read XML data by using ADO.NET. Most students will already know about XML documents. However, for students who are not familiar with XML, it will be useful to show an example of an XML document and how it is displayed on the client.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn about the data binding features in ASP.NET.
!
Overview of ADO.NET
!
Connecting to a Data Source
!
Accessing Data with DataSets
!
Using Stored Procedures
!
Accessing Data with DataReaders
!
Binding to XML Data
*****************************ILLEGAL FOR NON-TRAINER USE****************************** ADO.NET, offers a rich suite of data handling and data binding functions for manipulating all types of data. ADO.NET is an evolution of the ADO data access model that directly addresses user requirements for developing scalable applications. It was designed specifically for the Web with scalability, statelessness, and XML in mind. After completing this module, you will be able to: !
Describe the ADO.NET object model.
!
Connect to a data source by using ADO.NET.
!
Retrieve data from a database by using DataReaders and DataSets.
!
Display the data from a database on the client by DataGrid controls.
!
Use stored procedures.
!
Read data from an XML file into DataSets.
2
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
" Overview of ADO.NET Topic Objective
To introduce the topics included in this section.
Lead-in
ASP.NET offers a new means to retrieve data with the introduction of ADO.NET.
!
The ADO.NET Object Model
!
RecordSets vs. DataSets
!
Using Namespaces
*****************************ILLEGAL FOR NON-TRAINER USE****************************** ADO.NET is not a revision of Microsoft® ActiveX® Data Objects (ADO), but a new way to manipulate data that is based on disconnected data and XML. Although ADO is an important data access tool it is connected by default, relies on an OLE DB provider to access data, and it is entirely Component Object Model (COM)-based. ADO.NET has been designed to work with disconnected datasets. Disconnected datasets reduce network traffic. ADO.NET uses XML as the universal transmission format. This guarantees interoperability as long as the receiving component runs on a platform where an XML parser is available. When the transmission occurs through XML, it is no longer necessary that the receiver be a COM object. The receiving component has no architectural restrictions whatsoever. Any software component can share ADO.NET data, as long as it uses the same XML schema for the format of the transmitted data. In this section, you will learn about ADO.NET. You will learn about the new and modified objects in ADO.NET. You will also learn about some of the new namespaces.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
3
The ADO.NET Object Model Topic Objective
.ASPX Page
To describe the ADO.NET object model.
DataReader Command
Lead-in
ADO.NET includes some of the same objects as ADO (like Connection and Command), and introduces new objects, such as DataSets, DataReaders and DataAdapters.
Company: Company: Northwind NorthwindTraders Traders Database Connection
DataAdapter
DataView DataView
DataSet .ASPX Page
List-Bound List-Bound Control Control
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Point out that there are ADO and SQL Server versions of the Connection, Command, DataAdapter, and DataReader objects.
ADO.NET evolved from the ADO data access model. By using ADO.NET, you can develop applications that are robust and scalable, and that can use XML. ADO.NET has some of the same objects as ADO (like the Connection and Command objects), and introduces new objects, such as the Dataset, DataReader, and DataAdapter.
Connection Objects Connection objects are used to talk to databases. They have properties, such as DataSource, UserID, and Password, which are needed to access a particular DataSource. Commands travel over connections, and result sets are returned in the form of streams that can be read by DataReaders or pushed into DataSet objects. There are two kinds of connection objects in ADO.NET: SqlConnection and OleDbConnection.
Command Objects Command objects contain the information that is submitted to a database. A command can be a stored procedure call, an update statement, or a statement that returns results. You can also use input and output parameters and return values. In ADO.NET, you can use two kinds of command objects: SqlCommand and OleDbCommand.
4
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
DataReader Objects A DataReader is somewhat synonymous with a read-only/forward-only view of the data. The DataReader API supports flat as well as hierarchical data. A DataReader object is returned after executing a command against a database. It works similarly to a recordset in ADO; however the format of the returned DataReader object is different from a recordset. For example, you may use the DataReader to show the results of a search list in a Web page. ADO.NET includes two types of DataReader objects: the SqlDataReader for Microsoft SQL Server™ version 7.0 (or later) data, and the OleDbDataReader for ADO data. The DataReader object is database-specific. The behavior of the SqlDataReader may differ from the behavior of the OleDbDataReader and additional DataReader objects that are introduced in the future. You use the OleDbCommand and SqlCommand objects and the ExecuteReader method to transfer data into a DataReader.
DataSet Objects The DataSet object is similar to the ADO Recordset object, but more powerful, and with one other important distinction: the DataSet is always disconnected. The DataSet provides a rich object model to work with when passing data between various components of an enterprise solution. The DataSet object represents a cache of data, with database-like behavior. It contains tables, columns, relationships, constraints, and data. Data coming from a database, an XML file, code, or user input can be entered into DataSet objects and converted into files, forms, or databases. The behavior of a DataSet is completely consistent regardless of the underlying database, SQL Server or OLE DB. As changes are made to the DataSet, they are tracked in a way similar to the way changes are tracked in a word processing document. The DataSet object has a collection of DataTable objects. A DataTable represents one table of in-memory data. It contains a collection of columns that represents the table's schema. A DataTable also contains a collection of rows, representing the data contained in the table. You use the OleDbDataAdapter and SqlDataAdapter objects and the Fill method to get data into a DataSet.
DataView Objects A DataView enables you to create different views of the data stored in a DataTable, a capability that is often used in data-binding applications. By using a DataView, you can expose the data in a table with different sort orders, and you can filter the data by row state or based on a filter expression. A DataView provides a dynamic view of data whose content, ordering, and membership reflect changes to the underlying DataTable as they occur. This is different from the Select method of the DataTable, which returns a DataRow array from a table per a particular filter and/or sort order, and whose content reflects changes to the underlying table, but whose membership and ordering remain static. The dynamic capabilities of the DataView make it ideal for databinding applications.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
5
DataAdapter Object While the DataSet object provides a tool for in-memory data storage, you need another tool to create and initialize the various tables. This tool is the DataAdapter object. It represents a centralized console that hides the details of working with connections and commands. The DataAdapter object allows for the retrieval and saving of data between a DataSet object and the source data store. It is responsible for pulling out data from the physical store and pushing it into data tables and relations. The DataAdapter object is also responsible for transmitting any update, insertion, or deletion to the physical database. You can use four command objects to make any updates: UpdateCommand, InsertCommand, DeleteCommand, and SelectCommand. The DataAdapter object exists in two forms: SqlDataAdapter objects and OleDbDataAdapter objects. The data source is SQL Server for SqlDataAdapter objects and any other OLE DB provider for OleDbDataAdapter objects. The following illustration shows the use of a SQLDataAdapter object to transfer data between a SQL Server database and a DataSet object.
6
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Multimedia: Using ADO.NET to Access Data Topic Objective
To provide a high-level overview of how to access data by using ADO.NET.
Lead-in
In this animation, you will learn about how ADO.NET accesses data and how you can display that data in an ASP.NET page.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Tell students that they can view the animation again later for themselves by opening the 2349B_mod3.htm file from the Media folder. For details about how to run and describe the animation, see the Multimedia Presentation section in the Instructor Notes for this module.
In this animation, you will learn how to access data by using ADO.NET and how you can display that data in an ASP.NET page. To view the animation, open the file 2349B_16A001.htm file from the Media folder.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
7
Recordsets vs. DataSets Topic Objective
To highlight the differences between DataSets and Recordsets.
Lead-in
In ADO, you used Recordsets.
Feature
Recordset
DataSet
Number of tables
One table
Multiple tables
Relationships
Based on join
Includes relationships
Moving through data
Move row-by-row
Navigate via relationships
Data connections
Connected or disconnected
Disconnected
Transmitting data
COM marshalling
Transmit XML file
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In ADO, the in-memory representation of database data is the recordset. In ADO.NET, it is the DataSet. The DataSet contains a collection of tables and knowledge of relationships between those tables. Each table contains a collection of columns. These objects represent the schema of the DataSet. Each table can then have multiple rows, representing the data held by the DataSet. These rows track their original state along with their current state, so that the DataSet tracks what kinds of changes have occurred. Additionally, the DataSet provides persistence and de-persistence through XML. There are important differences between recordsets and DataSets, which are highlighted in the following table and detailed in the text that follows. Feature
Recordset
DataSet
Number of tables
One table
Multiple tables
Relationships
Based on join
Includes relationships
Moving through data
Move row-by-row
Navigate via relationships
Data connections
Connected or disconnected
Disconnected
Transmitting data
COM marshalling
Transmit XML file
Number of Tables An ADO recordset looks like a single table. If a recordset is to contain data from multiple database tables, it must use a JOIN query, which assembles the data from the various database tables into a single result table. In contrast, an ADO.NET DataSet is a collection of one or more tables. The tables within a data set are called data tables; specifically, they are DataTable objects.
8
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Relationships Typically, a DataSet also contains relationships. A relationship within a DataSet is analogous to a foreign-key relationship in a database. In ADO.NET, a DataRelation represents the relationship.
Moving Through Data In ADO.NET, the methods you use to read or modify data differ from the programming methods you use in ADO in the following ways: !
In ADO, you scan sequentially through the rows of the recordset.
!
In ADO.NET, rows are represented as collections, so you can loop through a table as you would through any collection or access particular rows through ordinal or primary key index. DataRelation objects maintain information about master and detail records and provide a method that allows you to get records related to the one you are working with. For example, starting from the row of the Investor table for "Jose Lugo", you can navigate to the set of rows of the Purchase table that describes the purchases made by Jose Lugo.
Data Connections In ADO.NET, the DataSet provides disconnected access to database data. In ADO, the recordset can provide disconnected access, but is typically used to provide connected access.
Transmitting Data To transmit an ADO disconnected recordset from one component to another, you use COM marshalling. To transmit an ADO.NET data set, you simply transmit an XML file. Because components exchange ADO.NET datasets by using XML, firewalls can allow datasets to pass.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
9
Using Namespaces Topic Objective
To describe the new namespaces used with ADO.NET.
!
Namespaces Used with ADO.NET Include:
Lead-in
In.NET, you must specify the namespace that you want to use.
#
System.Data
#
System.Data.OleDb
#
System.Data.SqlClient
#
System.Data.SqlTypes
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The Microsoft .NET Framework is an object-oriented system. When using specific parts of the framework, you must include references to the appropriate namespace. When using ADO.NET from either Microsoft Visual Basic® or Microsoft Visual C#™, you must reference the System.Data namespace, plus either the System.Data.OleDb or System.Data.SqlClient namespace, depending on the data source you choose to use. System.Data provides the code facilities, while System.Data.OleDb and System.Data.SqlClient are the namespaces for the two managed providers. The System.Data.Common namespace contains classes that are shared by the .NET data providers. The System.Data.SqlTypes namespace provides classes for native data types within SQL Server. These classes provide a safer, faster alternative to other data types. The following table summarizes the list of available namespaces with ADO.NET. Namespace
Contains
System.Data
Base objects and types for ADO.NET
System.Data.OleDb
Managed OLE DB data store objects
System.Data.SqlClient
SQL Server specific implementations of ADO.NET objects
System.Data.Common
Classes shared by the .NET data providers
System.Data.SqlTypes
SQL data types
10
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Connecting to a Data Source Topic Objective
To describe how to connect to a data source by using ADO.NET.
Lead-in
Connecting to a data source is the first step in data access.
!
Using SqlConnection
SqlConnection SqlConnection mySqlConnection mySqlConnection == new new SqlConnection("server=(local)\\NetSDK;! SqlConnection("server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind"); Trusted_Connection=yes;database=northwind"); !
Using OleDbConnection
OleDbConnection OleDbConnection myOleDbConnection myOleDbConnection == new new OleDbConnection("server=(local)\\NetSDK; OleDbConnection("server=(local)\\NetSDK; ! ! Trusted_Connection=yes;database=northwind; Trusted_Connection=yes;database=northwind; ! ! provider=sqloledb"); provider=sqloledb");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When you connect to a database, there are two routes that you can take: use OLE DB or use the native SQL provider. The native SQL provider is faster, but you must use Microsoft SQL Server as your database. If you are using Microsoft Access, Microsoft Excel, a comma-delimited file, or some other data source, you must use the OLE DB provider. You can use the OLE DB provider with a Microsoft SQL Server database; however, it is not as fast as using the native SQL provider. The Connection object defines how to connect to a specific data store. The .NET Framework provides two Connection objects: SqlConnection and OleDbConnection. The SqlConnection object defines how to connect to SQL Server databases and the OleDbConnection object allows you to establish a connection to a database through an OLE DB provider.
Using SqlConnection Delivery Tip
Point out to students that in ADO.NET you do not always need to explicitly open and close the connection to the database.
The following code illustrates how to create and open a connection to a Microsoft SQL Server database by using the SqlConnection object. SqlConnection mySqlConnection = new SqlConnection("server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind");
Warning Embedded usernames/passwords in database connection strings are inherently unsecure, and should only be used on platforms, such as Microsoft Windows® 9x, that do not support Integrated Security. For more information about Integrated Security, see the .NET Framework SDK documentation.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
11
Using OleDbConnection For the OLE DB Managed Provider, the connection string format is quite similar to the connection string format used in OLE DB. The following code illustrates how to create and open a connection to a Microsoft SQL Server database by using OleDbConnection. OleDbConnection myOleDbConnection = new OleDbConnection("server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind;! provider=sqloledb");
Delivery Tip
Stress to students that all examples in the module and lab will use SqlConnection objects.
The examples in this module, use SqlConnection objects. Implementation is slightly different for using OleDbConnection objects. For more information about using OleDbConnection objects, search for OleDbConnection in the Microsoft .NET Framework SDK documentation.
Choosing a .NET Data Provider Depending on the design and data source for your application, your choice of .NET data provider can improve the performance, capability, and integrity of your application. The following table discusses the advantages and limitations of each .NET data provider. Provider
Notes
SQL Server .NET Data Provider
Recommended for middle-tier applications that use Microsoft SQL Server 7.0 or later. Recommended for single-tier applications that use Microsoft Data Engine (MSDE) or Microsoft SQL Server 7.0 or later. Recommended over use of the OLE DB Provider for SQL Server (SQLOLEDB) with the OLE DB .NET Data Provider. For Microsoft SQL Server version 6.5 and earlier, you must use the OLE DB Provider for SQL Server with the OLE DB .NET Data Provider.
OLE DB .NET Data Provider
Recommended for middle-tier applications that use Microsoft SQL Server 6.5 or earlier, or any OLE DB provider that supports the OLE DB interfaces listed in OLE DB Interfaces Used by the OLE DB .NET Data Provider (OLE DB 2.5 interfaces are not required). For Microsoft SQL Server 7.0 or later, the SQL Server .NET Data Provider is recommended. Recommended for single-tier applications that use Microsoft Access databases. Use of a Microsoft Access database for a middle-tier application is not recommended. Support for the OLE DB Provider for ODBC (MSDASQL) is disabled. For access to Open Database Connectivity (ODBC) data sources, an ODBC .NET Data Provider is available as a separate download at http://msdn.microsoft.com/downloads.
12
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
" Accessing Data with DataSets Topic Objective
To introduce the topics included in the section.
Lead-in
After connecting to a database, the next step is to access data from it.
!
Using DataSets to Read Data
!
Storing Multiple Tables in a DataSet
!
Using DataViews
!
Updating a Database from a DataSet
!
Displaying Data in the DataGrid Control
*****************************ILLEGAL FOR NON-TRAINER USE****************************** ADO.NET provides two ways to access data, DataSets and DataReaders. In this section, you will learn how to access data by using DataSets. You will also learn about DataViews and displaying data in DataGrid controls.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
13
Using DataSets to Read Data Topic Objective
To describe how to retrieve data from a database by using DataSets.
Lead-in
Now that we can establish a connection, we need to be able to execute statements against the database to retrieve data.
!
Create the Database Connection
!
Store the Query in a SqlDataAdapter
SqlDataAdapter SqlDataAdapter mySqlDataAdapter mySqlDataAdapter == new new SqlDataAdapter( SqlDataAdapter( "select "select ** from from customers", customers", mySqlConnection); mySqlConnection); !
Create and Populate the DataSet with DataTables
DataSet DataSet myDataSet myDataSet == new new DataSet(); DataSet(); mySqlDataAdapter.Fill(myDataSet,"Customers"); mySqlDataAdapter.Fill(myDataSet,"Customers");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Explain the parameters to the Fill method. The second parameter is the name that will be given to the DataTable created in the DataSet. You use this name when reading data from the DataSet.
After you establish a connection to a database, you can access its data. ADO.NET provides multiple ways to access data.
Using DataSets The DataSet object is the centerpiece of ADO.NET. It represents a complete set of data, including multiple, related tables, and constraints. Although a DataSet stores data, you need DataAdapter objects to create and initialize the various tables. You also need the Fill method to populate a DataSet with the results from a query. The Fill method takes two parameters: a DataSet instance and a string. The DataSet instance represents the DataSet to be filled, and the string identifies the DataTable that will be created inside the DataSet. A DataSet can contain many DataTables. You use the string supplied to the Fill method to reference the DataTable after it is created.
14
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
The following code example illustrates how to create a SqlDataAdapter object that contains the query statement. The Fill method then populates the DataSet with the results from the query. // Create a connection SqlConnection mySqlConnection = new SqlConnection("server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind"); // Create the DataAdapter SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter( "select * from customers", mySqlConnection); // Create and populate the DataSet DataSet myDataSet = new DataSet(); mySqlDataAdapter.Fill(myDataSet,"Customers");
Displaying Data In A DataSet Because the data is stored in a collection of rows in the table, you can easily use a foreach statement to iterate through the rows: foreach (DataRow myDataRow in myDataSet.Tables["Customers"].Rows) { Console.WriteLine( " CustomerID: {0}", myDataRow["CustomerID"].ToString); }
Typed DataSet Along with late bound access to values through weakly typed variables, the DataSet provides access to data through a strongly typed metaphor. By using user-friendly names and strongly typed variables, you can access tables and columns that are part of the DataSet. You can also transport a strongly typed DataSet by using an XML Web service. A typed DataSet is a class that derives from a DataSet. As such, it inherits all of the methods, events, and properties of a DataSet. Additionally, a typed DataSet provides strongly typed methods, events, and properties. This means that you can access tables and columns by name, instead of using collectionbased methods. Aside from the improved readability of the code, a typed DataSet also allows the Microsoft Visual Studio® .NET code editor to automatically complete lines as you type. Additionally, a strongly typed DataSet provides access to the correct types for values at compile time. With a strongly typed DataSet, type mismatch errors are caught when the code is compiled rather than at run time. Using an XML Schema that complies with the XML Schema definition language (XSD) standard, you can generate a strongly typed DataSet by using the XSD.exe tool that is provided with the .NET Framework SDK. The use of this tool is outside the scope of this module. You will see how to easily create and use a typed DataSet using Visual Studio .NET in this module’s lab.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
15
Storing Multiple Tables in a DataSet Topic Objective
To explain how to retrieve and store multiple tables in a DataSet.
Lead-in
Unlike a disconnected recordset, a DataSet can hold more than one table.
!
Add the First Table
SqlDataAdapter SqlDataAdapter mySqlDataAdapter mySqlDataAdapter == new new SqlDataAdapter( SqlDataAdapter( "select "select ** from from customers",mySqlConnection); customers",mySqlConnection); DataSet DataSet myDataSet myDataSet == new new DataSet(); DataSet(); mySqlDataAdapter.Fill(myDataSet,"Customers"); mySqlDataAdapter.Fill(myDataSet,"Customers"); !
Add the Subsequent Table(s)
mySqlDataAdapter.SelectCommand.CommandText mySqlDataAdapter.SelectCommand.CommandText == "select "select ** from from orders"; orders"; mySqlDataAdapter.Fill(myDataSet,"Orders"); mySqlDataAdapter.Fill(myDataSet,"Orders"); Customers
DataSet:
Orders
Data Tables
*****************************ILLEGAL FOR NON-TRAINER USE****************************** A DataSet can contain multiple tables. You can retrieve multiple tables from a database and store them in a DataSet. Note You can store tables from different databases in the same DataSet. Delivery Tip
Tell students that they can create new relationships between the tables in a DataSet.
! To retrieve and store multiple tables in a DataSet 1. Create and populate the first DataSet. DataSet myDataSet = new DataSet(); string strSql ="select * from customers"; SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter( strSql, mySqlConnection); mySqlDataAdapter.Fill(myDataSet,"Customers");
2. Reset the SelectCommand, InsertCommand, or DeleteCommand property of the DataAdapter object to a new command string. strSql = "select * from orders"; mySqlDataAdapter.SelectCommand.CommandText = strSql;
3. Call Fill again. mySqlDataAdapter.Fill(myDataSet,"Orders");
16
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
The following code shows how you can add two tables from two different queries, one for authors and the other for titles, to the same DataSet. // create connection to database string strConn = "server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind"; SqlConnection mySqlConnection = new SqlConnection(strConn); string strSql = "select * from customers" SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(strSql, mySqlConnection); //fill DataSet with first set of data DataSet myDataSet = new DataSet(); mySqlDataAdapter.Fill(myDataSet,"Customers"); //fill DataSet with first set of data strSql = "select * from orders" mySqlDataAdapter.SelectCommand.CommandText = strSql; mySqlDataAdapter.Fill(myDataSet,"Orders");
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
17
Using DataViews Topic Objective
To explain the role of DataViews in accessing data from a database by using DataSets.
!
DataViews Can be Customized to Present a Subset of Data from a DataTable
Lead-in
!
The DefaultView Property Returns the Default DataView for the Table
To display data in a DataSet, you can use a DataView.
DataView DataView myDataView myDataView == myDataSet.Tables["Customers"].DefaultView; myDataSet.Tables["Customers"].DefaultView; !
Setting Up Different Views
myDataView.Sort myDataView.Sort == "Country"; "Country"; myDataView.RowFilter myDataView.RowFilter == "Country "Country == 'Argentina'"; 'Argentina'";
*****************************ILLEGAL FOR NON-TRAINER USE****************************** There are many ways to filter data. One way is to filter data at the database command level, by using a where clause in your query. A second way is to filter the data after it is in the DataSet. This topic covers filtering in the DataSet.
Filtering and Sorting with DataViews To display the data held in a DataSet, you can use a DataView. DataViews can be customized to present a subset of data from the DataTable. This capability allows you to have two controls bound to the same DataTable, but showing different versions of the data. For example, one control may be bound to a DataView that shows all the rows in the table, and a second may be configured to display only the rows that have been deleted from the DataTable. Each DataTable in a DataSet has a DefaultView property, which returns the default view for the table. You can access the default DataView of a DataSet as follows: DataView myDataView = myDataSet.Tables["Customers"].DefaultView;
Note The DataSet object contains a Tables collection. You reference the DataTable you are interested in by name. You can sort the data. For example, you can sort the customers by country. myDataView.Sort = "Country";
18
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Setting up a Different View of a DataSet You can also create a view of a subset of the data in a DataTable. For example, you can set the RowFilter property on a DataView to retrieve only customers from Argentina. myDataView.RowFilter = "Country = 'Argentina'";
For more information about the properties of the DataView object, see the Microsoft .NET Framework SDK documentation.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
19
Updating a Database From a DataSet Topic Objective
To show how to update data in a Database from a DataSet.
Lead-in
This topic illustrates how to update data in a database using a DataSet. You can also insert, update, and delete data in a database directly using a SqlCommand as described in the .NET Framework SDK.
!
SQLCommandBuilder generates Update command
SqlCommandBuilder SqlCommandBuilder mySqlCommandBuilder mySqlCommandBuilder == new new SqlCommandBuilder(mySqlDataAdapter); SqlCommandBuilder(mySqlDataAdapter); !
Set the MissingSchemaAction property
mySqlDataAdapter.MissingSchemaAction mySqlDataAdapter.MissingSchemaAction == MissingSchemaAction.AddWithKey; MissingSchemaAction.AddWithKey; !
Add a row
DataRow DataRow myDataRow myDataRow == myDataSet.Tables["Customers"].NewRow(); myDataSet.Tables["Customers"].NewRow(); myDataRow["CustomerId"] myDataRow["CustomerId"] == "NewID"; "NewID"; // // ... ... myDataSet.Tables["Customers"].Rows.Add(myDataRow); myDataSet.Tables["Customers"].Rows.Add(myDataRow); !
To submit the data
mySqlDataAdapter.Update(myDataSet, mySqlDataAdapter.Update(myDataSet, "Customers"); "Customers");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This topic illustrates how to update data in a database by using a DataSet. You can also insert, update, and delete data in a database directly by using a SqlCommand as described in the .NET Framework SDK documentation. After a DataSet is loaded, you can modify the data, and the DataSet will track the changes. The DataSet may be considered an in-memory cache of data that is retrieved from a database. In this topic, you will see how to use the Add method on the DataTable to add new data to a DataSet. The Add method takes either an array of the expected data columns, or a DataRow. To load the DataSet from the database: SqlConnection myConnection = new SqlConnection( "server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter( "Select * from Customers", myConnection); DataSet myDataSet = new DataSet();
Before you can submit the update back to the database, you must set up the InsertCommand, UpdateCommand, and DeleteCommand to reconcile the changes to the database. For limited scenarios you can use the SqlCommandBuilder to automatically generate those for you: SqlCommandBuilder mySqlCommandBuilder = new SqlCommandBuilder(mySqlDataAdapter);
20
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Because Fill will not cause primary key and unique key information to be retrieved unless AddWithKey is specified, you must set the MissingSchemaAction property to AddWithKey: mySqlDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; mySqlDataAdapter.Fill(myDataSet, "Customers"); DataRow myDataRow; myDataRow = myDataSet.Tables["Customers"].NewRow();
The DataTable must return a DataRow through the NewRow method. The method returns a DataRow object with the appropriate schema of the DataTable. The new DataRow is independent of the table until it is added to the RowsCollection. myDataRow["CustomerId"] = "NewID"; myDataRow["ContactName"] = "New Name"; myDataRow["CompanyName"] = "New Company Name"; myDataSet.Tables["Customers"].Rows.Add(myDataRow);
To submit the data from the DataSet into the database, use the Update method on the SqlDataAdapter. mySqlDataAdapter.Update(myDataSet, "Customers");
You can change data in a DataRow by accessing the DataRow. You can use the index of the row in the RowsCollection that is accessed through the Rows property: myDataSet.Tables["Customers"].Rows[0]["ContactName"]="Peach";
You can also access a specific row by the primary key value: DataRow myDataRow1 = myDataSet.Tables["Customers"].Rows.Find("ALFKI"); myDataRow1["ContactName"]="Peach";
In the preceding example, "ALFKI" is the value of the primary key "CustomerID" in the Customers table. When using the SqlDataAdapter, the key is established from the database. You can also set the key through the PrimaryKey property if you are not using the database. Use the Delete method to remove the Row. Note that a logical deletion occurs in the DataSet, which only results in a hard deletion after the DataSet is updated to the database. Similarly, you can use RejectChanges on the DataSet, in which case the Row is restored. myDataSet.Tables["Customers"].Rows[0].Delete();
The original and new values are maintained in the row. The RowChanging event allows you to access both original and new values to decide whether you want the edit to proceed. Because original and new values are maintained, you can establish scenarios such as optimistic locking and key changes.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
21
Demonstration: Accessing Data with DataSets Topic Objective
To demonstrate how to open a database, create a DataSet with multiple tables, and create views on the data.
Lead-in
In this demonstration, you will see how to access data with a DataSet.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this demonstration, you will see how to access data with a DataSet. Run the Visual Studio .NET Accessing Data with DataSets project from the following location: \Democode\Mod16\Demo16.1 The code reads in data from database tables, creates views, and updates the database. Tip You can examine the database tables and stored procedures by using the Server Explorer window Data Connections entries.
22
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Running this program produces output that is similar to the following: Opened Connection to! server=(local)\NetSDK;! Trusted_Connection=yes;database=northwind Total Number of Regions: 4 Total Number of Customers: 91 Regions: ID: 1 Description: ID: 2 Description: ID: 3 Description: ID: 4 Description:
Eastern Western Northern Southern
First 5 Customer IDs: Customer ID: ALFKI Customer ID: ANATR Customer ID: ANTON Customer ID: AROUT Customer ID: BERGS View sorted by country First 5 Customers in the view: Customer ID: CACTU Country: Customer ID: OCEAN Country: Customer ID: RANCH Country: Customer ID: ERNSH Country: Customer ID: PICCO Country:
Argentina Argentina Argentina Austria Austria
Filtered by country equals Argentina Customers with Country = Argentina, number of entries 3 ID: CACTU Country: Argentina ID: OCEAN Country: Argentina ID: RANCH Country: Argentina
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
23
Displaying Data in the DataGrid Control Topic Objective
To describe how to display data on the client DataGrid controls.
Lead-in
After you connect to a data source and retrieve data from it, the next step is to display the data on the client.
!
Create a Windows Forms DataGrid control
dataGrid1 dataGrid1 == new new System.Windows.Forms.DataGrid(); System.Windows.Forms.DataGrid(); !
Bind to a DataSet dataGrid1.DataSource dataGrid1.DataSource dataGrid1.DataMember dataGrid1.DataMember
!
== ==
myDataSet; myDataSet; "Regions"; "Regions";
Using a custom view
DataView DataView myDataView myDataView == myDataSet.Tables["Customers"].DefaultView; myDataSet.Tables["Customers"].DefaultView; myDataView.RowFilter myDataView.RowFilter == "Country "Country == 'Argentina'"; 'Argentina'"; dataGrid2.DataSource dataGrid2.DataSource == myDataView; myDataView;
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Displaying data from a data source is extremely simple and flexible in either Windows Forms or ASP.NET. These application development environments include a set of controls that perform the function of displaying data. You only need to bind these controls to a data source. To display data on the client, you can use any list-bound control, such as the DataGrid.
Using the DataGrid Control The DataGrid control is designed to produce output that resembles a worksheet. In Visual Studio .NET, you can drag and drop a DataGrid from the Toolbox onto the Design window. This generates code to create a DataGrid object as follows: dataGrid1 = new System.Windows.Forms.DataGrid();
To bind a DataSet to a DataGrid control, you first set the DataSource property of the DataGrid to a DataTable, or DataVieweither the DefaultView property of a DataSet or a custom DataView object. To specify a DataTable, set the DataMember property of the DataGrid to the name of the DataTable.
24
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Example Binding to a DataSet dataGrid1.DataSource = myDataSet; dataGrid1.DataMember = "Regions";
Alternatively, you can use the Tables collection of the DataSet to assign the DataTable directly to the DataSource of the DataGrid, as in the following example: dataGrid1.DataSource = myDataSet.Tables["Regions"];
If you want to display a different view of data in the DataGrid control, create a new DataView object from the DataSet and bind that to the control. For example, the following code will display only those customers in Argentina.
Example Using a Custom View DataView myDataView = myDataSet.Tables["Customers"].DefaultView; myDataView.RowFilter = "Country = 'Argentina'"; dataGrid2.DataSource = myDataView;
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
25
Demonstration: Displaying Data in a DataGrid Topic Objective
To introduce the Data Grid.
Lead-in
In this demonstration, you will see how to create a Windows Form that reads data from a database and displays it in a DataGrid control.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this demonstration, you will see how to read data from a database into a DataSet and then display it in a DataGrid control. Run the Visual Studio .NET DataGrid project from the following location: \Democode\Mod16\Demo16.2 Running this program displays a button labeled “Load Data” and two DataGrid controls. Clicking the button reads region and customer data from a database and displays them in the grids. A DataView object is used to display only those customers in Argentina.
26
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
" Using Stored Procedures Topic Objective
To introduce the topics in this section.
Lead-in
When working with data from a data source, the ability to use stored procedures is important.
!
Calling Stored Procedures
!
Passing Parameters
!
Calling Action Stored Procedures
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In the past, data processing has been primarily connection-based. Now, in an effort to make multi-tiered applications more efficient, data processing is turning to a message-based approach that revolves around chunks of information. In ADO.NET, this is accomplished by the DataAdapter object, which provides a bridge to retrieve and save data between a DataSet object and its source data store. The DataAdapter object accomplishes this by invoking the appropriate SQL commands against the data store. Both the SqlDataAdapter and the OleDbDataAdapter classes feature four command objects, InsertCommand, UpdateCommand, DeleteCommand, and SelectCommand. These objects provide the create, update, delete, and read functions for a specific DataTable in the DataSet. These command objects are used when you want to perform a number of updates at the same time. Instead of having one stored procedure to do it all, you can put SQL Statements in each object and call the Update method. For more information about these command objects, see the .NET Framework SDK documentation. However, the easier and more commonly used method of updating data in a database is to use stored procedures. You can use stored procedures to read and modify data from a database. You can call stored procedures both from DataAdapter and Command objects. In this section, you will learn how to call stored procedures, pass parameterized stored procedures, and call action stored procedures.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
27
Calling Stored Procedures Topic Objective
To explain how to use stored procedures to retrieve data in a database.
Lead-in
Like ADO, ADO.NET allows developers to use stored procedures to modify data.
!
Stored Procedures Provide Security for Database
!
Set Up the DataAdapter
SqlDataAdapter SqlDataAdapter mySqlDataAdapter mySqlDataAdapter == new new SqlDataAdapter(); SqlDataAdapter(); mySqlDataAdapter.SelectCommand mySqlDataAdapter.SelectCommand == new new SqlCommand(); SqlCommand(); mySqlDataAdapter.SelectCommand.Connection mySqlDataAdapter.SelectCommand.Connection == mySqlConnection; mySqlConnection; mySqlDataAdapter.SelectCommand.CommandText mySqlDataAdapter.SelectCommand.CommandText == "GetProducts"; "GetProducts"; mySqlDataAdapter.SelectCommand.CommandType mySqlDataAdapter.SelectCommand.CommandType == CommandType.StoredProcedure; CommandType.StoredProcedure; !
Run the Stored Procedure and Store Returned Records
mySqlDataAdapter.Fill(myDataSet,"Products"); mySqlDataAdapter.Fill(myDataSet,"Products");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** A stored procedure is a sequence of Transact-SQL (T-SQL) statements stored on the database server. Stored procedures provide a level of security to a database. The database designer can create stored procedures to retrieve and modify data, and not allow developers access to the actual tables of the database. In this way, the database designer can then change the structure of the database without breaking applications that use it. Though it is not always the case, stored procedures can return a set of records. Stored procedures can encapsulate repetitive tasks and execute them efficiently. When you call a stored procedure that returns a set of records, use a DataAdapter and the Fill method. When you call a stored procedure that performs some function on the database but does not return a set of records, use a Command object and the ExecuteNonQuery method.
28
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Calling a Select Stored Procedure Using stored procedures in ADO.NET is similar to ADO. You create a Command object and point it to the database connection. Next, you set the CommandText property to the name of the stored procedure and the CommandType property to CommandType.StoredProcedure. The following is the GetProducts stored procedure. It returns a list of ProductNames. PROCEDURE GetProducts as select ProductName from Products
Using a Connection object named mySqlConnection, the following example shows how to setup the SqlDataAdpator for the GetProducts stored procedure: SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(); mySqlDataAdapter.SelectCommand = new SqlCommand(); mySqlDataAdapter.SelectCommand.Connection = mySqlConnection; mySqlDataAdapter.SelectCommand.CommandText = "GetProducts"; mySqlDataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
Note You can directly set the connection and command text when creating the SqlDataAdapter object: SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter( "GetProducts", mySqlConnection);
You then set the CommandType property before you call the Fill method. mySqlDataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
To execute the stored procedure, call the Fill method of the mySqlDataAdapter object. This fills a DataTable object with the returned records of the stored procedure. DataSet myDataSet = new DataSet(); mySqlDataAdapter.Fill(myDataSet,"Products");
After you have filled a DataTable with the results of a Select stored procedure, you can bind it to a list-bound control to display the data. For example, to bind to a Windows Forms DataGrid control named dataGrid1: dataGrid1.DataSource = myDataSet.Tables["Products"];
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
29
Passing Parameters Topic Objective
To show students how to use parameterized stored procedures.
Lead-in
When using Microsoft SQL Server, or other procedure-based databases, you can use parameters to pass and retrieve information from the database.
!
Create Parameter, Set Direction and Value, Add to the Parameters Collection
SqlParameter SqlParameter workParam workParam == new new SqlParameter("@ProductName",SqlDbType.NChar, SqlParameter("@ProductName",SqlDbType.NChar, 40); 40); workParam.Direction workParam.Direction == ParameterDirection.Input; ParameterDirection.Input; // // Input Input is is default default workParam.Value = textBox1.Text; workParam.Value = textBox1.Text; mySqlDataAdapter.SelectCommand.Parameters.Add( mySqlDataAdapter.SelectCommand.Parameters.Add( workParam); workParam); !
Run Stored Procedure
DataSet DataSet myDataSet myDataSet == new new DataSet(); DataSet(); mySqlDataAdapter.Fill(myDataSet,"ProductData"); mySqlDataAdapter.Fill(myDataSet,"ProductData");
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Stress that, when you create a parameter, you must use the same name that was used in the stored procedure. Also, stress that the order in which you create parameters does not matter.
When using Microsoft SQL Server, or other procedure-based databases, parameters can be used to pass and retrieve information from the database. Using parameters in ADO.NET works just as it does in ADO. You can pass the string in the command or use the parameters collection. When using parameters, the names of the parameters added to the parameters collection of the command must match the names of the parameter markers in the stored procedure.
Parameters Your application can pass specific data to a stored procedure by using parameters. The following table describes the types of parameters that are available to you. Direction
Use
Input
Used by your application to send specific data values to a stored procedure.
Output
Used by a stored procedure to send specific values back to the calling application.
InputOutput
Used by a stored procedure to both retrieve information sent by your application and to send specific values back to the application.
ReturnValue
Used by a stored procedure to send a return value back to the calling application.
30
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Creating a Parameter To create a parameter for the SqlDataAdapter object, create a new SqlParameter object with the name and data type of the parameter. Next, set the Direction property of the new parameter to indicate how the parameter is used by the stored procedure. If the parameter is an input parameter, set the Value property to specify the data that should be sent to the server. For example, the GetProductData stored procedure takes one input parameter: PROCEDURE GetProductData ! @ProductName nchar(40) as select * from Products ! where @ProductName = ProductName
To call the GetProductData stored procedure, create an input parameter named @ProductName and set its value to the Text property of a text box named textBox1. SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(); mySqlDataAdapter.SelectCommand = new SqlCommand(); mySqlDataAdapter.SelectCommand.Connection = mySqlConnection; mySqlDataAdapter.SelectCommand.CommandText = "GetProductData"; mySqlDataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure; SqlParameter workParam = new SqlParameter("@ProductName", SqlDbType.NChar, 40); // ParameterDirection.Input is the default for the Direction // property. Thus the following line is not needed here. // To set the Direction property to its default value, // use the following line. // workParam.Direction = ParameterDirection.Input; workParam.Value = textBox1.Text;
After you have created the parameter, use the Add method of the Parameters collection of the SelectCommand object. The Add method takes a SqlParameter as an argument. If a stored procedure has more than one parameter, it does not matter in which order you add them because you create them by name. mySqlDataAdapter.SelectCommand.Parameters.Add(workParam);
Use the Fill method to run the stored procedure and retrieve the records. DataSet myDataSet = new DataSet(); mySqlDataAdapter.Fill(myDataSet,"ProductData"); // bind to a DataGrid dataGrid.DataSource = myDataSet.Tables["ProductData"];
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
31
Calling Action Stored Procedures Topic Objective
To show how to call a stored procedure that does not return records.
Lead-in
When calling a stored procedure that does not return records, you use the SqlCommand directly and call the ExecuteNonQuery method to run it.
!
Use SQLCommand object
SqlCommand SqlCommand mySqlCommand mySqlCommand == new new SqlCommand("GetProductCount", SqlCommand("GetProductCount", mySqlConnection); mySqlConnection); mySqlCommand.CommandType mySqlCommand.CommandType == CommandType.StoredProcedure; CommandType.StoredProcedure; SqlParameter SqlParameter workParam workParam == new new SqlParameter("@ProductCount", SqlParameter("@ProductCount", SqlDbType.Int); SqlDbType.Int); workParam.Direction workParam.Direction == ParameterDirection.Output; ParameterDirection.Output; mySqlCommand.Parameters.Add(workParam); mySqlCommand.Parameters.Add(workParam); !
Call the ExecuteNonQuery Method
mySqlCommand.ExecuteNonQuery(); mySqlCommand.ExecuteNonQuery(); !
Retrieve Output Parameters
textBox2.Text textBox2.Text == mySqlCommand.Parameters["@ProductCount"].! mySqlCommand.Parameters["@ProductCount"].! Value.ToString(); Value.ToString();
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When calling a stored procedure that does not return records, you use the SqlCommand directly and call the ExecuteNonQuery method to run it. The GetProductCount stored procedure returns the number of products: PROCEDURE GetProductCount @ProductCount int out as ! select @ProductCount=COUNT(ProductID) from Products
Because this stored procedure does not return a set of records, you do not need to use a DataAdapter object. Instead, you can use a Command object directly, and call the ExecuteNonQuery method to run the stored procedure. To call this stored procedure, create an output parameter named @ProductCount, add it to the Parameters collection of a Command object, and then call ExecuteNonQuery to run the stored procedure: SqlCommand mySqlCommand = new SqlCommand("GetProductCount", mySqlConnection); mySqlCommand.CommandType = CommandType.StoredProcedure; SqlParameter workParam = new SqlParameter("@ProductCount", SqlDbType.Int); workParam.Direction = ParameterDirection.Output; mySqlCommand.Parameters.Add(workParam); // execute the stored procedure mySqlCommand.ExecuteNonQuery();
32
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Retrieving an Output Parameter Delivery Tip
If a stored procedure returns records and an output parameter, retrieve the records in the usual way by calling the Fill method.
If you need to retrieve a value from a stored procedure that returns a value or sets an output parameter, use the Parameters collection to find the value of the output parameter. You can reference the value of the output parameter by name or index. The following example code retrieves the value of the @ProductCount output parameter by name and assigns it to a text box named textBox2: textBox2.Text = mySqlCommand.Parameters["@ProductCount"].Value.ToString();
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
33
Demonstration: Calling Stored Procedures Topic Objective
To introduce the demonstration.
Lead-in
In this demonstration, you will see how to call stored procedures both with and without parameters.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this demonstration, you will see how to create and call stored procedures with and without parameters. Run the Visual Studio .NET StoredProcedure project from the following location: \Democode\Mod16\Demo16.3 Tip You can examine the database tables and stored procedures by using the Server Explorer window Data Connections entries. To create and execute a stored procedure that returns the product names of all the entries in the Products table, click the Load Product Names button. Typing a name in the text box that is labeled Enter Product Name allows you to click on the Load Product Data button. This button creates and executes a stored procedure that takes the product name as an input parameter and returns all the fields of that product. To create and execute a stored procedure that has an output parameter, which gets set to the number of products in the database’s Products table, click the Get Number of Products button.
34
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Lab 16: Using ADO.NET to Access Data Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create an XML Web service and a Windows form that reads and updates database records by using ADO.NET.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Explain the lab objectives.
Objectives After completing this lab, you will be able to: !
Use the rapid application design facilities of Visual Studio .NET to create an XML Web service whose methods can be used to read and update a database by using ADO.NET.
!
Use Visual Studio .NET rapid application design facilitates to create a Windows Forms application that displays and enables a user to update a DataGrid control that is bound to a DataSet. The DataSet is obtained and updated by using an XML Web service.
Lab Setup Only solution files are associated with this lab. The solution files for this lab are in the folder \Labs\Lab16\Solution.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
35
Scenario This lab is based on the Visual Studio .NET Framework SDK Walkthrough: Creating a Distributed Application. In this lab, you will create a multi-tiered, distributed application. The application consists of three logical tiers: data, business object, and user interface. The data tier is a SQL Server database. The business object tier handles the tasks of accessing the data and distributing it to the clients. The user interface tier consists of a Windows-based application. You will create the Authors XML Web service and the Windows Client Application. The following diagram shows the architecture of the distributed application:
As part of the distributed application, you will build a simple data application with look-up and update functionality. You will create an XML Web service to retrieve data from the Authors table in the SQL Server Pubs sample database. You will also build a Windows-based client application to display the results of the query by the XML Web service. The communication between the client and the XML Web service is handled by using HTTP and XML. For this lab, the data has already been generated and is available in the Pubs sample database. Therefore, you will begin by creating the business object, the XML Web service, followed by building the Windows Form user interface.
Estimated time to complete this lab: 60 minutes
36
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Exercise 1 Creating the Middle-Tier Business Object In this exercise, you will create a business object that runs on a Web server. You will implement the business object as an XML Web service that holds the data connections and dataset definition. After first creating an ASP.NET Web Service project in Visual Studio .NET, you will use the Visual Studio .NET design environment to add a data connection, a data adapter, and a dataset class definition to the XML Web service. You will add two methods to the XML Web service: GetAuthors, which returns a dataset from the database, and UpdateAuthors, which updates the database with changes from the user.
! Create a ASP.NET Web Service project 1. Open Visual Studio .Net and on the File menu, click New, and then click Project to display the New Project dialog box. 2. In the Project Types pane, click Visual C# Projects, and then select ASP.NET Web Service in the Templates pane. 3. In the Location box, type the name of the Web server, http://localhost/AuthorsWebService. The dimmed Name box will now contain the text: AuthorsWebService. Click OK. 4. The AuthorsWebService project is added to the solution. The Component Designer for Service1.asmx appears in the development environment. In this component, you will create a connection to the data store and obtain an instance of the data by using a dataset. In Solution Explorer, double-click Service1.asmx. 5. In the Properties window, set the Name property of Service1 to AuthorsService. 6. In Solution Explorer, right-click the Service1.asmx file, click Rename, and rename the file AuthorsService.asmx, to match the service name.
! Create a database connection and data adapter 1. If Server Explorer is not visible, on the View menu, click Server Explorer. 2. In Server Explorer, right-click the Data Connections node and on the shortcut menu, click Add Connection. 3. In the Connection tab of the Data Link Properties dialog box, type the name of the SQL Server where the pubs database is installed: (local)\NETSDK Select Use Windows NT Integrated security for the logon information. 4. Select the pubs database from the list. Click Test Connection to validate the information that you provided, and then click OK to establish the connection. A new node appears in the Data Connections node of Server Explorer. 5. In Server Explorer, expand the node for the new connection node and then expand the Tables node.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
37
6. Find the authors node and expand it to show the fields in the authors table. 7. Using CTRL+Click, select the au_id, au_lname, au_fname, and city fields. 8. Drag these fields from Server Explorer onto the design surface. A SqlConnection that is paired with a SqlDataAdapter appears in the designer. A connection has now been created to the database, with the transfer of information to be handled by the SqlDataAdapter. These components are configured to move a dataset with the selected fields of the authors table in and out of the database.
! Configure Integrated Windows authentication 1. Start the Internet Information Services tool. It can be run from the Start button, Control Panel, Performance and Maintenance, Administrative Tools. 2. Expand the node for your server. 3. Expand the Default Web Site node. 4. Right-click the node for AuthorsWebService and click Properties. 5. Click the Directory Security tab. 6. Click the Edit button in the Anonymous access and authentication control section, click the Edit button. 7. Clear the Anonymous access check box. 8. Select the Integrated Windows authentication check box. Click OK to return to the AuthorsWebService Properties and click OK again. You have now configured your XML Web service directory. 9. Returning to the project in Visual Studio, in Solution Explorer, doubleclick the Web.config file. 10. Add the following tag on the line after the <system.web> tag to configure integrated security for your XML Web service.
! Create a dataset class definition 1. In Solution Explorer, double-click the AuthorsService.asmx file to open it in the designer. 2. On the Data menu, click Generate DataSet. In the Generate Dataset dialog box, select New and name the dataset authors1. Do not select the Add this dataset to the designer check box. Click OK. Note A dataset schema file, authors1.xsd, is created and added to the project. This schema file contains a class definition for authors1. This class, which inherits from DataSet, contains a typed dataset definition for the authors table. 3. On the File menu, click Save All.
38
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
! Add methods to the XML Web service 1. In Solution Explorer, double-click AuthorsService.asmx, if it is not already open in the designer. 2. On the View menu, click Code. 3. Add a method named GetAuthors to deliver a dataset to the client. This method creates a new authors1 dataset, and fills it by using the SqlDataAdapter that is based on the authors table. The method then returns the dataset: The following code shows the method: [WebMethod] public authors1 GetAuthors() { authors1 authors = new authors1(); sqlDataAdapter1.Fill(authors); return authors; }
4. Add a method named UpdateAuthors to propagate changes from the client back to the database. This method has an authors1 dataset parameter (authorChanges) that contains the changed data and updates the database through the SqlDataAdapter.Update method. The Update method accepts the changes in the dataset. The dataset is returned to the client. The client then uses this returned dataset to update its own instance of the authors1 dataset. The following code shows the method: [WebMethod] public authors1 UpdateAuthors(authors1 authorChanges) { if (authorChanges != null) { sqlDataAdapter1.Update(authorChanges); return authorChanges; } else { return null; } }
Note In a production application, you would add error checking and exception handling to these methods. 5. On the File menu, click Save All. 6. On the Build menu, click Build Solution. 7. To test the XML Web service, in the Solution Explorer right-click AuthorsService.asmx and click View in Browser. In the browser, click GetAuthors and in the resulting GetAuthors page, click the Invoke button. You should see the list of authors displayed as XML.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
39
Exercise 2 Creating a Windows User Interface In this exercise, you will create a Windows Forms client application. The application consists of one Windows Form that contains a reference to AuthorsWebService. The data in the database is displayed in a DataGrid control when a Load button on the form is clicked. To implement the display of data in the DataGrid, you call the XML Web service’s GetAuthors method. The DataGrid control enables direct editing of the data, with changes being passed directly to the underlying dataset. In addition to a Load button, the form has a save button. The code for this button calls the XML Web service's UpdateAuthors method to save the changes back to the database.
! Create the Windows application 1. Open Visual Studio .NET and on the File menu, click New, and then click Project to display the New Project dialog box. 2. Select Visual C# Projects in the Project Types pane, then select Windows Application in the Templates pane. 3. Name the project AuthorsWinClient. 4. In the Location box, type the name of the Starter folder: \Labs\Lab16\Starter 5. Click OK. The AuthorsWinClient project is created and Form1 is automatically added to the project and appears in the Windows Forms Designer. 6. Add a reference to the XML Web service project that you created earlier. a. In Solution Explorer, expand the AuthorsWinClient node, right-click the References node, and on the shortcut menu, click Add Web Reference. b. In the Address box at the top of the Add Web Reference dialog box, type the following and then press ENTER: http://localhost/AuthorsWebService/AuthorsService.asmx This is the location of the XML Web service file of your ASP.NET Web Service project. c. Click Add Reference. You can now create an instance of the authors1 dataset in your application.
40
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
! Add the controls to the form 1. Drag a DataGrid control from the Windows Forms tab of the Toolbox onto the form. 2. Drag a Button control from the Windows Forms tab of the Toolbox onto the form. Set the button's Name property to LoadData and its Text property to Load. 3. Drag a second Button control from the Windows Forms tab of the Toolbox onto the form. Set the button's Name property to SaveData and its Text property to Save. 4. Drag a DataSet object from the Data tab of the Toolbox onto the form. In the Add DataSet dialog box, select Typed dataset and then select AuthorsWinClient.localhost.authors1 from the Name list. Click OK. This action creates a DataSet object in the component tray that is based on the authors1 dataset class definition. 5. Select the DataSet control and set the Name property to AuthorData. 6. Select the DataGrid control and select AuthorData from the DataSource property list. Select authors from the DataMember property list. The column headings of the DataGrid are set to the Authors table column names. You may want to adjust the width of the form and the DataGrid control to accommodate all of the column headings. The user can then avoid having to scroll horizontally to view all of the columns in the DataGrid.
! Add code for the LoadData and SaveData buttons 1. On the View menu, click Designer. Double-click the button that is labeled Load to create an empty event handler for the Click event. Add the following code to the LoadData_Click method: private void LoadData_Click( object sender, System.EventArgs e) { AuthorsWinClient.localhost.AuthorsService ws = new AuthorsWinClient.localhost.AuthorsService(); ws.Credentials = System.Net.CredentialCache.DefaultCredentials; AuthorData.Merge(ws.GetAuthors()); }
XML Web service methods are called by first creating an instance of the service class, and then calling the service methods. In this case, the GetAuthors method is called. The dataset returned is merged with the AuthorData dataset.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
41
2. On the View menu, click Designer. Double-click the button labeled Save to create an empty event handler for the Click event. Add the following code to the SaveData_Click method: private void SaveData_Click( object sender, System.EventArgs e) { if (AuthorData.HasChanges()) { AuthorsWinClient.localhost.AuthorsService ws = new AuthorsWinClient.localhost.AuthorsService(); ws.Credentials = System.Net.CredentialCache.DefaultCredentials; AuthorsWinClient.localhost.authors1 diffAuthors = new AuthorsWinClient.localhost.authors1(); diffAuthors.Merge(AuthorData.GetChanges()); ws.UpdateAuthors(diffAuthors); AuthorData.Merge(diffAuthors); } }
If there are changes in the dataset, a new dataset of type authors1 is created to hold just the changed data. This dataset is then passed to the XML Web service's UpdateAuthors method. The dataset is returned with the changes accepted, and the AuthorData dataset is updated to reflect these new changes.
! Run the application 1. On the File menu, click Save All. 2. In Solution Explorer, right-click AuthorsWinClient, and click Set as StartUp Project. 3. Press CTRL+F5 to run the application. A window is displayed that contains an empty table with headers from the authors table in the pubs database. 4. Click Load to populate the table. Make some changes to the data, and then click Save. You can click Load again to see that the changes that you made were stored in the database. Also, close and reopen the application. The next time you click Load, you will see that your changes have persisted.
42
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
" Accessing Data with DataReaders Topic Objective
To introduce the topics in this section.
Lead-in
Another method of accessing data from a data source is to use DataReaders.
!
Creating a DataReader
!
Reading Data from a DataReader
!
Using DataSets vs. DataReaders
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The benefit of using a DataSet is that it gives you a disconnected view of the database. For long running applications, this is often the best approach. For Web applications, developers usually perform short and simple operations with each request, such as displaying data. For such operations, developers do not have to maintain a DataSet object. In such cases, you can use a DataReader.
Choosing a DataReader or a DataSet When deciding whether your application should use a DataReader or a DataSet, you should consider the type of functionality that your application requires. Use a DataSet when you need to do any of the following: !
Remote data between tiers or from an XML Web service.
!
Interact with data dynamically such as binding to a Windows Forms control or combining and relating data from multiple sources.
!
Cache data locally in your application.
!
Provide a hierarchical XML view of relational data and use tools like an XSL Transformation or an XML Path Language (XPath) Query on your data. For more information, see XML and the DataSet in the .NET Framework SDK documentation.
!
Perform extensive processing on data without requiring an open connection to the data source, which frees the connection to be used by other clients.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
43
If you do not require the functionality that is provided by the DataSet, you can improve the performance of your application by using the DataReader to return your data in a forward-only read-only fashion. Although the DataAdapter uses the DataReader to fill the contents of a DataSet, by using the DataReader instead, you can receive the following performance gains: !
The saving of memory that would be consumed by the DataSet.
!
The saving of the processing that would be required to create and fill the contents of the DataSet.
In this section, you will learn how to read data from a data source by using DataReaders.
44
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Creating a DataReader Topic Objective
To describe how to retrieve data from a database by using a DataReader.
Lead-in
You can also use a DataReader object to read data from a database.
!
Create and Open the Database Connection
SqlConnection SqlConnection mySqlConnection mySqlConnection == new new SqlConnection("server=(local)\\NetSDK; SqlConnection("server=(local)\\NetSDK; ! ! Trusted_Connection=yes;database=northwind"); Trusted_Connection=yes;database=northwind"); mySqlConnection.Open(); mySqlConnection.Open(); !
Create the DataReader From a Command Object
SqlCommand SqlCommand mySqlCommand mySqlCommand == new new SqlCommand( SqlCommand( "select * from customers", "select * from customers", mySqlConnection); mySqlConnection); SqlDataReader SqlDataReader myReader myReader == mySqlCommand.ExecuteReader(); mySqlCommand.ExecuteReader(); !
Close the Reader and the Connection
If If (myReader (myReader != != null) null) myReader.Close(); myReader.Close(); if (mySqlConnection.State if (mySqlConnection.State == == ConnectionState.Open) ConnectionState.Open) mySqlConnection.Close(); mySqlConnection.Close();
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When a large amount of data is being retrieved, holding memory open becomes an issue. For example, reading 10,000 rows out of a database causes a DataTable to allocate and maintain memory for those 10,000 rows for the lifetime of the table. If 1,000 users do this against the same server at the same time, memory usage becomes critical. To address such situations, the DataReader is designed to produce a read-only, forward-only stream returned from the database. Only one record at a time is ever in memory. There are two DataReader objects, the SqlDataReader and the OleDbDataReader. A DataReader keeps the connection open until the DataReader is closed. To use a SqlDataReader, declare a SqlCommand instead of a SqlDataAdapter. The SqlCommand exposes an ExecuteReader method that takes a SqlDataReader as a parameter. Note that you must explicitly open and close the SqlConnection when you use a SQLCommand. SqlConnection mySqlConnection = new SqlConnection("server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind"); mySqlConnection.Open(); SqlCommand mySqlCommand = new SqlCommand( "select * from customers", mySqlConnection); SqlDataReader myReader = mySqlCommand.ExecuteReader(); //... if (myReader != null) myReader.Close(); if (mySqlConnection.State == ConnectionState.Open) mySqlConnection.Close();
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
45
Error Handling with a DataReader When using connections with the DataReader object, you should always use a try/finally block to ensure that, if anything fails, connections will be closed. SqlDataReader myReader = null; SqlConnection mySqlConnection = new SqlConnection( "server=(local)\\NetSDK;! Trusted_Connection=yes;database=northwind"); SqlCommand mySqlCommand = new SqlCommand( "select * from customers", mySqlConnection); try { mySqlConnection.Open(); myReader = mySqlCommand.ExecuteReader(); //… } catch(Exception e) { Console.WriteLine(e.ToString()); } finally { if (myReader != null) myReader.Close(); if (mySqlConnection.State == ConnectionState.Open) mySqlConnection.Close(); }
46
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Reading Data from a DataReader Topic Objective
To explain how to read data from a DataReader.
!
Lead-in
After you have called the ExecuteReader method of the Command object, you access a record in the DataReader by calling the Read method.
Call Read for Each Record #
!
Returns false when there are no more records
Get Field(s) #
Parameter is the ordinal position or name of the field
while while (myReader.Read()) (myReader.Read()) {{ Console.Write(myReader["CustomerID"].ToString() Console.Write(myReader["CustomerID"].ToString() ++ "" "); "); Console.WriteLine(myReader["CompanyName"].ToString()); Console.WriteLine(myReader["CompanyName"].ToString()); }} !
Call Close to Free Up the Reader and the Connection
*****************************ILLEGAL FOR NON-TRAINER USE****************************** After you have called the ExecuteReader method of the Command object, you access a record in the DataReader by calling the Read method. The default positioning in the DataReader is before the first record, therefore you must call Read before accessing any data. When no more records are available, the Read method returns a null value.
Reading Fields from the Current Record To get the data from fields in the current record, call an appropriate Get method, for example, GetDateTime, GetDouble, GetInt32, or GetString. The parameter of the Get method is the ordinal value of the field that you want to read. For example, the following code reads the CustomerID and CompanyName fields, from the current record of the DataReader, by using the GetString method: string customerID = myReader.GetString(0); string companyName = myReader.GetString(1);
You can also reference the fields of data in the current record of the data reader by name, and then call an appropriate conversion function, as shown in the following example code: string customerID = myReader["CustomerID"].ToString(); string companyName = myReader["CompanyName"].ToString();
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
47
Looping Through All Records To loop through and output to the Console all the records in a DataReader, you can use a While loop, as shown in the following sample code: while (myReader.Read()) { Console.Write(myReader["CustomerID"].ToString() + " "); Console.WriteLine(myReader["CompanyName"].ToString()); }
Closing the DataReader While the DataReader is in use, the associated connection is busy serving the DataReader. Therefore, you must call Close to close the DataReader when you are finished using it. if (myReader != null) myReader.Close(); if (mySqlConnection.State == ConnectionState.Open) mySqlConnection.Close();
48
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Demonstration: Accessing Data Using DataReaders Topic Objective
To introduce the demonstration.
Lead-in
In this demonstration, you will see how to read data from a database by using a SqlDataReader.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this demonstration, you will see how to read data from a database by using a SQLDataReader. Run the Visual Studio .NET DataReader project from the following location: \Democode\Mod16\Demo16.4
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
49
Using DataSets vs. DataReaders Topic Objective
To describe the different processes for getting data with a DataSet versus a DataReader.
DataSet
DataReader
1.
Create a database connection
1.
Create a database connection
Lead-in
2.
Store query in DataAdapter
2.
Open the database connection
3.
Populate DataSet with Fill method
3.
Store query in SqlCommand
4.
4.
Create DataView
Populate DataReader with ExecuteReader method
5.
Bind DataView to list-bound control
5.
Call Read for each record, and Get for each field
6.
Manually display data
7.
Close the DataReader and the connection
The procedure for accessing databases from ASP.NET is different depending on whether you use a DataSet or a DataReader.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Discuss performance issues in DataReaders and DataSets.
The general procedure for accessing databases from ASP.NET is different depending on whether you will be using a DataSet or a DataReader: Using DataSets
Using DataReaders
1. Connect to the database by using SqlConnection or OleDbConnection.
1. Connect to the database by using SqlConnection or OleDbConnection.
2. Store the database query in SqlDataAdapter or OleDbDataAdapter objects.
2. Open the connection with the Open method.
3. Populate a DataSet from the DataAdapter by using Fill.
3. Store database query in SqlCommand or OleDbCommand objects.
4. Set up a new DataView for the desired table.
4. Populate a DataReader from the Command by using ExecuteReader method.
5. Bind a server control, such as the DataGrid, to the DataView.
5. Call Read and Get methods of the DataReader to read data. 6. Manually display data. 7. Close the DataReader. and the connection.
50
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
" Binding to XML Data Topic Objective
To introduce the topics included in this section.
Lead-in
Another common source of data for applications is XML documents.
!
Overview of XML
!
Reading XML Data into a DataSet
*****************************ILLEGAL FOR NON-TRAINER USE****************************** HTML is widely used for presenting information on the Web. HTML works well as a presentation language, but it is not suitable for representing data. For example, you can easily format data in an HTML table, but you cannot describe the individual components of the information. To share information between applications, you must have a language that can describe data in a standardized way so that any application, present or future, can understand and use this data correctly. XML is one such standardized language. XML not only helps you structure your data but acts as a common language between different business applications. In this section, you will get an overview of XML. You will also learn how to read and display XML data by using ADO.NET.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
51
Overview of XML Topic Objective
To provide an overview of XML data.
Lead-in
XML is a common language used for exchanging information between business applications.
!
Machine-Readable and Human-Readable Data
!
Defines the Data Content and Structure
!
Separates Structure from Presentation
!
Allows You to Define Your Own Tags and Attributes <employee> <employee> Jake Jake <salary>25000 <salary>25000 Ohio Ohio
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Businesses today face many problems when it comes to organizing data. They need to meet the following requirements: !
Data must be readable by both computers and users.
!
Both the content and the structure of the data must be defined.
!
The structure of the data needs to be separate from the presentation of the data.
!
The structure needs to be open and extensible.
XML fulfills all of these requirements. XML defines the structure of data in an open and self-describing manner. This allows data to be easily transferred over a network and consistently processed by the receiver. XML describes how data is structured, not how it should be displayed or used. XML documents contain tags that assign meaning to the content of the document. These tags allow programmers to find the data they need in the XML document. For example, the following XML sample contains information about an employee but does not specify how to display this information: <employee> Jake <salary>25000 Ohio
52
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
XML is considered a markup language because it allows you to define data structure by using markup tags. You can define your own tags that describe the data in whatever way you find useful. XML data is held in a simple, open format that is easily parsed by other applications. The fact that XML documents contain text rather than binary data is another key advantage. Applications can parse an XML document, looking for specific tags of interest to those applications. Unknown tags and their associated data can be freely ignored.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
53
Reading XML Data into a DataSet Topic Objective
To explain how to read XML data into a DataSet.
Lead-in
Because XML is an important part of ASP.NET, there are various easy ways to bind to XML data.
!
Setup the XML file for reading
FileStream FileStream fs fs == File.OpenRead("myFile.xml"); File.OpenRead("myFile.xml"); StreamReader StreamReader myStreamReader myStreamReader == new new StreamReader(fs); StreamReader(fs); !
Read the contents of the File into a DataSet DataSet DataSet myDataSet myDataSet == new new DataSet(); DataSet(); myDataSet.ReadXml(myStreamReader); myDataSet.ReadXml(myStreamReader);
!
Bind a DataGrid to the DataSet dataGrid1.DataSource dataGrid1.DataSource == myDataSet.Tables[0]; myDataSet.Tables[0];
*****************************ILLEGAL FOR NON-TRAINER USE****************************** DataSets in ADO.NET are designed to extract data in a way that is independent of its data source. Therefore, reading data from an XML source is similar to reading data from a database. To read XML data, you import the System.IO namespace in your ASP.NET page. Note You cannot read XML data into a DataReader. You can read it only into a DataSet. For XML data, the DataSet supports a ReadXml method that takes a FileStream as its parameter. The DataSet expects data to be in the following format: column column column column column column
value value value
value value value
Each TableName section corresponds to a single row in the table.
54
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
The following example shows how to read the schema and data from an XML file by using the ReadXml method, the FileStream object, and the StreamReader object. Note that after the data is read into the DataSet, it is indistinguishable from SQL data—the DataGrid binds to it in the same way. You can perform the following steps to read a file named myFile.xml that contains XML data and displays it in a DataGrid. First open the XML file and create a StreamReader: FileStream fs = File.OpenRead("myFile.xml"); StreamReader myStreamReader = new StreamReader(fs);
Next, read the XML data into the DataSet: DataSet myDataSet = new DataSet(); myDataSet.ReadXml(myStreamReader);
Finally, bind a DataGrid to the DataSet: dataGrid1.DataSource = myDataSet.Tables[0];
After the data has been read into a DataSet, the repeated elements in the XML become the columns in the DataSet and can be bound to any control to be displayed on the client. The Windows Forms code, including try/catch/finally blocks, that is used in Demonstration: Reading XML Data into a DataSet, is as follows: StreamReader myStreamReader = null; statusBar1.Text = ""; try { FileStream fs = File.OpenRead(textBox1.Text); myStreamReader = new StreamReader(fs); DataSet myDataSet = new DataSet(); myDataSet.ReadXml(myStreamReader); dataGrid1.DataSource = myDataSet.Tables[0]; statusBar1.Text = "Read file: " + textBox1.Text; } catch(Exception exception) { statusBar1.Text = exception.ToString(); } finally { if (myStreamReader != null) myStreamReader.Close(); }
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
55
Demonstration: Reading XML Data into a DataSet Topic Objective
To demonstrate how to display information from an XML file.
Lead-in
In this demonstration, you will see how to read information from an XML file and display it in a DataGrid control.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** In this demonstration, you will see how to read information from an XML file and display it in a DataGrid control. Run the Visual Studio .NET XMLData project from the following location: \Democode\Mod16\Demo16.5 Enter the default filename books1.xml and click the Load Data button. The file books1.xml is read into a DataSet and displayed in a DataGrid.
56
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
Overview of ADO.NET
!
Connecting to a Data Source
!
Accessing Data with DataSets
!
Using Stored Procedures
!
Accessing Data with DataReaders
!
Binding to XML Data
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. What are some of the new objects in the ADO.NET object model? Some of the new objects in the ADO.NET object model are: DataSet, DataAdapter, DataView, DataReader, and DataTable.
2. What is the difference between a DataSet and a DataView? DataSet is a collection of DataTables and a DataView is a custom view of the DataTables in a DataSet.
3. What is the difference between a DataSet and a DataReader? The DataReader is designed to produce a read-only, forward-only stream returned from the database. The DataSet is designed to handle the actual data from a data store. It represents a cache of data, with database-like behavior. It can contain tables, columns, relationships, constraints, and data.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
57
4. What is the purpose of the DataAdapter object? A DataAdapter object is a tool that is used to create and initialize various tables. It allows for the retrieval and saving of data between a DataSet object and the source data store. It is responsible for pulling out data from the physical store and pushing it into data tables and relations.
5. Which method is used to populate a DataSet with results of a query? The method that is used to populate the DataSet with results of a query is the Fill method.
THIS PAGE INTENTIONALLY LEFT BLANK
Module 17 (Optional): Attributes Contents Overview Overview of Attributes Defining Custom Attributes
1 2 13
Retrieving Attribute Values
22
Demonstration: Custom Attributes
26
Lab 17: Defining and Using Attributes
27
Review
36
Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, places or events is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001-2002 Microsoft Corporation. All rights reserved. Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint, Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
Module 17 (Optional): Attributes
Instructor Notes Presentation: 60 Minutes
Teach this module if time permits. Module 17 is a stand-alone module that is not dependent upon any other module.
Lab: 45 Minutes
This module provides students with the details about how to use attributes in code. It describes the predefined attributes that are provided by the Microsoft® .NET Framework and provides some simple examples of how to use some common attributes. The concept of custom attributes is introduced. This introduction is followed by a detailed explanation of how to define and use custom attributes. The process used to compile code that has custom attributes is also explained. Finally, the module describes how to retrieve attribute values during run time by using reflection. The procedure that is used to retrieve attribute information into an array and query the array to obtain the required values is explained. After completing this module, students will be able to: !
Use common predefined attributes.
!
Create simple custom attributes.
!
Query attribute information at run time.
Materials and Preparation This section provides the materials and preparation tasks that you need to teach this module.
Required Materials To teach this module, you need Microsoft PowerPoint® file 2349B_17.ppt.
Preparation Tasks To prepare for this module, you should: !
Read all of the materials for this module.
!
Complete the lab.
!
Read the instructor notes and margin notes for the module.
!
Practice using the Microsoft Intermediate Language disassembler to examine the metadata of an assembly.
iii
iv
Module 17 (Optional): Attributes
Demonstration This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for the student notes. Use the debugger to step through the code while you point out features and ask students what they think will happen next.
Custom Attributes In this demonstration, you will show students how to declare and apply custom attributes and how to use reflection to retrieve custom attribute metadata. The code for this demonstration is contained in one project and is located in \Democode\Mod17.
Module Strategy Use the following strategy to present this module: !
Overview of Attributes Begin by explaining that attributes are only annotations to classes and perform no function themselves. Before attributes can cause an action, certain code must be implemented. This code is in the runtime for the predefined attributes and is written by the developer for custom attributes. Explain the syntax used to apply an attribute, and, after covering the lists of predefined attributes briefly, discuss the three attributes—Conditional, DllImport, and Transaction—using examples.
!
Defining Custom Attributes Introduce the need for creating custom attributes, and explain the procedures involved in defining a custom attribute. Explain the use of AttributeUsage and how to create an attribute class. Then explain the details of the procedure used to compile code that uses custom attributes. Finish the section by explaining how to use multiple attributes in code.
!
Retrieving Attribute Values In this section, introduce the concept of retrieving attribute values at run time by using reflection. Explain how to use the MemberInfo class and the typeof operator to obtain attribute values. Finally, discuss how to iterate through the stored attribute values in an array to retrieve the required values. To end the discussion about attributes, use the review slide to recapitulate the main concepts covered in the module.
Module 17 (Optional): Attributes
1
Overview Topic Objective
To provide an overview of the module topics and objectives.
Lead-in
In this module, you will learn about using attributes in Microsoft Visual C# .NET.
!
Overview of Attributes
!
Defining Custom Attributes
!
Retrieving Attribute Values
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Teach this module if time permits. This is a standalone module that is not dependent upon any other module.
Attributes are a simple technique for adding metadata to classes. They can be useful when you need to build components. In this module, you will learn the purpose of attributes and the function that they perform in Microsoft® Visual C#™ .NET applications. You will learn about attribute syntax and how to use some of the predefined attributes in the Microsoft .NET Framework environment. You will also learn to create custom user-defined attributes. Finally, you will learn how classes and other object types can implement and use these custom attributes to query attribute information at run time. After completing this module, you will be able to: !
Use common predefined attributes.
!
Create simple custom attributes.
!
Query attribute information at run time.
2
Module 17 (Optional): Attributes
" Overview of Attributes Topic Objective
To introduce the topics covered in this section.
Lead-in
In this section, you will learn what attributes are and how they are used.
!
Introduction to Attributes
!
Applying Attributes
!
Common Predefined Attributes
!
Using the Conditional Attribute
!
Using the DllImport Attribute
!
Using the Transaction Attribute
*****************************ILLEGAL FOR NON-TRAINER USE****************************** With the introduction of attributes, the C# language provides a convenient technique that will help handle tasks such as changing the behavior of the runtime, obtaining transaction information about an object, conveying organizational information to a designer, and handling unmanaged code. After completing this lesson, you will be able to: !
Identify which tasks you can perform with attributes.
!
Use the syntax for using attributes in your code.
!
Identify some of the predefined attributes that are available in the .NET Framework.
Module 17 (Optional): Attributes
3
Introduction to Attributes Topic Objective
To define attributes.
Lead-in
!
The concept of an attribute is simple.
!
Attributes Are: #
Declarative tags that convey information to the runtime
#
Stored with the metadata of the element
.NET Framework Provides Predefined Attributes #
The runtime contains code to examine values of attributes and act on them
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Stress that attributes are fundamentally a very simple idea—they are simply annotations for your code that are intended to convey useful declarative information.
The .NET Framework provides attributes so that you can extend the capabilities of the C# language. An attribute is a declarative tag that you use to convey information to the runtime about the behavior of programmatic elements such as classes, enumerators, and assemblies. You can think of attributes as annotations that your programs can store and use. In most cases, you write the code that retrieves the values of an attribute in addition to the code that performs a change in behavior at run time. In its simplest form, an attribute is an extended way to document your code. You can apply attributes to many elements of the source code. Information about the attributes is stored with the metadata of the elements they are associated with. The .NET Framework is equipped with a number of predefined attributes. The code to examine them and act upon the values they contain is also incorporated as a part of the runtime and .NET Framework software development kit (SDK).
4
Module 17 (Optional): Attributes
Applying Attributes Topic Objective
To explain the syntax for using attributes.
Lead-in
Attributes can be applied to several different types of programming elements.
!
Syntax: Use Square Brackets to Specify an Attribute
[attribute(positional_parameters,named_parameter=value, [attribute(positional_parameters,named_parameter=value, ...)] ...)] element element !
To Apply Multiple Attributes to an Element, You Can: #
Specify multiple attributes in separate square brackets
#
Use a single square bracket and separate attributes with commas
#
For some elements such as assemblies, specify the element name associated with the attribute explicitly
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can apply attributes to different kinds of programming elements. These elements include assemblies, modules, classes, structs, enums, constructors, methods, properties, fields, events, interfaces, parameters, return values, and delegates.
Attribute Syntax To specify an attribute and associate it with a programming element, use the following general syntax: [attribute(positional_parameters,name_parameter=value, ...)] element
You specify an attribute name and its values within square brackets ([ and ]) before the programmatic element to which you want to apply the attribute. Most attributes take one or more parameters, which can be either positional or named. You specify a positional parameter in a defined position in the parameter list, as you would specify parameters for methods. Any named parameter values follow the positional parameters. Positional parameters are used to specify essential information, whereas named parameters are used to convey optional information in an attribute. Tip Before using an unfamiliar attribute, it is a good practice to check the documentation for the attribute to find out which parameters are available and whether they should be positional or named.
Module 17 (Optional): Attributes
Example As an example of using attributes, consider the following code, in which the DefaultEvent attribute is applied on a class by using a positional string parameter, ShowResult: [DefaultEvent("ShowResult")] public class Calculator: System.Windows.Forms.UserControl { ... }
Applying Multiple Attributes You can apply more than one attribute to an element. You can enclose each attribute in its own set of square brackets, although you can also enclose multiple attributes, separated with commas, in the same set of square brackets. In some circumstances, you must specify exactly which element an attribute is associated with. For example, in the case of assembly attributes, place them after any using clauses but before any code, and explicitly specify them as attributes of the assembly. The following example shows how to use the CLSCompliant assembly attribute. This attribute indicates whether or not an assembly strictly conforms to the Common Language Specification. using System; [assembly:CLSCompliant(true)] class MyClass { ... }
5
6
Module 17 (Optional): Attributes
Common Predefined Attributes Topic Objective
To list some common predefined attributes.
Lead-in
!
.NET Provides Many Predefined Attributes
The .NET Framework provides a large number of predefined attributes.
#
General attributes
#
COM interoperability attributes
#
Transaction handling attributes
#
Visual designer component-building attributes
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The capabilities of predefined attributes in the .NET Framework encompass a wide range of areas, from interoperability with COM to compatibility with visual design tools. This topic describes some of the common predefined attributes that are provided by the .NET Framework. However, it is not intended to be comprehensive. For more information about predefined attributes, refer to the Microsoft Visual Studio® .NET documentation.
General Attributes The following list summarizes some of the general attributes that are provided by the .NET Framework. Attribute
Applicable to
Description
Conditional
Method
Tests to see whether a named symbol is defined. If it is defined, any calls to the method are executed normally. If the symbol is not defined, the call is not generated.
DllImport
Method
Indicates that the method is implemented in unmanaged code, in the specified DLL. It causes the DLL to be loaded at run time and the named method to execute.
SecurityPermissionAttributes
Assembly, Class, Struct, Constructor, Method
Allows security actions for SecurityPermission to be applied to code using declarative security.
Module 17 (Optional): Attributes
Delivery Tip
Avoid getting into long conversations about COM, transactions, and interoperability because this is beyond the scope of this module. This information is presented to show that the .NET Framework is compatible with COM. Tell students that COM and interoperability is covered in depth in Module 15, “Interoperating Between Managed and Unmanaged Code,” in this course.
7
COM Interoperability Attributes When using the attributes to provide interoperability with COM, the goal is to ensure that using COM components from the managed .NET Framework environment is as seamless as possible. The .NET Framework has many attributes relating to COM interoperability. Some of these are listed in the following table. Attribute
Applicable to
Description
ComImport
Class/Interface
Indicates that a class or interface definition was imported from a COM type library.
ComRegisterFunction
Method
Specifies the method to be called when a .NET Framework assembly is registered for use from COM.
ComUnregisterFunction
Method
Specifies the method to be called when a .NET assembly is unregistered for use from COM.
DispId
Method, field, property
Indicates which dispatch ID is to be used for the method, field, or property.
In
parameter
Indicates that the data should be marshaled from the caller to the callee.
MarshalAs
Field, parameter, return values
Specifies how data should be marshaled between COM and the managed environment.
ProgId
Class
Specifies which prog ID is to be used for the class.
Out
parameter
Indicates that data should be marshaled from the callee back to caller.
InterfaceType
Interface
Specifies whether a managed interface is IDispatch, IUnknown, or Dual when it is exposed to COM.
For more information about COM interoperability, see Module 15, “Interoperating Between Managed and Unmanaged Code,” in Course 2349B, Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET), or search for "Microsoft ComServices" in the .NET Framework SDK.
Transaction Handling Attributes Components running in a COM+ environment use transaction management. The attribute you use for this purpose is shown in the following table. Attribute
Applicable to
Description
Transaction
Class
Specify the type of transaction that should be available to this object.
8
Module 17 (Optional): Attributes
Visual Designer Component-Building Attributes Developers who build components for a visual designer use the attributes listed in the following table. Attribute
Applicable to
Description
Bindable
Property
Specifies whether a property is typically used for binding.
DefaultProperty
Class
Specifies the default property for the component.
DefaultValue
Property
Indicates that the property is the default value for the component.
Localizable
Property
When code is generated for a component, members that are marked with Localizable(true) have their property values saved in resource files. You can localize these resource files without modifying the code.
DefaultEvent
Class
Specifies the default event for the component.
Category
Property, event
Specifies the category into which the visual designer should place this property or event in the property window.
Description
Property, event
Defines a brief piece of text to be displayed at the bottom of the property window in the visual designer when this property or event is selected.
Module 17 (Optional): Attributes
9
Using the Conditional Attribute Topic Objective
To show how to use the Conditional attribute.
!
Serves As a Debugging Tool #
Lead-in
The Conditional attribute is frequently used for debugging classes.
#
!
Causes conditional compilation of method calls, depending on the value of a programmer-defined symbol Does not cause conditional compilation of the method itself
Restrictions on Methods
class class MyClass MyClass {{ [Conditional [Conditional ("DEBUGGING")] ("DEBUGGING")] public public static static void void MyMethod( MyMethod( )) {{ ... ... }} }}
#
Must have return type of void
#
Must not be declared as override
#
Must not be from an inherited interface
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
The Conditional attribute is common and is used in the labs. Make sure students understand how to use it.
You can use the Conditional attribute as a debugging aid in your C# code. This attribute causes conditional compilation of method calls, depending on the value of a symbol that you define. It lets you invoke methods that, for example, display the values of variables, while you test and debug code. After you have debugged your program, you can “undefine” the symbol and recompile your code without changing anything else. (Or you can simply remove the symbol from the command line, and not change anything.)
Example The following example shows how to use the Conditional attribute. In this example, the MyMethod method in MyClass is tagged with the Conditional attribute by the symbol DEBUGGING: class MyClass { [Conditional ("DEBUGGING")] public static void MyMethod( ) { ... } }
10
Module 17 (Optional): Attributes
The symbol DEBUGGING is defined as follows: #define DEBUGGING class AnotherClass { public static void Test( ) { MyClass.MyMethod( ); } }
As long as the symbol DEBUGGING remains defined when the method call is compiled, the method call will operate normally. When DEBUGGING is undefined, the compiler will omit calls to the method. Therefore, when you run the program, it will be treated as though that line of code does not exist. You can define the symbol in one of two ways. You can either add a #define directive to the code as shown in the preceding example, or define the symbol from the command line when you compile your program.
Restrictions on Methods The methods to which you can apply a Conditional attribute are subject to a number of restrictions. In particular, they must have a return type of void, they must not be marked as override, and they must not be the implementation of a method from an inherited interface. Note The Conditional attribute does not cause conditional compilation of the method itself. The attribute only determines the action that will occur when the method is called. If you require conditional compilation of a method, then you must use the #if and #endif directives in your code.
Module 17 (Optional): Attributes
11
Using the DllImport Attribute Topic Objective
To explain how to use the DllImport attribute.
Lead-in
The DllImport attribute is used to handle unmanaged code.
!
With the DllImport Attribute, You Can: #
Invoke unmanaged code in DLLs from a C# environment
#
Tag an external method to show that it resides in an unmanaged DLL
[DllImport("MyDLL.dll", [DllImport("MyDLL.dll", EntryPoint=“MyFunction")] EntryPoint=“MyFunction")] public public static static extern extern int int MyFunction(string MyFunction(string param1); param1); public public class class MyClass( MyClass( )) {{ ... ... int int result result == MyFunction("Hello MyFunction("Hello Unmanaged Unmanaged Code"); Code"); ... ... }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can use the DllImport attribute to invoke unmanaged code in your C# programs. Unmanaged code is the term used for code that has been developed outside the .NET environment (that is, standard C compiled into DLL files). By using the DllImport attribute, you can invoke unmanaged code residing in dynamic-link libraries (DLLs) from your managed C# environment.
Invoking Unmanaged Code The DllImport attribute allows you to tag an extern method as residing in an unmanaged DLL. When your code calls this method, the common language runtime locates the DLL, loads it into the memory of your process, marshals parameters as necessary, and transfers control to the address at the beginning of the unmanaged code. This is unlike a normal program, which does not have direct access to the memory that is allocated to it. The following code provides an example of how to invoke unmanaged code: [DllImport("MyDLL.dll", EntryPoint="MyFunction")] public static extern int MyFunction(string param1); public class MyClass( ) { ... int result = MyFunction("Hello Unmanaged Code"); ... }
12
Module 17 (Optional): Attributes
Using the Transaction Attribute Topic Objective
To show how to use the Transaction attribute.
Lead-in
The Transaction attribute is used to manage transactions.
!
To Manage Transactions in COM+ #
Specify that your component be included when a transaction commit is requested
#
Use a Transaction attribute on the class that implements the component
using using EnterpriseServices; EnterpriseServices; ... ... [Transaction(TransactionOption.Required)] [Transaction(TransactionOption.Required)] public public class class MyTransactionalComponent MyTransactionalComponent :: ServicedComponent ServicedComponent {{ ... ... }}
*****************************ILLEGAL FOR NON-TRAINER USE****************************** It is likely that, as a Microsoft Visual Basic® or C++ developer working in a Microsoft environment, you are familiar with technologies such as COM+. An important feature of COM+ is that it allows you to develop components that can participate in distributed transactions, which are transactions that can span multiple databases, machines, and components.
Managing Transactions in COM+ Writing code to guarantee a correct transaction commit in a distributed environment is difficult. However, if you use COM+, it takes care of managing the transactional integrity of the system and coordinating events on the network. In this case, you only need to specify that your component be included when an application that uses your component requests a transaction commit. To make this specification, you can use a Transaction attribute on the class that implements the component, as follows: using EnterpriseServices; ... [Transaction(TransactionOption.Required)] public class MyTransactionalComponent : ServicedComponent { ... }
The Transaction attribute is one of the predefined .NET Framework attributes that the .NET Framework runtime interprets automatically.
Module 17 (Optional): Attributes
13
" Defining Custom Attributes Topic Objective
To introduce the topics covered in this section.
Lead-in
You can define your own custom attributes.
!
Defining Custom Attribute Scope
!
Defining an Attribute Class
!
Processing a Custom Attribute
!
Using Multiple Attributes
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When you encounter a situation in which none of the predefined .NET Framework attributes satisfy your requirements, you can create your own attribute. Such a custom attribute will provide properties that allow you to store and retrieve information from the attribute. Like predefined attributes, custom attributes are objects that are associated with one or more programmatic elements. They are stored with the metadata of their associated elements, and they provide mechanisms for a program to retrieve their values. After completing this lesson, you will be able to: !
Define your own custom attributes.
!
Use your own custom attributes.
14
Module 17 (Optional): Attributes
Defining Custom Attribute Scope Topic Objective
To describe the scope of a custom attribute.
!
Use the AttributeUsage Tag to Define Scope #
Lead-in
Example
You need to scope your custom attribute to define the elements that can use your custom attribute.
[AttributeUsage(AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Method)] public public class class MyAttribute: MyAttribute: System.Attribute System.Attribute {{ ... ... }}
!
Use the Bitwise “or” Operator (|) to Specify Multiple Elements #
Example
[AttributeUsage(AttributeTargets.Class [AttributeUsage(AttributeTargets.Class || AttributeTargets.Struct)] AttributeTargets.Struct)] public public class class MyAttribute: MyAttribute: System.Attribute System.Attribute {{ ... } ... }
*****************************ILLEGAL FOR NON-TRAINER USE****************************** As with some predefined attributes, you must explicitly specify the programming element to which you want to apply a custom attribute. To do so, you annotate your custom attribute with an AttributeUsage tag as shown in the following example: [AttributeUsage(target_elements)] public class MyAttribute: System.Attribute { ... }
Defining Attribute Scope The parameter to AttributeUsage contains values from the System.AttributeTargets enumeration to specify how the custom attribute can be used. The members of this enumeration are summarized in the following table. Member name
Attribute can be applied to
All
Any element
Assembly
assembly
Class
class
Constructor
constructor
Delegate
delegate
Enum
enum
Event
event
Field
field
Interface
interface
Method
method
Module
module
Module 17 (Optional): Attributes
15
(continued) Member name
Attribute can be applied to
Parameter
parameter
Property
property
ReturnValue
return value
Struct
struct
Example of Using Custom Attributes To specify that the MyAttribute custom attribute can be applied only to methods, use the following code: [AttributeUsage(AttributeTargets.Method)] public class MyAttribute: System.Attribute { ... }
Specifying Multiple Elements If the attribute can be applied to more than one element type, use the bitwise “or” operator (|) to specify multiple target types. For example, if MyAttribute can also be applied to constructors, the earlier code will be modified as follows: [AttributeUsage(AttributeTargets.Method | !AttributeTargets.Constructor)] public class MyAttribute: System.Attribute { ... }
If a developer attempts to use MyAttribute in a context different than that which is defined by AttributeUsage, the developer’s code will not compile.
16
Module 17 (Optional): Attributes
Defining an Attribute Class Topic Objective
To describe what attribute classes are and how they are defined.
!
Lead-in
To define custom attributes, you must first define an attribute class. !
Deriving an Attribute Class #
All attribute classes must derive from System.Attribute, directly or indirectly
#
Suffix name of attribute class with “Attribute”
Components of an Attribute Class #
Define a public constructor for the attribute class whose arguments are the required positional parameters
#
Use non-static public read-write properties for optional named parameters
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Delivery Tip
Use the terminology carefully. Talking about attributes having attributes could be confusing to some students.
Key Points
1. Always append the suffix “Attribute” to the end of custom attribute classes. 2. Custom attribute classes must descend from System.Attribute.
After you define the scope of a custom attribute, you need to specify the way you want the custom attribute to behave. For this purpose, you must define an attribute class. Such a class will define the name of the attribute, how it can be created, and the information that it will store. The .NET Framework SDK provides a base class, System.Attribute that you must use to derive custom attribute classes and to access the values held in custom attributes.
Deriving an Attribute Class All custom attribute classes must derive from System.Attribute, either directly or indirectly. The following code provides an example: public class DeveloperInfoAttribute: System.Attribute { ... public DeveloperInfoAtribute(string developer) public string Date { get { ... } set { ... } } }
It is a good practice to append the name of a custom attribute class with the suffix “Attribute,” as in DeveloperInfoAttribute. This makes it easier to distinguish the attribute classes from the non-attribute classes.
Module 17 (Optional): Attributes
17
Components of an Attribute Class All attribute classes must have a constructor. For example, the DeveloperInfo attribute requires the name of the developer as a string parameter; it therefore has a constructor that accepts a string parameter. A custom attribute must define a single constructor that sets the mandatory information. The positional parameter or parameters of the attribute pass this information to the constructor. An attribute class can allow for optional values by declaring non-static public read-write properties or fields. You can specify these optional values as named parameters when using the attribute. For example, the following DeveloperInfoAttribute provides the required developer name, Bert, as a positional parameter and specifies the optional Date parameter, “08-28-2001” as a named parameter: [DeveloperInfoAttribute("Bert", Date="08-28-2001")] public class MyClass { ... }
18
Module 17 (Optional): Attributes
Processing a Custom Attribute Topic Objective
To describe how the compiler processes a custom attribute.
Lead-in
In this topic, you will see the process that occurs when the compiler encounters an attribute on a program element.
!
The Compilation Process 1. Searches for the Attribute Class 2. Checks the Scope of the Attribute 3. Checks for a Constructor in the Attribute 4. Creates an Instance of the Object 5. Checks for a Named Parameter 6. Sets Field or Property to Named Parameter Value 7. Saves Current State of Attribute Class
*****************************ILLEGAL FOR NON-TRAINER USE****************************** When the compiler encounters an attribute on a programming element, the compiler uses the following process to determine how to apply the attribute: 1. Searches for the attribute class 2. Checks the scope of the attribute 3. Checks for a constructor in the attribute 4. Creates an instance of the object 5. Checks for a named parameter 6. Sets the field or property to a named parameter value 7. Saves the current state of the attribute class To be completely accurate, the compiler actually verifies that it could create the attribute, and then stores the information to do so in the metadata. The compiler does not create attribute instances at compile time.
Module 17 (Optional): Attributes
19
Example To learn more about how the compiler handles attributes, consider the following example: [AttributeUsage(AttributeTargets.Class)] public class DeveloperInfoAttribute: System.Attribute { ... } ..... { ..... } [DeveloperInfo("Bert", Date="08-28-2001")] public class MyClass { ... }
The Compilation Process In the preceding example, when MyClass is compiled, the compiler will search for an attribute class called DevloperInfoAttribute. If the class cannot be located, the compiler will then search for DeveloperInfo. After it finds DeveloperInfo, the compiler will check whether the attribute is allowed on a class. Then it will check for a constructor that matches the parameters specified in the attribute use. If it finds one, it creates an instance of the object by calling the constructor with the specified values. If there is a named parameter, the compiler matches the name of the parameter with a field or property in the attribute class, and then sets the field or property to the specified value. Then the current state of the attribute class is saved to the metadata for the program element on which it is applied. Note As is mentioned in the previous topic, it is a good practice to add the suffix “Attribute” to the name of an attribute class. Strictly speaking, it is not necessary to do so. Even if you omit the Attribute suffix as shown in the example, your code will still compile correctly. However, without the Attribute suffix there are potential issues concerning how the compiler searches for classes. Always use the Attribute suffix.
20
Module 17 (Optional): Attributes
Using Multiple Attributes Topic Objective
To describe how the same attribute can be applied multiple times to the same programming element.
Lead-in
Depending upon the circumstances, it might be useful to use a custom attribute multiple times on the same element.
!
An Element Can Have More Than One Attribute #
!
Define both attributes separately
An Element Can Have More Than One Instance of The Same Attribute #
Use AllowMultiple = true
*****************************ILLEGAL FOR NON-TRAINER USE****************************** You can apply more than one attribute to a programming element, and you can use multiple instances of the same attribute in an application.
Using Multiple Attributes You can apply more than one attribute to a programming element. For example, the following code shows how you can tag the FinancialComponent class with two attributes: DeveloperInfo and DefaultProperty: [DeveloperInfo("Bert", Date="08-28-2001")] [DefaultProperty("Balance")] public class FinancialComponent: { ... public long Balance { ... } }
Module 17 (Optional): Attributes
21
Using the Same Attribute Multiple Times Delivery Tip
Stress that the default behavior of a custom attribute is that it does not permit its multiple use.
The default behavior of a custom attribute does not permit multiple instances of the attribute. However, under some circumstances it might make sense to allow an attribute to be used on the same element more than once. An example of this is the custom attribute DeveloperInfo. This attribute allows you to record the name of the developer that wrote a class. If more than one developer was involved in the development, you need to use the DeveloperInfo attribute more than once. For an attribute to permit this, you must mark it as AllowMultiple in the AttributeUsage attribute, as follows: [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)] public class DeveloperInfoAttribute: System.Attribute { ... }
22
Module 17 (Optional): Attributes
" Retrieving Attribute Values Topic Objective
To introduce the topics covered in this section.
Lead-in
Having learned to create a custom attribute and apply it to a class, you now need to be able to query and process attribute information.
!
Examining Class Metadata
!
Querying for Attribute Information
*****************************ILLEGAL FOR NON-TRAINER USE****************************** After you have applied attributes to programming elements in your code, it is useful to be able to determine the values of the attributes. In this section, you will learn how to use reflection to examine the attribute metadata of a class and query classes for attribute information. After completing this lesson, you will be able to: !
Use reflection to examine the attribute metadata of a class.
!
Query classes for attribute information.
Module 17 (Optional): Attributes
23
Examining Class Metadata Topic Objective
To describe how to access the information inside a class at run time.
!
Lead-in
Attribute information is stored with the metadata of a class. To gain access to it, you first need to retrieve the metadata for that class. !
To Query Class Metadata Information: #
Use the MemberInfo class in System.Reflection
#
Populate a MemberInfo array by using the method System.Type.GetMembers
#
Create a System.Type object by using the typeof operator
Example
System.Reflection.MemberInfo[ System.Reflection.MemberInfo[ ]] memberInfoArray; memberInfoArray; memberInfoArray memberInfoArray == typeof(MyClass).GetMembers( typeof(MyClass).GetMembers( ); );
*****************************ILLEGAL FOR NON-TRAINER USE****************************** The .NET Framework runtime supplies a mechanism called reflection that allows you to query information held in metadata. Metadata is where attribute information is stored.
Using the MemberInfo Class The .NET Framework provides a namespace named System.Reflection, which contains classes that you can use for examining metadata. One particular class in this namespace—the MemberInfo class—is very useful if you need to find out about the attributes of a class. To populate a MemberInfo array, you can use the GetMembers method of the System.Type object. To create a System.Type object, you use the typeof operator with a class or any other element, as shown in the following code: System.Reflection.MemberInfo[ ] memberInfoArray; memberInfoArray = typeof(MyClass).GetMembers( ); ...
Once created, the memberInfoArray elements can be queried for metadata information about the members of the class MyClass. Tip If you need more detailed information, for example, if you want to discover the values of attributes that a method has, you can use a MethodInfo object. In addition, there are other “Info” classes: ConstructorInfo, EventInfo, FieldInfo, ParameterInfo, and PropertyInfo. Detailed information about how to use these classes is beyond the scope of this course. For more information, search for “System.Reflection namespace” in the .NET Framework SDK. Note MemberInfo is actually the abstract base class of the other “Info” types.
24
Module 17 (Optional): Attributes
Querying for Attribute Information Topic Objective
To show how to use the GetCustomAttributes method.
!
To Retrieve Custom Attribute Information: #
Lead-in
Once you have the MemberInfo for a class, you can query to find out attribute information for that class.
Use GetCustomAttributes to retrieve all attribute information as an array
System.Reflection.MemberInfo System.Reflection.MemberInfo typeInfo; typeInfo; typeInfo typeInfo == typeof(MyClass); typeof(MyClass); object[ object[ ]] attrs attrs == typeInfo.GetCustomAttributes(false); typeInfo.GetCustomAttributes(false); #
Iterate through the array and examine the values of each element in the array
#
Use the IsDefined method to determine whether a particular attribute has been defined for a class
*****************************ILLEGAL FOR NON-TRAINER USE****************************** After you create a MemberInfo variable typeInfo and initialize it with typeof(MyClass), you can query it to get information about the attributes that are applied to its associated class.
Retrieving Attribute Information The MemberInfo object has a method called GetCustomAttributes. This method retrieves the information about all attributes of a class and stores it as an array, as shown in the following code: System.Reflection.MemberInfo typeInfo; typeInfo = typeof(MyClass); object [ ] attrs = typeInfo.GetCustomAttributes(false);
For Your Information
GetCustomAttribute is an overloaded method. The parameter specifies whether to search the inheritance chain.
You can then iterate through the array to find the values of the attributes that you are interested in.
Module 17 (Optional): Attributes
25
Iterating Through Attributes You can iterate through the array of attributes and examine the value of each one in turn. In the following code, the only attribute of interest is DeveloperInfoAttribute, and all the others are ignored. For each DeveloperInfoAttribute found, the values of the Developer and Date properties are displayed as follows: ... object [ ] attrs = typeInfo.GetCustomAttributes(false); foreach(Attribute atr in attrs) { if (atr is DeveloperInfoAttribute) { DeveloperInfoAttribute dia = (DeveloperInfoAttribute)atr; Console.WriteLine("{0} {1}", dia.Developer, dia.Date); } } ...
Tip GetCustomAttributes is an overloaded method. If you only want values for that one attribute type, you can invoke this method by passing the type of the custom attribute you are looking for through it, as shown in the following code: object [ ] attrs = typeInfo.GetCustomAttributes(typeof(DeveloperInfoAttribute), false);
Using the IsDefined Method If there are no matching attributes for a class, GetCustomAttributes returns a null object reference. However, to find out whether a particular attribute has been defined for a class, you can use the IsDefined method of MemberInfo as follows: Type devInfoAttrType = typeof(DeveloperInfoAttribute); if (typeInfo.IsDefined(devInfoAttrType, false)) { Object [ ] attrs = typeInfo.GetCustomAttributes(devInfoAttrType, false); ... }
Note You can use the Microsoft Intermediate Language Disassembler to see these attributes inside the assembly.
26
Module 17 (Optional): Attributes
Demonstration: Custom Attributes Topic Objective
This demonstration shows how to declare and apply custom attributes and how to use reflection to retrieve custom attribute metadata.
Lead-in
This demonstration shows how to declare and apply a custom attribute and how to use the .NET Framework reflection mechanism to retrieve this metadata information.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** This demonstration shows how to declare a custom attribute and apply the custom attribute to a class and to a method. The .NET Framework reflection mechanism is then used to retrieve metadata information about the class and its members including custom attributes. The code for this demonstration is located in \Democode\Mod17\CustomAttribute. The demonstration declares a custom attribute MyAttribute and applies this attribute both to a class named MyClass1 and to a public method of MyClass1 named MyMethod. The demonstration then uses reflection to iterate over and print out metadata for MyClass1. The output of this demonstration is similar to the following: MyClass1 has 1 custom attributes Custom Attribute MyAttribute's Name Property: This is an example attribute associated with the class! MyClass1
MyClass1 members: member name: GetHashCode has 0 custom attributes: member name: Equals has 0 custom attributes: member name: ToString has 0 custom attributes: member name: MyMethod has 1 custom attributes: attribute has type: MyAttribute Name Property: This is an example attribute associated! with the method MyMethod in MyClass1 member name: GetType has 0 custom attributes: member name: .ctor has 0 custom attributes:
Module 17 (Optional): Attributes
27
Lab 17: Defining and Using Attributes Topic Objective
To introduce the lab.
Lead-in
In this lab, you will use the predefined Conditional attribute, and then create and use a custom attribute.
*****************************ILLEGAL FOR NON-TRAINER USE****************************** Explain the lab objectives.
Objectives After completing this lab, you will be able to: !
Use the predefined Conditional attribute.
!
Create a custom attribute.
!
Add a custom attribute value to a class.
!
Use reflection to query attribute values.
Prerequisites Before working on this lab, you should be familiar with the following: !
Creating classes in C#
!
Defining constructors and methods
!
Using the typeof operator
!
Using properties and indexers in C#
Estimated time to complete this lab: 45 minutes
28
Module 17 (Optional): Attributes
Exercise 1 Using the Conditional Attribute In this exercise, you will use the predefined Conditional attribute to conditionally execute your code. Conditional execution is a useful technique if you want to incorporate testing or debugging code into a project but do not want to edit the project and remove the debugging code after the system is complete and functioning correctly. During this exercise, you will add a method called DumpToScreen to the BankAccount class. This method will display the details of the account. You will use the Conditional attribute to execute this method depending on the value of a symbol called DEBUG_ACCOUNT.
! Apply the Conditional attribute 1. Open the Bank.sln project in the install folder\Labs\Lab17\Starter\Bank folder. 2. In the BankAccount class, add a public void method called DumpToScreen that takes no parameters. The method must display the contents of the account: account number, account holder, account type, and account balance. The following code shows a possible example of the method: public void DumpToScreen( ) { Console.WriteLine("Debugging account {0}. Holder is {1}. !Type is {2}. Balance is {3}", this.accNo, this.holder, this.accType, this.accBal); }
3. Make use of the method’s dependence on the DEBUG_ACCOUNT symbol. Add the following Conditional attribute before the method as follows: [Conditional("DEBUG_ACCOUNT")]
4. Add a using directive for the System.Diagnostics namespace. 5. Compile your code and correct any errors.
Module 17 (Optional): Attributes
29
! Test the Conditional attribute 1. Open the TestHarness.sln project in the install folder\Labs\Lab17\ Starter\TestHarness folder. 2. Add a reference to the Bank library. a. In Solution Explorer, expand the TestHarness tree. b. Right-click References, and then click Add Reference. c. Click Browse, and then navigate to install folder\Labs\Lab17\ Starter\Bank\Bin\Debug. d. Click Bank.dll, click Open, and then click OK. 3. Review the Main method of the CreateAccount class. Notice that it creates a new bank account. 4. Add the following line of code to Main to call the DumpToScreen method of myAccount: myAccount.DumpToScreen( );
5. Save your work, compile the project, and correct any errors. 6. Run the test harness. Notice that nothing happens. This is because the DumpToScreen method has not been called. 7. Use the MSIL disassembler (ildasm), from the Visual Studio .NET command prompt to examine install folder\Labs\Lab17\Starter\ Bank\Bin\Debug\Bank.dll. Important To use Microsoft Visual Studio .NET tools within a command prompt window, the command prompt window must have the proper environment settings. The Visual Studio .NET Command Prompt window provides such an environment. To run a Visual Studio .NET Command Prompt window: on the start menu, point to All Programs, point to Microsoft Visual Studio .NET, Visual Studio .NET Tools, and then click Visual Studio .NET Command Prompt. You will see that the DumpToScreen method is present in the BankAccount class. 8. Double-click the DumpToScreen method to display the Microsoft intermediate language (MSIL) code. You will see the ConditionalAttribute at the beginning of the method. The problem is in the test harness. Because of the ConditionalAttribute on DumpToScreen, the runtime will effectively ignore calls made to that method if the DEBUG_ACCOUNT symbol is not defined when the calling program is compiled. The call is made, but because DEBUG_ACCOUNT is not defined, the runtime finishes the call immediately.
30
Module 17 (Optional): Attributes
9. Close the MSIL disassembler. 10. Return to the test harness. At the top of the CreateAccount.cs file, before the first using directive, add the following code: #define DEBUG_ACCOUNT
This defines the DEBUG_ACCOUNT symbol. 11. Save and compile the test harness, correcting any errors. 12. Run the test harness. Notice that the DumpToScreen method displays the information from myAccount.
Module 17 (Optional): Attributes
31
Exercise 2 Defining and Using a Custom Attribute In this exercise, you will create a custom attribute called DeveloperInfoAttribute. This attribute will allow the name of the developer and, optionally, the creation date of a class to be stored in the metadata of that class. This attribute will permit multiple use because more than one developer might be involved in the coding of a class. You will then write a method that retrieves and displays all of the DevloperInfoAttribute values for a class.
! Define a custom attribute class 1. Using Visual Studio .NET, create a new Microsoft Visual C# project, using the information shown in the following table. Element
Value
Project Type
Visual C# Projects
Template
Class Library
Name
CustomAttribute
Location
install folder\Labs\Lab17\Starter
2. Change the name and file name of class Class1 to DeveloperInfoAttribute. Make sure that you also change the name of the constructor. 3. Specify that the DeveloperInfoAttribute class is derived from System.Attribute. This attribute will be applicable to classes, enums, and structs only. It will also be allowed to occur more than once when it is used. 4. Add the following AttributeUsage attribute before the class definition: [AttributeUsage(AttributeTargets.Class | !AttributeTargets.Enum | AttributeTargets.Struct, !AllowMultiple=true)]
5. Document your attribute with a meaningful summary (between the <summary> tags). Use the exercise description to help you. 6. The DeveloperInfoAttribute attribute requires the name of the developer of the class as a mandatory parameter and takes the date that the class was written as an optional string parameter. Add private instance variables to hold this information, as follows: private string developerName; private string dateCreated;
7. Modify the constructor so that it takes a single string parameter that is also called developerName, and add a line of code to the constructor that assigns this parameter to this.developerName. 8. Add a public string read-only property called Developer that can be used to get the value of developerName. Do not write a set accessor.
32
Module 17 (Optional): Attributes
9. Add another public string property that is called Date. This property should have a get accessor that reads dateCreated and a set accessor that writes dateCreated. 10. Compile the class and correct any errors. Because the class is in a class library, the compilation process will produce a DLL (CustomAttribute.dll) rather than a stand-alone executable program. The complete code for the DeveloperInfoAttribute class follows: namespace CustomAttribute { using System; /// <summary> /// This class is a custom attribute that allows /// the name of the developer of a class to be stored /// with the metadata of that class. /// [AttributeUsage(AttributeTargets.Class | !AttributeTargets.Enum | AttributeTargets.Struct, !AllowMultiple=true)] public class DeveloperInfoAttribute: System.Attribute { private string developerName; private string dateCreated; // Constructor. Developer name is the only // mandatory parameter for this attribute. public DeveloperInfoAttribute(string developerName) { this.developerName = developerName; } public string Developer { get { return developerName; } } // Optional parameter public string Date { get { return dateCreated; } set { dateCreated = value; } } } }
Module 17 (Optional): Attributes
! Add a custom attribute to a class 1. You will now use the DeveloperInfo attribute to record the name of the developer of the Rational number class. Open the Rational.sln project in the install folder\Labs\Lab17\Starter\Rational folder. 2. Perform the following steps to add a reference to the CustomAttribute library that you created earlier: a. In Solution Explorer, expand the Rational tree. b. Right-click References, and then click Add Reference. c. In the Add Reference dialog box, click Browse. d. Navigate to the install folder\Labs\Lab17\Starter\ CustomAttribute\Bin\Debug folder, and click CustomAttribute.dll. e. Click Open, and then click OK. 3. Add a CustomAttribute.DeveloperInfo attribute to the Rational class, specifying your name as the developer and the current date as the optional date parameter, as follows: [CustomAttribute.DeveloperInfo("Your Name", !Date="Today")]
4. Add a second developer to the Rational class. 5. Compile the Rational project and correct any errors. 6. Open a Visual Studio .NET Command Prompt window and navigate to the install folder\Labs\Lab17\Starter\Rational\Bin\Debug folder. This folder should contain your Rational.exe executable. 7. Run the MSIL disassembler and open Rational.exe. 8. Expand the Rational namespace in the tree view. 9. Expand the Rational class. 10. Near the top of the class, notice your custom attribute and the values that you supplied. 11. Close the MSIL disassembler.
33
34
Module 17 (Optional): Attributes
! Use reflection to query attribute values Using the MSIL disassembler is only one way to examine attribute values. You can also use reflection in C# programs. Return to Visual Studio, and edit the TestRational class in the Rational project. 1. In the Main method, create a variable called attrInfo of type System.Reflection.MemberInfo, as shown in the following code: public static void Main( ) { System.Reflection.MemberInfo attrInfo; ...
2. You can use a MemberInfo object to hold information about the members of a class. Assign the Rational type to the MemberInfo object by using the typeof operator, as follows: attrInfo = typeof(Rational);
3. The attributes of a class are held as part of the class information. You can retrieve the attribute values by using the GetCustomAttributes method. Create an object array called attrs, and use the GetCustomAttributes method of attrInfo to find all of the custom attributes used by the Rational class, as shown in the following code: object[ ] attrs = attrInfo.GetCustomAttributes(false);
4. Now you need to extract the attribute information that is stored in the attrs array and print it. Create a variable called developerAttr of type CustomAttribute.DeveloperInfoAttribute, and assign it the first element in the attrs array, casting as appropriate, as shown in the following code: CustomAttribute.DeveloperInfoAttribute developerAttr; developerAttr = !(CustomAttribute.DeveloperInfoAttribute)attrs[0];
Note In production code, you would use reflection rather than a cast to determine the type of the attribute. 5. Use the get accessor of the DeveloperInfoAttribute attribute to retrieve the Developer and Date attributes and print them out as follows: Console.WriteLine("Developer: {0}\tDate: {1}", !developerAttr.Developer, developerAttr.Date);
6. Repeat steps 4 and 5 for element 1 of the attrs array. You can use a loop if you want to be able to retrieve the values of more than two attributes.
Module 17 (Optional): Attributes
7. Compile the project and correct any errors. The completed code for the Main method is shown in the following code: namespace Rational { using System; // Test harness public class TestRational { public static void Main( ) { System.Reflection.MemberInfo attrInfo; attrInfo = typeof(Rational); object[ ] attrs = attrInfo.GetCustomAttributes(false); CustomAttribute.DeveloperInfoAttribute developerAttr; developerAttr = !(CustomAttribute.DeveloperInfoAttribute)attrs[0]; Console.WriteLine("Developer: {0}\tDate: {1}", !developerAttr.Developer, developerAttr.Date); developerAttr = !(CustomAttribute.DeveloperInfoAttribute)attrs[1]; Console.WriteLine("Developer: {0}\tDate: {1}", !developerAttr.Developer, developerAttr.Date); } } }
Here is an alternative Main that uses a foreach loop: public static void Main( ) { System.Reflection.MemberInfo attrInfo; attrInfo = typeof(Rational); object[ ] attrs = attrInfo.GetCustomAttributes(false); foreach (CustomAttribute.DeveloperInfoAttribute ! devAttr in attrs) { Console.WriteLine("Developer: {0}\tDate: {1}", !devAttr.Developer, devAttr.Date); } }
8. When you run this program, it will display the names and dates that you supplied as DeveloperInfoAttribute information to the Rational class.
35
36
Module 17 (Optional): Attributes
Review Topic Objective
To reinforce module objectives by reviewing key points.
Lead-in
The review questions cover some of the key concepts taught in the module.
!
Overview of Attributes
!
Defining Custom Attributes
!
Retrieving Attribute Values
*****************************ILLEGAL FOR NON-TRAINER USE****************************** 1. Can you tag individual objects by using attributes? No. Attributes can be associated with classes or other types, or with methods, parameters, return values, constructors, assemblies, delegates, events, interfaces, properties, or fields, but not with individual objects.
2. Where are attribute values stored? An attribute value is stored with the metadata of the programming element that it was used with.
3. What mechanism is used to determine the value of an attribute at run time? Reflection.
Module 17 (Optional): Attributes
4. Define an attribute class called CodeTestAttributes that is applicable only to classes. It should have no positional parameters and two named parameters called Reviewed and HasTestSuite. These parameters should be of type bool and should be implemented by using read/write properties. using System; [AttributeUsage(AttributeTargets.Class)] public class CodeTestAttributes: System.Attribute { public bool Reviewed { get { return reviewed; } set { reviewed = value; } } public bool HasTestSuite { get { return hasTestSuite; } set { hasTestSuite = value; } } private bool reviewed, hasTestSuite; }
5. Define a class called Widget, and use CodeTestAttributes from the previous question to mark that Widget has been reviewed but has no test suite. [CodeTestAttributes(Reviewed=true, HasTestSuite=false)] class Widget { ... }
6. Suppose that Widget from the previous question had a method called LogBug. Could CodeTestAttributes be used to mark only this method? No. CodeTestAttributes can only target whole classes.
37
THIS PAGE INTENTIONALLY LEFT BLANK