Pro
LINQ
Language Integrated Query in VB 2008
Joseph C. Rattz, Jr.
and
Dennis Hayes
Learn to use the power of Microsoft’s
ground-breaking new technology.
Pro LINQ
Language Integrated Query
in VB 2008
All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher.
ISBN-13 (pbk): 978-1-4302-1644-5
ISBN-13 (electronic): 978-1-4302-1645-2
Printed and bound in the United States of America 9 8 7 6 5 4 3 2 1
Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark.
Lead Editor: Ewan Buckingham Technical Editor: Joseph C. Rattz, Jr.
Technical Reviewers: Joseph C. Rattz, Jr., Fabio Ferracchiati
Editorial Board: Clay Andres, Steve Anglin, Mark Beckner, Ewan Buckingham, Tony Campbell, Gary Cornell, Jonathan Gennick, Michelle Lowman, Matthew Moodie, Jeffrey Pepper, Frank Pohlmann, Ben Renow-Clarke, Dominic Shakeshaft, Matt Wade, Tom Welsh Project Manager: Richard Dal Porto
Copy Editor: Heather Lang
Associate Production Director: Kari Brooks-Copony Production Editor: Kelly Winquist
Compositors: Dina Quan, Patrick Cunningham Proofreader: April Eddy
Indexer: Carol Burbo Artist: April Milne
Cover Designer: Kurt Krames
Manufacturing Director: Tom Debolski
Distributed to the book trade worldwide by Springer-Verlag New York, Inc., 233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax 201-348-4505, e-mail kn`ano)ju<olnejcan)o^i*_ki, or visitdppl6++sss*olnejcankjheja*_ki.
For information on translations, please contact Apress directly at 2855 Telegraph Avenue, Suite 600, Berkeley, CA 94705. Phone 510-549-5930, fax 510-549-5939, e-mail ejbk<]lnaoo*_ki, or visit dppl6++sss* ]lnaoo*_ki.
Apress and friends of ED books may be purchased in bulk for academic, corporate, or promotional use. eBook versions and licenses are also available for most titles. For more information, reference our Special Bulk Sales–eBook Licensing web page at dppl6++sss*]lnaoo*_ki+ejbk+^qhgo]hao.
The information in this book is distributed on an “as is” basis, without warranty. Although every precau-tion has been taken in the preparaprecau-tion of this work, neither the author(s) nor Apress shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indi-rectly by the information contained in this work.
in C# 2008. Unfortunately, Art’s health began to fail toward the end of that book, and for a while, it seemed doubtful he would live to see it completed. Not one to give up, Art
over-came numerous hospitalizations and did indeed receive his personally signed copy. Sadly, Art will never see this book because he passed away on February 25, 2009, at the
iv
Contents at a Glance
About the Author
. . . xxiiiAbout the Technical Reviewer
. . . xxvAcknowledgments
. . . .xxviiPART 1
Pro LINQ: Language Integrated Query
in VB.NET 2008
CHAPTER 1
Hello LINQ
. . . 3CHAPTER 2
VB.NET 2008 Language Enhancements for LINQ
. . . 21PART 2
LINQ to Objects
CHAPTER 3
LINQ to Objects Introduction
. . . 81CHAPTER 4
Deferred Operators
. . . 93CHAPTER 5
Nondeferred Operators
. . . 177PART 3
LINQ to XML
CHAPTER 6
LINQ to XML Introduction
. . . 241CHAPTER 7
The LINQ to XML API
. . . 247CHAPTER 8
LINQ to XML Operators
. . . 337v
CHAPTER 10
LINQ to DataSet Operators
. . . 413CHAPTER 11
Additional DataSet Capabilities
. . . 453Part 5
LINQ to SQL
CHAPTER 12
LINQ to SQL Introduction
. . . 463CHAPTER 13
LINQ to SQL Tips and Tools
. . . 477CHAPTER 14
LINQ to SQL Database Operations
. . . 505CHAPTER 15
LINQ to SQL Entity Classes
. . . 553CHAPTER 16
The DataContext
. . . 599CHAPTER 17
Concurrency Conflicts
. . . 663CHAPTER 18
Additional SQL Capabilities
. . . 681vii
Contents
About the Author
. . . xxiiiAbout the Technical Reviewer
. . . xxvAcknowledgments
. . . .xxviiPART 1
Pro LINQ: Language Integrated
Query in VB.NET 2008
CHAPTER 1
Hello LINQ
. . . 3A Paradigm Shift
. . . 4Query XML
. . . 4Query a SQL Server Database
. . . 6Introduction to LINQ
. . . 7LINQ Is About Data Queries
. . . 8Components
. . . 8LINQ to Objects
. . . 8LINQ to XML
. . . 9LINQ to DataSet
. . . 9LINQ to SQL
. . . 9LINQ to Entities
. . . 9How to Obtain LINQ
. . . 9LINQ Is Not Just for Queries
. . . 10Tips to Get You Started
. . . 13Use Dim Without Specifying the Type When Confused
. . . 13Use the Cast or OfType Operators for Legacy Collections
. . . 15Prefer the OfType Operator to the Cast Operator
. . . 16Don’t Assume a Query Is Bug Free
. . . 16Take Advantage of Deferred Queries
. . . 17Use the DataContext Log
. . . 18Use the LINQ Forum
. . . 19CHAPTER 2
VB.NET 2008 Language Enhancements for LINQ
. . . 21New VB.NET 2008 Language Additions
. . . 21New (and Not So New) Options
. . . 22Option Explicit
. . . 22Option Compare
. . . 23Option Strict
. . . 23Option Infer
. . . 24Lambda Expressions
. . . 24Using Named Methods
. . . 25Using Lambda Expressions
. . . 26Expression Trees
. . . 29Keyword Dim, Object Initialization, and Anonymous Types
. . . 30The Implicitly Typed Local Variable Keyword Dim
(with Option Infer On/Option Strict On)
. . . 31Object Initialization Expressions
. . . 33Anonymous Types
. . . 34Extension Methods
. . . 38Instance (Object) vs. Shared (Class) Methods Recap
. . . 38The Problem Solved by Extension Methods
. . . 40The Solution
. . . 41Extension Method Declarations and Invocations
. . . 42Extension Method Precedence
. . . 43Partial Methods
. . . 43A Partial Method Example
. . . 44What Is the Point of Partial Methods?
. . . 46The Rules
. . . 47Query Expressions
. . . 47Query Expression Grammar
. . . 49Query Expression Translation
. . . 51Nullable Value Types
. . . 60XML Enhancements
. . . 61XML Literals
. . . 61XML Embedded Expressions
. . . 64XML Axis Properties
. . . 67XML Namespace Imports
. . . 74PART 2
LINQ to Objects
CHAPTER 3
LINQ to Objects Introduction
. . . 81LINQ to Objects Overview
. . . 81IEnumerable(Of T), Sequences, and the Standard Query Operators
. . . 82Returning IEnumerable(Of T) and Deferred Queries
. . . 83Func Delegates
. . . 87The Standard Query Operators Alphabetical Cross-Reference
. . . 88A Tale of Two Syntaxes
. . . 90Summary
. . . 90CHAPTER 4
Deferred Operators
. . . 93Referenced Namespaces
. . . 93Referenced Assemblies
. . . 93Query Expression Syntax vs. Standard Dot Notation Syntax
. . . 94Common Classes
. . . 94The Deferred Operators by Purpose
. . . 96Restriction
. . . 96Where
. . . 96Projection
. . . 99Select
. . . 99SelectMany
. . . 105Partitioning
. . . 110Take
. . . 110TakeWhile
. . . 113Skip
. . . 115SkipWhile
. . . 116Concatenation
. . . 119Concat
. . . 119Ordering
. . . 122OrderBy
. . . 123OrderByDescending
. . . 129ThenBy
. . . 132ThenByDescending
. . . 137Join
. . . 142Join
. . . 142GroupJoin
. . . 144Grouping
. . . 146GroupBy
. . . 146Set
. . . 152Distinct
. . . 153Union
. . . 154Intersect
. . . 156Except
. . . 157Conversion
. . . 159Cast
. . . 159OfType
. . . 161AsEnumerable
. . . 164Element
. . . 167DefaultIfEmpty
. . . 167Generation
. . . 172Range
. . . 172Repeat
. . . 173Empty
. . . 174Summary
. . . 175CHAPTER 5
Nondeferred Operators
. . . 177Referenced Namespaces
. . . 177Common Classes
. . . 177The Nondeferred Operators by Purpose
. . . 180Conversion
. . . 181ToArray
. . . 181ToList
. . . 183ToDictionary
. . . 185ToLookup
. . . 190Equality
. . . 195SequenceEqual
. . . 195Element
. . . 199First
. . . 199FirstOrDefault
. . . 201Last
. . . 203LastOrDefault
. . . 205SingleOrDefault
. . . 209ElementAt
. . . 211ElementAtOrDefault
. . . 212Quantifiers
. . . 213Any
. . . 214All
. . . 216Contains
. . . 217Aggregate
. . . 220Count
. . . 220LongCount
. . . 222Sum
. . . 224Min
. . . 226Max
. . . 229Average
. . . 232Aggregate
. . . 234Summary
. . . 238PART 3
LINQ to XML
CHAPTER 6
LINQ to XML Introduction
. . . 241Additional Advantages of LINQ to XML
. . . 244Cheating the W3C DOM XML API
. . . 244Summary
. . . 246CHAPTER 7
The LINQ to XML API
. . . 247Referenced Namespaces
. . . 247Significant API Design Enhancements
. . . 248XML Tree Construction Simplified with
Functional Construction
. . . 248Document Centricity Eliminated in Favor of
Element Centricity
. . . 251Names, Namespaces, and Prefixes
. . . 253Node Value Extraction
. . . 255The LINQ to XML Object Model
. . . 258XML Creation
. . . 262Creating XML with XML Literals
. . . 263Creating Elements with XElement
. . . 263Creating Attributes with XAttribute
. . . 266Creating Comments with XComment
. . . 267Creating Containers with XContainer
. . . 268Creating Declarations with XDeclaration
. . . 268Creating Document Types with XDocumentType
. . . 269Creating Documents with XDocument
. . . 270Creating Names with XName
. . . 271Creating Namespaces with XNamespace
. . . 272Creating Nodes with XNode
. . . 272Creating Processing Instructions with XProcessingInstruction
. . . 273Creating Streaming Elements with XStreamingElement
. . . 275Creating Text with XText
. . . 276Creating CData with XCData
. . . 277XML Output
. . . 277Saving with XDocument.Save()
. . . 277Saving with XElement.Save()
. . . 279XML Input
. . . 280Loading with XDocument.Load()
. . . 280Loading with XElement.Load()
. . . 282Parsing with XDocument.Parse() or XElement.Parse()
. . . 282XML Traversal
. . . 283Traversal Properties
. . . 284Forward with XNode.NextNode
. . . 284Backward with XNode.PreviousNode
. . . 285Up to Document with XObject.Document
. . . 286Up with XObject.Parent
. . . 287Traversal Methods
. . . 288Down with XContainer.Nodes()
. . . 288Down with XContainer.Elements()
. . . 292Down with XContainer.Element()
. . . 294Up Recursively with XNode.Ancestors()
. . . 295Up Recursively with XElement.AncestorsAndSelf()
. . . 296Down Recursively with XContainer.Descendants()
. . . 297Down Recursively with XElement.DescendantsAndSelf()
. . . 298Forward with XNode.NodesAfterSelf()
. . . 298Forward with XNode.ElementsAfterSelf()
. . . 300Backward with XNode.NodesBeforeSelf()
. . . 300XML Modification
. . . 302Adding Nodes
. . . 303XContainer.Add() (AddLast)
. . . 303XContainer.AddFirst()
. . . 304XNode.AddBeforeSelf()
. . . 305XNode.AddAfterSelf()
. . . 307Deleting Nodes
. . . 308XNode.Remove()
. . . 308IEnumerable(Of T).Remove()
. . . 309XElement.RemoveAll()
. . . 310Updating Nodes
. . . 311XElement.Value on XElement Objects, XText.Value
on XText Objects, and XComment.Value on
XComment Objects
. . . 311XDocumentType.Name, XDocumentType.PublicId,
XDocumentType.SystemId, and XDocumentType.
InternalSubset on XDocumentType Objects
. . . 313XProcessingInstruction.Target on XProcessingInstruction
Objects and XProcessingInstruction.Data on
XProcessingInstruction Objects
. . . 313XElement.ReplaceAll()
. . . 314XElement.SetElementValue() on Child XElement Objects
. . . 315XML Attributes
. . . 317Attribute Creation
. . . 317Attribute Traversal
. . . 317Forward with XElement.FirstAttribute
. . . 317Forward with XAttribute.NextAttribute
. . . 318Backward with XAttribute.PreviousAttribute
. . . 318Backward with XElement.LastAttribute
. . . 319XElement.Attribute()
. . . 319XElement.Attributes()
. . . 320Attribute Modification
. . . 321Adding Attributes
. . . 321Deleting Attributes
. . . 321Updating Attributes
. . . 324XElement.SetAttributeValue()
. . . 325XML Annotations
. . . 327Adding Annotations with XObject.AddAnnotation()
. . . 327Removing Annotations with XObject.RemoveAnnotations()
. . . 327Annotations Example
. . . 328XML Events
. . . 331XObject.Changing
. . . 331XObject.Changed
. . . 332An Event Example
. . . 332Trick or Treat, or Undefined?
. . . 335Summary
. . . 335CHAPTER 8
LINQ to XML Operators
. . . 337Introduction to LINQ to XML Operators
. . . 337Ancestors
. . . 338Declarations
. . . 338Examples
. . . 339AncestorsAndSelf
. . . 343Declarations
. . . 343Examples
. . . 344Attributes
. . . 346Declarations
. . . 346Examples
. . . 347DescendantNodes
. . . 348Declarations
. . . 349Examples
. . . 349DescendantNodesAndSelf
. . . 350Declarations
. . . 350Examples
. . . 351Descendants
. . . 352Declarations
. . . 352Examples
. . . 352DescendantsAndSelf
. . . 354Declarations
. . . 355Examples
. . . 355Elements
. . . 357Declarations
. . . 358Examples
. . . 358InDocumentOrder
. . . 360Declarations
. . . 360Nodes
. . . 362Declarations
. . . 362Examples
. . . 362Remove
. . . 363Declarations
. . . 363Examples
. . . 364Summary
. . . 366CHAPTER 9
Additional XML Capabilities
. . . 369Referenced Namespaces
. . . 369Queries
. . . 370No Reaching
. . . 370A Complex Query
. . . 373Transformations
. . . 379Transformations Using XSLT
. . . 379Transformations Using Functional Construction
. . . 381Tips On XML Transformations Using LINQ
. . . 384Simplify Complex Tasks with Helper Methods
. . . 384Suppressing Node Construction with Nothing
. . . 385Handling Multiple Peer Nodes While Remaining Flat
. . . 388Validation
. . . 391The Extension Methods
. . . 391Declarations
. . . 391Obtaining an XML Schema
. . . 393Examples
. . . 395XPath
. . . 406Declarations
. . . 406Examples
. . . 408Summary
. . . 409PART 4
LINQ to DataSet
CHAPTER 10
LINQ to DataSet Operators
. . . 413Assembly References
. . . 414Referenced Namespaces
. . . 414DataRow Set Operators
. . . 416Distinct
. . . 416Declarations
. . . 416Examples
. . . 417Except
. . . 420Declarations
. . . 421Examples
. . . 421Intersect
. . . 423Declarations
. . . 423Examples
. . . 423Union
. . . 425Declarations
. . . 425Examples
. . . 426SequenceEqual
. . . 427Declarations
. . . 428Examples
. . . 428DataRow Field Operators
. . . 429Field(Of T)
. . . 435Declarations
. . . 435Examples
. . . 436SetField(Of T)
. . . 440Declarations
. . . 440Examples
. . . 441DataTable Operators
. . . 444AsEnumerable
. . . 444Declarations
. . . 444Examples
. . . 444CopyToDataTable(Of DataRow)
. . . 445Declarations
. . . 445Examples
. . . 446Summary
. . . 451CHAPTER 11
Additional DataSet Capabilities
. . . 453Required Namespaces
. . . 453Typed DataSets
. . . 453Putting It All Together
. . . 455PART 5
LINQ to SQL
CHAPTER 12
LINQ to SQL Introduction
. . . 463Introducing LINQ to SQL
. . . 464The DataContext
. . . 466Entity Classes
. . . 466Associations
. . . 467Concurrency Conflict Detection
. . . 468Concurrency Conflict Resolution
. . . 468Prerequisites for Running the Examples
. . . 469Obtaining the Appropriate Version of the Northwind Database
. . . 469Generating the Northwind Entity Classes
. . . 469Generating the Northwind XML Mapping File
. . . 471Importing the Generated Code
. . . 471Using the LINQ to SQL API
. . . 472IQueryable(Of T)
. . . 472Some Common Methods
. . . 473GetStringFromDb()
. . . 473ExecuteStatementInDb()
. . . 474Summary
. . . 474CHAPTER 13
LINQ to SQL Tips and Tools
. . . 477Introduction to LINQ to SQL Tips and Tools
. . . 477Tips
. . . 478Use the DataContext.Log Property
. . . 478Use the GetChangeSet() Method
. . . 479Consider Using Partial Classes or Mapping Files
. . . 479Consider Using Partial Methods
. . . 480Tools
. . . 480SqlMetal
. . . 480XML Mapping File vs. DBML Intermediate File
. . . 485Working with DBML Intermediate Files
. . . 486The Object Relational Designer
. . . 487Creating Your LINQ to SQL Classes File
. . . 487Connecting the DataContext to the Database
. . . 489Adding an Entity Class
. . . 490Adding Objects to the Entity Class Model
. . . 494Overriding the Insert, Update, and Delete Methods
. . . 495Use SqlMetal and the O/R Designer Together
. . . 502Summary
. . . 503CHAPTER 14
LINQ to SQL Database Operations
. . . 505Prerequisites for Running the Examples
. . . 505Some Common Methods
. . . 505Using the LINQ to SQL API
. . . 506Standard Database Operations
. . . 506Inserts
. . . 506Inserting Attached Entity Objects
. . . 509Queries
. . . 511Exceptions to the Norm
. . . 512Associations
. . . 514Joins
. . . 527Deferred Query Execution
. . . 532The SQL IN Statement with the Contains Operator
. . . 536Updates
. . . 537Updating Associated Classes
. . . 537Deletes
. . . 541Deleting Attached Entity Objects
. . . 543Deleting Relationships
. . . 543Overriding Database Modification Statements
. . . 544Overriding the Insert Method
. . . 545Overriding the Update Method
. . . 545Overriding the Delete Method
. . . 545Example
. . . 545Overriding in the Object Relational Designer
. . . 548Considerations
. . . 548SQL Translation
. . . 549Summary
. . . 551CHAPTER 15
LINQ to SQL Entity Classes
. . . 553Prerequisites for Running the Examples
. . . 553Entity Classes
. . . 553Creating Entity Classes
. . . 553Generating Entity Classes
. . . 554Writing Entity Classes By Hand
. . . 554XML External Mapping File Schema
. . . 583Projecting into Entity Classes vs. Nonentity Classes
. . . 583Prefer Object Initialization to Parameterized
Construction When Projecting
. . . 586Extending Entity Classes with Partial Methods
. . . 589Important System.Data.Linq API Classes
. . . 591EntitySet(Of T)
. . . 591EntityRef(Of T)
. . . 592Entity
. . . 592HasLoadedOrAssignedValue
. . . 593Table(Of T)
. . . 594IExecuteResult
. . . 594ReturnValue
. . . 595GetParameterValue
. . . 595ISingleResult(Of T)
. . . 595ReturnValue
. . . 596IMultipleResults
. . . 596ReturnValue
. . . 597GetResult(Of T)
. . . 597Summary
. . . 598CHAPTER 16
The DataContext
. . . 599Prerequisites for Running the Examples
. . . 599Some Common Methods
. . . 599Using the LINQ to SQL API
. . . 599[Your]DataContext Class
. . . 599The DataContext Class
. . . 600The DataContext Class Implements IDisposable
. . . 603Primary Purposes
. . . 603Identity Tracking
. . . 603Change Tracking
. . . 609Change Processing
. . . 610The DataContext Lifetime
. . . 610DataContext() and [Your]DataContext()
. . . 611Declarations
. . . 611Examples
. . . 612Creating Mapping Sources
. . . 624SubmitChanges()
. . . 624Declarations
. . . 624DatabaseExists()
. . . 631Declarations
. . . 632Examples
. . . 632CreateDatabase()
. . . 632Declarations
. . . 633Examples
. . . 633DeleteDatabase()
. . . 634Declarations
. . . 634Examples
. . . 634CreateMethodCallQuery()
. . . 635Declarations
. . . 635Examples
. . . 635ExecuteQuery()
. . . 637Declarations
. . . 637Examples
. . . 637Translate()
. . . 639Declarations
. . . 639Examples
. . . 640ExecuteCommand()
. . . 641Declarations
. . . 641Examples
. . . 641ExecuteMethodCall()
. . . 642Declarations
. . . 642Examples
. . . 643GetCommand()
. . . 650Declarations
. . . 650Examples
. . . 651GetChangeSet()
. . . 652Declarations
. . . 652Examples
. . . 652GetTable()
. . . 654Declarations
. . . 654Examples
. . . 654Refresh()
. . . 655Declarations
. . . 655Examples
. . . 656CHAPTER 17
Concurrency Conflicts
. . . 663Prerequisites for Running the Examples
. . . 663Some Common Methods
. . . 663Using the LINQ to SQL API
. . . 663Concurrency Conflicts
. . . 663Optimistic Concurrency
. . . 664Conflict Detection
. . . 664Conflict Resolution
. . . 668Pessimistic Concurrency
. . . 675An Alternative Approach for N-Tier Architectures
. . . 678Summary
. . . 680CHAPTER 18
Additional SQL Capabilities
. . . 681Prerequisites for Running the Examples
. . . 681Using the LINQ to SQL API
. . . 681Using the LINQ to XML API
. . . 681Database Views
. . . 681Entity Class Inheritance
. . . 683Transactions
. . . 689Summary
. . . 691xxiii
About the Author
JOSEPH C. RATTZ, JR. unknowingly began his career in software development in 1990 when a friend asked him for assistance writing an ANSI text editor named ANSI Master for the Com-modore Amiga. A hangman game (The Gallows) soon followed. From these compiled Basic programs, he moved on to programming in C for more speed and power. Joe then developed applications that were sold to JumpDisk, an Amiga disk magazine, as well as Amiga World
magazine. Due to developing in a small town on a fairly isolated platform, Joe learned all the wrong ways to write code. It was while trying to upgrade his poorly written applications that he gained respect for the importance of easily maintainable code. It was love at first sight when Joe spotted a source-level debugger in use for the first time.
Two years later, Joe obtained his first software development opportunity at Policy Management Systems Corporation as an entry-level programmer developing a client/server insurance application for OS/2 and Presentation Manager. Through the years, he added C++, Unix, Java, ASP, ASP.NET, C#, HTML, DHTML, and XML to his skill set while develop-ing applications for SCT, DocuCorp, IBM and the Atlanta Committee for the Olympic Games, CheckFree, NCR, EDS, Delta Technology, Radiant Systems, and the Genuine Parts Company. Joe enjoys the creative aspects of user interface design, and he appreciates the discipline nec-essary for server-side development. But, given his druthers, his favorite development pastime is debugging code.
Joe can be found working for the Genuine Parts Company—the parent company of NAPA—in the Automotive Parts Group Information Systems department, where he works on his baby, the Storefront web site. This site for NAPA stores provides a view into their accounts and data on a network of AS/400s.
Joe’s first book, Pro LINQ: Language Integrated Query in C# 2008, was published on November 19, 2007, which is the exact same date that Visual Studio 2008 was released to man-ufacturing. One final LINQ coincidence is that Visual Studio 2008 was officially launched on February 27, 2008, which just happens to be Joe’s birthday.
xxv
About the Technical Reviewer
xxvii
Acknowledgments
W
ell, it appears that it is time to write the acknowledgments for another book. Readers ofPro LINQ: Language Integrated Query in C# 2008 may recall the endless delays in publishing that book. A project that initially started as a 9-month project grew into a 17-month project. This book turned out to be no less challenging in terms of scheduling and delays. If you were one of those who patiently waited for this book to be published, I want to thank you for your patience. I hope you find it worth the wait.
When I wrote the acknowledgments for my C# LINQ book, I expected that to be my single opportunity to show my appreciation to all those people who made the book possible. I did not imagine that I would have another opportunity to write acknowledgments, so I spilled my guts on the first one. Obviously, most of the people who made the C# book possible also made this one possible. The beauty of writing this book’s acknowledgments is that I have the luxury of getting to correct any errors of omission that occurred in the first book’s Acknowledgments. The first person that I want to thank is my wife, Vickey. I mentioned in the first book that she had to do many of my chores during the 17 months I worked on it. Then, a mere 7 months after it was published, I was engaged in yet another book—for a year. She has waited more than long enough for us to resume a normal life. I have promised her that I will straighten the basement before I begin any more books. So this may be my last.
I would like to thank Apress for another opportunity to write a book. Specifically, I want to thank Ewan Buckingham. I can’t help but think I have taken years off his life. Richard Dal Porto performed project management duties for this book and with chapters flying about, it is amazing that he could keep it all straight. Heather Lang handled the role of copy editor. She also took over the reins as copy editor on my C# LINQ book, so I have been twice privileged to have her make my writing look coherent, well-formed, and written in a language consist-ing of characters other than commas. Sadly, I still haven’t learned when to use commas. Kelly Winquist was the production editor for this book, and I certainly appreciate her and her team’s effort, which is apparent when you get to see the finished product. I turn in Microsoft Word documents, and a book comes out on the other side. It is a fabulous machine! Thank you April Eddy for proofreading the book once I was finished with it. I know there are unnamed others who worked on this book, and while you don’t get to see your name here, you get the more valuable benefit of probably never having had an interaction with me. Kudos to you too!
Next, I must extend my appreciation to Fabio Ferracchiati. Not only did he perform the technical reviewer duties, he filled in as stunt author. This book still might not be finished were it not for his assistance.
I want to thank the Microsoft VB language team for wonderful LINQ support being added to the language. Your VB.NET XML enhancements are enough to make this C# programmer envious. I hope the C# design team was paying attention.
Again, I must thank my parents: my dad for giving me the confidence to think I could actually write a book, and my mom for her constant input of ideas.
Last, I want to thank all the readers of the C# LINQ book who sent in comments, sug-gestions, and corrections. Specifically, I want to thank Jon Skeet and Judson White for their much-appreciated effort. Both of these fine developers took the time to send me detailed notes. This book is better because of their efforts.
3
Hello LINQ
Listing 1-1. Hello LINQ
KlpekjAtlhe_epKj KlpekjEjbanKj KlpekjOpne_pKj
EilknpoOuopai EilknpoOuopai*Hejm
Ik`qhaIk`qha-Oq^I]ej$%
@eicnaapejco$%=oOpnejc9wdahhksknh`(dahhkHEJM(dahhk=lnaooy
@eiepaio9[
BnkioEjcnaapejco[ Sdanao*Aj`oSepd$HEJM%[ Oaha_po
BknA]_depaiEjepaio ?kjokha*SnepaHeja$epai% Jatpepai
Aj`Oq^ Aj`Ik`qha
Note
The code in Listing 1-1 is a complete console program created in Visual Studio 2008. In most places, I will leave out the Ik`qha,Oq^I]ej, and common Eilknpo statements, but I will include any unusualEilknpo that an example requires.dahhkHEJM
Note
Also note that, in most cases, you do not need to have the common Eilknpo statements in your code, because VB.NET adds them behind the scenes at compile time. You can see (and modify) the list of namespaces added by looking in the project’s Properties, on the Reference tab, in the “Imported namespaces” table.A Paradigm Shift
Did you just feel your world shift? As a .NET developer, you should have. With the trivial pro-gramming example in Listing 1-1, you just ran what somewhat appears to be a Structured Query Language (SQL) query on an array of strings.1 Check out that sdana clause. Did you
notice that I used the Aj`oSepd method of a Opnejc object? You may be wondering why, with
KlpekjOpne_pKj, an error was not generated when I declared epaio without a type or when I used epai as a variable in the BknA]_d loop without giving it a type. To those used to pro-gramming in VB6 or VB.NET with KlpekjAtlhe_ep and KlpekjOpne_p set toKbb, declaring a variable without giving it a type may not seem like a big deal. But with KlpekjOpne_pKj, it is. Explaining why we do not get a compilation error from this would be a distraction right now, but I will pick up this idea again at the start of Chapter 2. So, what features of VB.NET are allowing all of this? The answer is Microsoft’s Language Integrated Query, otherwise known as LINQ.
Query XML
While the example in Listing 1-1 is trivial, the example in Listing 1-2 may begin to indicate the potential power that LINQ puts into the hands of the .NET developer. It displays the ease with which one can interact with and query Extensible Markup Language (XML) data utilizing the LINQ to XML API. You should pay particular attention to how I construct the XML data into an object named ^kkgo that I can programmatically interact with.
Listing 1-2. A Simple XML Query Using LINQ to XML
EilknpoOuopai EilknpoOuopai*Hejm EilknpoOuopai*Tih*Hejm
@ei^kkgo=oTAhaiajp9[ 8^kkgo:
8^kkg:
8pepha:LnkHEJM6H]jcq]caEjpacn]pa`MqanuejR>*JAP.,,48+pepha: 8]qpdkn:@ajjeoD]uao8+]qpdkn:
8+^kkg: 8^kkg:
8pepha:LnkSB6Sej`ksoSkngbhksej*JAP/*18+pepha: 8]qpdkn:>nq_a>qgkre_o8+]qpdkn:
8+^kkg: 8^kkg:
8pepha:Lnk?.,,4]j`pda*JAP/*1Lh]pbkni(BkqnpdA`epekj8+pepha: 8]qpdkn:=j`nasPnkahoaj8+]qpdkn:
8+^kkg: 8+^kkgo:
#?Opn_]opjkpjaa`a`ebKlpekjOpne_peokbb @eipephao9[
Bnki^kkgEj^kkgo*Ahaiajpo$^kkg%[
Sdana?Opn$^kkg*Ahaiajp$]qpdkn%%9@ajjeoD]uao[ Oaha_p^kkg*Ahaiajp$pepha%
BknA]_dpephaEjpephao
?kjokha*SnepaHeja$pepha*R]hqa% Jatppepha
Note
The code in Listing 1-2 requires adding the Ouopai*Tih*Hejm*`hh assembly to the project refer-ences if it is not already added. Also notice that I added an Eilknpo statement for the Ouopai*Tih*Hejmnamespace.
Running the previous code by pressing Ctrl+F5 outputs the following data to the console window:
LnkHEJM6H]jcq]caEjpacn]pa`MqanuejR>*JAP.,,4
Did you notice how I simply loaded the XML data into an object of type TAhaiajp? Among the new features in VB.NET 2008 are XML literals, where a block of XML can be assigned to an XML element. I will cover this in more detail in Chapter 2. Also, LINQ to XML expands the XML API with its richer API. While the W3C Document Object Model (DOM) XML API requires
Tih@k_qiajp-centric development, LINQ to XML allows the developer to interact at the ele-ment level using the TAhaiajp class. The LINQ to XML API will be covered in Chapter 7.
Again notice that I utilized the same SQL-like syntax to query the XML data, as though it were a database.
Query a SQL Server Database
My next example shows how to use LINQ to SQL to query database2 tables. In Listing 1-3, I
query the standard Microsoft Northwind sample database.
Listing 1-3. A Simple Database Query Using LINQ to SQL
EilknpoOuopai EilknpoOuopai*Hejm EilknpoOuopai*@]p]*Hejm
Eilknpo?d]lpan-*jsej`
@ei`^=oJasJknpdsej`$[
@]p]Okqn_a9*XOMHATLNAOO7Ejepe]h?]p]hkc9Jknpdsej`7Ejpacn]pa`Oa_qnepu9OOLE7%
@ei_qopo9[
Bnki_Ej`^*?qopkiano[
Sdana_*?epu9Nek`aF]jaenk[ Oaha_p_
BknA]_d_qopEj_qopo
?kjokha*SnepaHeja$w,y(_qop*?kil]juJ]ia% Jatp_qop
Note
The code in Listing 1-3 requires adding the Ouopai*@]p]*Hejm*`hh assembly to the project refer-ences if it is not already added. Also notice that I added an Eilknpo statement for the Ouopai*@]p]*Hejmnamespace.
When working with the files generated by SqlMetal, you need to use namespaces and Eilknpo state-ments that are appropriate for the way to set up your project. Depending on how you set up your project, you may need to include the project’s default namespace, such as Eilknpo?d]lpan-*jsej`, just the gener-ated namespace, Eilknpojsej`, or you may not need any Eilknpo statement at all. I cover this in more detail in Chapter 2.
You can see that I added an Eilknpo statement for the ?d]lpan-*jsej` namespace. For this example to work, you must use the SqlMetal command-line utility, or the Object Relational Designer, to generate entity classes for the targeted database, which in this example is the Microsoft Northwind sample database. See Chapter 12 to read how this is done with SqlMetal.
The generated entity classes are created in the jsej` namespace, which I specify when gen-erating them. I then add the SqlMetal generated source module to my project and add the
Eilknpo statement for the jsej` namespace.
Note
You may need to change the connection string that is passed to the Jknpdsej` constructor in Listing 1-3 for the connection to be properly made. Read the section on @]p]?kjpatp$% andWUkqnY@]p]?kjpatp$% in Chapter 16 to see different ways to connect to the database.
Running the previous code by pressing Ctrl+F5 outputs the following data to the console window:
D]j]ne?]njao Mqa@ah_e] Ne_]n`k=`k_e_]`ko
This simple example demonstrates querying the Customers table of the Northwind data-base for customers in Rio de Janeiro. While it may appear that there is nothing new or special going on here that I wouldn’t already have with existing means, there are some significant differences. Most noticeably, this query is integrated into the language, and this means I get language-level support, which includes syntax checking and IntelliSense. Gone will be the days of writing a SQL query into a string and not detecting a syntax error until runtime. Want to make your sdana clause dependent on a field in the Customers table but you cannot remember the name of the field? IntelliSense will show the table’s fields to you. Once you type
c.in the previous example, IntelliSense will display all the fields of the Customers table to you. All of the previous queries use the query expression syntax. You will learn in Chapter 2 that there are two syntaxes available for LINQ queries, of which the query expression syntax is one. Of course, you may always use the standard dot notation syntax that you are accustomed to seeing in .NET instead. This syntax is the normal k^fa_p*iapdk`$% invocation pattern you have always been using.
Introduction to LINQ
As the Microsoft .NET platform, and its supporting languages C# and VB.NET, have matured, it has become apparent that one of the more troublesome areas still remaining for developers is that of accessing data from different data sources. In particular, database access and XML manipulation are often cumbersome at best, and problematic in the worst cases.
unanticipated error behavior. Having database code that is not validated at compile time can certainly lead to this problem.
A second problem is the nuisance caused by the differing data types utilized by a par-ticular data domain, such as database or XML data types vs. the native language in which the program is written. In particular, dates and times can be quite a hassle.
XML parsing, iterating, and manipulation can be quite tedious. Often an XML fragment is all that is desired, but due to the W3C DOM XML API, an Tih@k_qiajp must be created just to perform various operations on the XML fragment.
Rather than just add more classes and methods to address these deficiencies in a piece-meal fashion, the development team at Microsoft decided to go one step further by abstracting the fundamentals of data query from these particular data domains. The result was LINQ. LINQ is Microsoft’s technology to provide a language-level support mechanism for querying data of all types. These types include in-memory arrays and collections, databases, XML docu-ments, and more.
LINQ Is About Data Queries
For the most part, LINQ is all about queries, whether they are queries returning a set of matching objects, a single object, or a subset of fields from an object or set of objects. In LINQ, this returned set of objects is called a sequence. Most LINQ sequences are of type
EAjqian]^ha$KbP%, where P is the data type of the objects stored in the sequence. For example, if you have a sequence of integers, they would be stored in a variable of type EAjqian]^ha$Kb Ejpacan%. You will see that EAjqian]^ha$KbP% runs rampant in LINQ. Many of the LINQ meth-ods return an EAjqian]^ha$KbP%.
In the previous examples, all of the queries actually return an EAjqian]^ha$KbP% or a type that inherits from EAjqian]^ha$KbP%. However, I declare the variables without types for the sake of simplicity at this point. This is unrelated to the old VB6 R]ne]jp or to how VB.NET has handled untyped variables prior to VB.NET 2008, because these variables are given a specific type by VB.NET. I will cover this in detail in Chapter 2, where you will see examples that begin demonstrating that sequences are truly stored in variables implementing the EAjqian]^ha$Kb P% interface.
Components
Because LINQ is so powerful, you should expect to see a lot of systems and products become LINQ compatible. Virtually any data store would make a good candidate for supporting LINQ queries. This includes databases, Microsoft’s Active Directory, the registry, the file system, an Excel file, and so on.
Microsoft has already identified the components in this section as necessary for LINQ. There is no doubt that there will be more to come.
LINQ to Objects
LINQ to XML
LINQ to XML is the name given to the LINQ API dedicated to working with XML. This interface was known as XLinq in older prereleases of LINQ. Not only has Microsoft added the neces-sary XML libraries to work with LINQ, it has addressed other deficiencies in the standard XML DOM, thereby making it easier than ever to work with XML. Gone are the days of having to cre-ate an Tih@k_qiajp just to work with a small piece of XML. To take advantage of LINQ to XML, you must have a reference to the Ouopai*Tih*Hejm*`hh assembly in your project and have an
Eilknpo statement such as the following:
EilknpoOuopai*Tih*Hejm
LINQ to DataSet
LINQ to DataSet is the name given to the LINQ API for DataSets. Many developers have a lot of existing code relying on DataSets. Those who do will not be left behind, nor will they need to rewrite their code to take advantage of the power of LINQ.
LINQ to SQL
LINQ to SQL is the name given to the EMqanu]^ha$KbP% API that allows LINQ queries to work with Microsoft’s SQL Server database. This interface was known as DLinq in older prereleases of LINQ. To take advantage of LINQ to SQL, you must have a reference to the Ouopai*@]p]* Hejm*`hh assembly in your project and have anEilknpo statement such as the following:
EilknpoOuopai*@]p]*Hejm
LINQ to Entities
LINQ to Entities is an alternative LINQ API that is used to interface with a database using the ADO.NET Entity Framework. It decouples the entity object model from the physical database by injecting a logical mapping between the two. With this decoupling comes increased power and flexibility, as well as complexity. Because LINQ to Entities appears to be outside the core LINQ framework, it is not covered in this book. However, if you find that you need more flexi-bility than LINQ to SQL permits, it would be worth considering as an alternative. Specifically, if you need looser coupling between your entity object model and database, entity objects com-posed of data coming from multiple tables, or more flexibility in modeling your entity objects, LINQ to Entities may be your answer.
How to Obtain LINQ
LINQ Is Not Just for Queries
Maybe by definition you could say LINQ is just for queries because it stands for Language Inte-grated Query. But please don’t think of it only in that context. Its power transcends mere data queries. I prefer to think of LINQ as a data iteration engine, but perhaps Microsoft didn’t want a technology named DIE.
Have you ever called a method and it returned data back in some data structure that you then needed to convert to yet another data structure before you could pass it to another method? Let’s say for example you call method A, and method A returns an array of type
Opnejc that contains numeric values stored as strings. You then need to call method B, but method B requires an array of integers. You normally end up writing a loop to iterate through the array of strings and populate a newly constructed array of integers. What a nuisance. Allow me to point out the power of Microsoft’s LINQ.
Let’s pretend I have an array of strings that I received from some method A, as shown in Listing 1-4.
Listing 1-4. Converting an Array of Strings to Integers
@eijqi^ano$%=oOpnejc9w,,0.(,-,(5(.3y
For this example, I’ll just statically declare an array of strings. Now before I call method B, I need to convert the array of strings to an array of integers:
@eijqio$%=oEjpacan9jqi^ano*Oaha_p$Bqj_pekj$o%Ejp/.*L]noa$o%%*Pk=nn]u$%
That’s it. How much easier could it get? Even just saying “abracadabra” only saves you 65 characters. Here is some code to display the resulting array of integers:
BknA]_djqi=oEjpacanEjjqio ?kjokha*SnepaHeja$jqi% Jatpjqi
Here is the output showing the integers:
0. -, 5 .3
I know what you are thinking: maybe I just trimmed off the leading zeros. If I sort it, will you then be convinced? If they were still strings, 9 would be at the end, and 10 would be first. Listing 1-5 contains some code to do the conversion and sort the output.
Listing 1-5. Converting an Array of Strings to Integers and Sorting It
@eijqi^ano$%=oOpnejc9w,,0.(,-,(5(.3y
@eijqio$%=oEjpacan9[
BknA]_djqi=oEjpacanEjjqio ?kjokha*SnepaHeja$jqi% Jatpjqi
Here are the results:
5 -, .3 0.
How slick is that? “OK,” you say, “that is nice, but it sure is a simple example.” Now, I’ll give you a more complex example.
Let’s say you have some common code that contains an Ailhkuaa class. In that Ailhkuaa
class is a method to return all the employees. Also assume you have another code base of com-mon code that contains a ?kjp]_p class, and in that class is a method to publish contacts. Let’s assume you have the assignment to publish all employees as contacts.
The task seems simple enough, but there is a catch. The common Ailhkuaa method that retrieves the employees returns the employees in an =nn]uHeop of Ailhkuaa objects, and the
?kjp]_p method that publishes contacts requires an array of type ?kjp]_p. Here is that com-mon code:
J]iaol]_aHEJM@ar*DN Lq^he_?h]ooAilhkuaa Lq^he_e`=oEjpacan Lq^he_benopJ]ia=oOpnejc Lq^he_h]opJ]ia=oOpnejc
Lq^he_Od]na`Bqj_pekjCapAilhkuaao$%=o=nn]uHeop
#Kb_kqnoapdana]h_k`askqh`lnk^]^hu^ai]gejc]`]p]^]oamqanu #necdp]^kqpdana*
@ei]h=oJas=nn]uHeop$%
#I]j(`kpdajasR>*JAPk^fa_pejepe]hev]pekjba]pqnaoi]gapdeo #]oj]l*
]h*=``$JasAilhkuaaSepdw[
*e`9-(*benopJ]ia9@ajjeo(*h]opJ]ia9D]uaoy% ]h*=``$JasAilhkuaaSepdw[
*e`9.(*benopJ]ia9Sehhe]i(*h]opJ]ia9C]paoy% ]h*=``$JasAilhkuaaSepdw[
*e`9/(*benopJ]ia9=j`ano(*h]opJ]ia9Dafho^ancy%
J]iaol]_aHEJM@ar*?kiikj Lq^he_?h]oo?kjp]_p Lq^he_E`=oEjpacan Lq^he_J]ia=oOpnejc
Lq^he_Od]na`Oq^Lq^heod?kjp]_po$>uR]h_kjp]_po$%=o?kjp]_p% #Pdeolq^heodiapdk`fqopsnepaopdaipkpda_kjokhasej`ks* BknA]_d_=o?kjp]_pEj_kjp]_po
?kjokha*SnepaHeja$?kjp]_pE`6w,y?kjp]_p6w-y(_*E`(_*J]ia% Jatp_
Aj`Oq^ Aj`?h]oo Aj`J]iaol]_a
As you can see, the Ailhkuaa class and CapAilhkuaao method are in one namespace,
HEJM@ar*DN, and the CapAilhkuaao method returns an =nn]uHeop. The Lq^heod?kjp]_po method is in another namespace, HEJM@ar*?kiikj, and requires an array of ?kjp]_p objects to be passed.
Previously, this always meant iterating through the =nn]uHeop returned by the
CapAilhkuaao method and creating a new array of type ?kjp]_p to be passed to the
Lq^heod?kjp]_po method. LINQ makes it easy, as shown in Listing 1-6.
Listing 1-6. Calling the Common Code
@ei]hAilhkuaao=o=nn]uHeop9HEJM@ar*DN*Ailhkuaa*CapAilhkuaao$%
@ei_kjp]_po$%=oHEJM@ar*?kiikj*?kjp]_p9[ ]hAilhkuaao[
*?]op$KbHEJM@ar*DN*Ailhkuaa%$%[
*Oaha_p$Bqj_pekj$a%JasHEJM@ar*?kiikj*?kjp]_p[ Sepdw[
*E`9a*e`([
*J]ia9Opnejc*Bkni]p$w,yw-y(a*benopJ]ia(a*h]opJ]ia%[y%[ *Pk=nn]u$%
HEJM@ar*?kiikj*?kjp]_p*Lq^heod?kjp]_po$_kjp]_po%
To convert the =nn]uHeop of Ailhkuaa objects to an array of ?kjp]_p objects, I first cast the
=nn]uHeop of Ailhkuaa objects to an EAjqian]^ha$KbAilhkuaa% sequence using the ?]op Stan-dard Query Operator. This is necessary because the legacy =nn]uHeop collection class was used. Syntactically speaking, objects of the Ouopai*K^fa_p class type are stored in an =nn]uHeop, not objects of the Ailhkuaa class type. So I must cast them to Ailhkuaa objects. Had the
CapAilhkuaao method returned a generic Heop collection, this would not have been necessary. However, that collection type was not available when this legacy code was written.
Next, I call the Oaha_p operator on the returned sequence of Ailhkuaa objects and in the
A lambda expression is a new VB.NET 2008 feature that allows new shorthand for specifying anonymous methods that I will explain in Chapter 2. Last, I convert the sequence of newly constructed?kjp]_p objects to an array of ?kjp]_p objects using the Pk=nn]u operator because that is what the Lq^heod?kjp]_po method requires. Isn’t that slick? Here are the results:
?kjp]_pE`6-?kjp]_p6@ajjeoD]uao ?kjp]_pE`6.?kjp]_p6Sehhe]iC]pao ?kjp]_pE`6/?kjp]_p6=j`anoDafho^anc
As you can see, LINQ can do a lot besides just querying data. As you read through the chapters of this book, try to think of additional uses for the features LINQ provides.
Tips to Get You Started
While working with LINQ to write this book, I often found myself confused, befuddled, and stuck. While there are many very useful resources available to the developer wanting to learn to use LINQ to its fullest potential, I want to offer a few tips to get you started. In some ways, these tips feel like they should come at the end of the book. After all, I haven’t even explained what some of these concepts are at this point. But it would seem a bit sadistic to make you read the full text of the book first, only to offer the tips at the end. So with that said, this section contains some tips I think you might find useful, even if you do not fully understand them or the context.
Use Dim Without Specifying the Type When Confused
While it is necessary to use the @ei keyword without specifying a type when capturing a sequence of anonymous classes to a variable, sometimes it is a convenient way to get code to compile if you are confused. While I am very much in favor of developers knowing exactly what type of data is contained in a sequence—meaning that for EAjqian]^ha$KbP% you should know what data type P is—sometimes, especially when just starting with LINQ, it can get con-fusing. If you find yourself stuck, where code will not compile because of some sort of data type mismatch, consider removing the data type from the @ei statement.
For example, let’s say you have the following code:
#Pdeo_k`asehhjkp_kileha*
@ei`^=oJasJknpdsej`$@]p]Okqn_a9*XOMHATLNAOO7Ejepe]h?]p]hkc9Jknpdsej`%
@eikn`ano=oEAjqian]^ha$;%9`^*?qopkiano[
*Sdana$Bqj_pekj$_%_*?kqjpnu9QO==j`=hok_*NacekjEoS=%[ *Oaha_pI]ju$Bqj_pekj$_%_*Kn`ano%
Listing 1-7. Code Sample Using the Dim Keyword Without Specifing the Type
@ei`^=oJasJknpdsej`$@]p]Okqn_a9*XOMHATLNAOO7Ejepe]h?]p]hkc9Jknpdsej`%
@eikn`ano9`^*?qopkiano[
*Sdana$Bqj_pekj$_%_*?kqjpnu9QO==j`=hok_*NacekjEoS=%[ *Oaha_pI]ju$Bqj_pekj$_%_*Kn`ano%
?kjokha*SnepaHeja$kn`ano*CapPula$%%
In this example, please notice that the kn`ano variable type is now not specified. Running this code produces the following:
Ouopai*@]p]*Hejm*@]p]Mqanu\-W?d]lpan-*jsej`*Kn`anY
There is a lot of compiler gobbledygook there, but the important part is the jsej`*Kn`an
portion. You now know that the data type you are getting a sequence of is jsej`*Kn`an. So you could rewrite the previous code, plus enumerate through the results, as shown in Listing 1-8.
Listing 1-8. Sample Code from Listing 1-7 Except with Explicit Types
@ei`^=oJasJknpdsej`$[
@]p]Okqn_a9*XOMHATLNAOO7Ejepe]h?]p]hkc9Jknpdsej`7Ejpacn]pa`Oa_qnepu9OOLE7%
@eikn`ano=oEAjqian]^ha$KbKn`an%9`^*?qopkiano[
*Sdana$Bqj_pekj$_%_*?kqjpnu9QO==j`=hok_*NacekjEoS=%[ *Oaha_pI]ju$Bqj_pekj$_%_*Kn`ano%
BknA]_depai=oKn`anEjkn`ano ?kjokha*SnepaHeja$[
w,y)w-y)w.y([ epai*Kn`an@]pa([ epai*Kn`anE@([ epai*OdelJ]ia% Jatpepai
Note
For the previous code to work, you will need to have an Eilknpo statement for the Ouopai* ?khha_pekjo*Cajane_ namespace, in addition to the Ouopai*Hejm namespace you should always expect to have when LINQ code is present.This code would produce the following abbreviated results:
Å
0+-3+-554-.6,,6,,=I)--,/.)Sdepa?hkranI]ngapo 1+-+-554-.6,,6,,=I)--,22)Sdepa?hkranI]ngapo
Use the Cast or OfType Operators for Legacy Collections
You will find that the majority of LINQ’s Standard Query Operators can only be called on collections implementing the EAjqian]^ha$KbP% interface. None of the legacy .NET collec-tions—those in the Ouopai*?khha_pekj namespace—implement EAjqian]^ha$KbP%. So the question becomes, how do you use LINQ with legacy collections in your existing code base?
There are two Standard Query Operators specifically for this purpose, ?]op and KbPula. Both of these operators can be used to convert legacy collections to EAjqian]^ha$KbP%
sequences. Listing 1-9 shows an example.
Listing 1-9. Converting a Legacy Collection to an IEnumerable(Of T) Using the Cast Operator
#E#hh_na]pa]hac]_u_khha_pekj* @ei]nn]uHeop=o=nn]uHeop9[
Jas=nn]uHeop$JasK^fa_p$%w=`]io(=npdqn(>q_d]j]jy%
@eij]iao=oEAjqian]^ha$KbOpnejc%9[
]nn]uHeop*?]op$KbOpnejc%$%*Sdana$Bqj_pekj$j%j*Hajcpd83%
BknA]_dj]ia=oOpnejcEjj]iao ?kjokha*SnepaHeja$j]ia% Jatpj]ia
Listing 1-10 shows the same example using the KbPula operator.
Listing 1-10. Using the OfType Operator
#E#hh_na]pa]hac]_u_khha_pekj* @ei]nn]uHeop=o=nn]uHeop9[
Jas=nn]uHeop$JasK^fa_p$%w=`]io(=npdqn(>q_d]j]jy%
@eij]iao=oEAjqian]^ha$KbOpnejc%9[
]nn]uHeop*KbPula$KbOpnejc%$%*Sdana$Bqj_pekj$j%j*Hajcpd83%
BknA]_dj]ia=oOpnejcEjj]iao ?kjokha*SnepaHeja$j]ia% Jatpj]ia
Both examples provide the exact same results. Here they are:
The difference between the two operators is that the ?]op operator will attempt to cast every element in the collection to the specified type to be put into the output sequence. If there is a type in the collection that cannot be cast to the specified type, an exception will be thrown. This makes the ?]op operator excellent for enforcing the data type of objects in a col-lection. The KbPula operator will only attempt to put those elements that can be cast to the type specified into the output sequence. This makes the KbPula operator excellent for filtering a collection by data type.
Prefer the OfType Operator to the Cast Operator
One of the most important reasons why generics were added to .NET was to give the lan-guage the ability to have data collections with static type checking. Prior to generics—barring creating your own specific collection type for every type of data for which you wanted a collection—there was no way to ensure that every element in a legacy collection, such as an
=nn]uHeop, a D]odp]^ha, and so on, was of the same and correct type. Nothing in the language prevented code from adding a Patp^kt object to an =nn]uHeop meant to contain only H]^ah
objects.
With the introduction of generics in .NET 2.0, .NET developers now have a way to explic-itly state that a collection can only contain elements of a specified type. While either the
KbPula or ?]op operator may work for a legacy collection, ?]op requires that every object in the collection be of the correct type. When using the ?]op operator, if any object is unable to be cast to the specified data type, an exception is thrown. If there is any legitimate possibility that objects of differing types exist in the collection, use the KbPula operator. With it, only objects of the specified type will be stored in the output EAjqian]^ha$KbP% sequence, and no exception will be thrown. The best-case scenario is that every object will be of the specified type and be in the output sequence. The worst case is that some elements will get skipped, but they would have thrown an exception had the ?]op operator been used instead.
Don’t Assume a Query Is Bug Free
In Chapter 3, I discuss that LINQ queries are often deferred and not actually executed when it appears you are calling them. For example, consider this code fragment from Listing 1-1:
@eiepaio9[
BnkioEjcnaapejco[ Sdanao*Aj`oSepd$HEJM%[ Oaha_po
BknA]_depaiEjepaio ?kjokha*SnepaHeja$epai% Jatpepai
It is often easy to forget that many of the query operators are deferred and will not execute until a sequence is enumerated. This means you could have an improperly written query that will throw an exception when the resulting sequence is eventually enumerated. That enumera-tion could take place far enough downstream that it is easily forgotten that a query may be the culprit.
Let’s examine the code in Listing 1-11.
Listing 1-11. Query with Intentional Exception Deferred Until Enumeration
@eiopnejco$%=oOpnejc9wkja(psk(Jkpdejc(pdnaay
?kjokha*SnepaHeja$>abknaSdana$%eo_]hha`*%
@eieaOpnejco=oEAjqian]^ha$KbOpnejc%9opnejco*Sdana$Bqj_pekj$o%o*Hajcpd9/% ?kjokha*SnepaHeja$=bpanSdana$%eo_]hha`*%
BknA]_do=oOpnejcEjeaOpnejco ?kjokha*SnepaHeja$Lnk_aooejc"o% Jatpo
I know that the third element in the array of strings is Jkpdejc, and I cannot call Jkpdejc* Hajcpd without throwing an exception. The execution steps over the line of code calling the query just fine. It is not until I enumerate the sequence eaOpnejco, and specifically the third element, that the exception occurs. Here are the results of this code:
>abknaSdana$%eo_]hha`* =bpanSdana$%eo_]hha`* Lnk_aooejckja
Lnk_aooejcpsk
Qjd]j`ha`At_alpekj6Ouopai*JqhhNabanaj_aAt_alpekj6K^fa_pnabanaj_ajkpoappk]j ejop]j_akb]jk^fa_p*
Å
As you can see, I called the Sdana operator without exception. It’s not until I try to enu-merate the third element of the sequence that an exception is thrown. Now imagine if that sequence,eaOpnejco, is passed to a function that downstream enumerates the sequence, perhaps to populate a drop-down list or some other control. It would be easy to think the exception is caused by a fault in that function, not the LINQ query itself.
Take Advantage of Deferred Queries
In Chapter 3, I go into deferred queries in more depth. However, I want to point out that if a query is a deferred query that ultimately returns an EAjqian]^ha$KbP%, that EAjqian]^ha$KbP%
object can be enumerated over, time and time again, obtaining the latest data from the data source. You don’t need to actually call or, as I earlier pointed out, declare the query again.
In most of the code samples in this book, you will see a query called and an
code is executed multiple times, calling the actual query each time is needless work. It might make more sense to have a query initialization method that gets called once for the lifetime of the scope and to construct all the queries there. Then, you could enumerate over a particular sequence to get the latest version of the query results at will.
Use the DataContext Log
When working with LINQ to SQL, don’t forget that the database class that is generated by SqlMetal inherits from Ouopai*@]p]*Hejm*@]p]?kjpatp. This means that your generated
@]p]?kjpatp class has some useful built-in functionality, such as a PatpSnepan property namedHkc.
One of the niceties of the Hkc object is that it will output the equivalent SQL statement of anEMqanu]^ha$KbP% query prior to the parameter substitution. Have you ever had code break in production that you think might be data related? Wouldn’t it be nice if there was a way to get the query executed against the database, so that you could enter it in SQL Server Enterprise Manager or Query Analyzer and see the exact data coming back? The @]p]?kjpatp’sHkc object will output the SQL query for you. An example is shown in Listing 1-12.
Listing 1-12. An Example Using the DataContext.Log Object
@ei`^=oJasJknpdsej`$[
@]p]Okqn_a9*XOMHATLNAOO7Ejepe]h?]p]hkc9Jknpdsej`7Ejpacn]pa`Oa_qnepu9OOLE7%
`^*Hkc9?kjokha*Kqp
@eikn`ano=oEMqanu]^ha$KbKn`an%9[ Bnki_Ej`^*?qopkiano[
BnkikEj_*Kn`ano[
Sdana_*?kqjpnu9QO==j`=hok_*NacekjEoS=[ Oaha_pk
BknA]_depai=oKn`anEjkn`ano
?kjokha*SnepaHeja$w,y)w-y)w.y(epai*Kn`an@]pa(epai*Kn`anE@(epai*OdelJ]ia% Jatpepai
This code produces the following output:
OAHA?PWp-Y*WKn`anE@Y(Wp-Y*W?qopkianE@Y(Wp-Y*WAilhkuaaE@Y(Wp-Y*WKn`an@]paY( Wp-Y*WNamqena`@]paY(Wp-Y*WOdella`@]paY(
Wp-Y*WOdelRe]Y(Wp-Y*WBnaecdpY(Wp-Y*WOdelJ]iaY(Wp-Y*WOdel=``naooY( Wp-Y*WOdel?epuY(Wp-Y*WOdelNacekjY(Wp-Y*WOdelLkop]
h?k`aY(Wp-Y*WOdel?kqjpnuY
BNKIW`^kY*W?qopkianoY=OWp,Y(W`^kY*WKn`anoY=OWp-Y
SDANA$Wp,Y*W?kqjpnuY9<l,%=J@$Wp,Y*WNacekjY9<l-%=J@$Wp-Y*W?qopkianE@Y9 Wp,Y*W?qopkianE@Y%
))<l,6EjlqpJR]n?d]n$Oeva9/7Lna_9,7O_]ha9,%WQO=Y ))<l-6EjlqpJR]n?d]n$Oeva9.7Lna_9,7O_]ha9,%WS=Y
))?kjpatp6OmhLnkre`an$Omh.,,4%Ik`ah6=ppne^qpa`Iap]Ik`ah>qeh`6/*1*/,3.5*-/+.-+-553-.6,,6,,=I)-,04.)H]vuGGkqjpnuOpkna 1+..+-553-.6,,6,,=I)-,101)H]vuGGkqjpnuOpkna
2+-5+-553-.6,,6,,=I)-,130)Pn]eh#oDa]`CkqniapLnkreoekjano 2+./+-553-.6,,6,,=I)-,133)Pn]eh#oDa]`CkqniapLnkreoekjano -+4+-554-.6,,6,,=I)-,4..)Pn]eh#oDa]`CkqniapLnkreoekjano 3+/-+-552-.6,,6,,=I)-,.25)Sdepa?hkranI]ngapo
--+-+-552-.6,,6,,=I)-,/00)Sdepa?hkranI]ngapo /+-,+-553-.6,,6,,=I)-,025)Sdepa?hkranI]ngapo /+.0+-553-.6,,6,,=I)-,04/)Sdepa?hkranI]ngapo 0+--+-553-.6,,6,,=I)-,1,0)Sdepa?hkranI]ngapo 3+--+-553-.6,,6,,=I)-,152)Sdepa?hkranI]ngapo -,+2+-553-.6,,6,,=I)-,25/)Sdepa?hkranI]ngapo -,+4+-553-.6,,6,,=I)-,252)Sdepa?hkranI]ngapo -,+/,+-553-.6,,6,,=I)-,3./)Sdepa?hkranI]ngapo --+-/+-553-.6,,6,,=I)-,30,)Sdepa?hkranI]ngapo -+/,+-554-.6,,6,,=I)-,42-)Sdepa?hkranI]ngapo .+.0+-554-.6,,6,,=I)-,5,0)Sdepa?hkranI]ngapo 0+-3+-554-.6,,6,,=I)--,/.)Sdepa?hkranI]ngapo 1+-+-554-.6,,6,,=I)--,22)Sdepa?hkranI]ngapo
Use the LINQ Forum
Despite providing the best tips I can think of, there will more than likely be times when you get stuck. Don’t forget that there is a forum dedicated to LINQ at MSDN.com. You can find a link to it here: dppl6++sss*hejm`ar*_ki. This forum is monitored by Microsoft developers, and you will find a wealth of knowledgeable resources there.
Summary
I sense that by now you are chomping at the bit to move on to the next chapter, but before you do, I want to remind you of a few things.
First, LINQ is going to change the way .NET developers query data. Vendors will more than likely be lining up to add a “LINQ Compatible” sticker to their products, just like they cur-rently do with XML.
Bear in mind that LINQ is not just a new library to be added to your project. It is a total approach to querying data that comprises several components depending on the data store being queried. At the present time, you can use LINQ to query the following data sources: in-memory data collections using LINQ to Objects, XML using LINQ to XML, DataSets using LINQ to DataSet, and SQL Server databases using LINQ to SQL.
Also, please remember what I said about LINQ being for more than just queries. In a sample project I have been working on using LINQ, I have found LINQ very useful not only for querying data but for getting data into the necessary format for presentation in a WinForm control.
VB.NET 2008 Language
Enhancements for LINQ
I
n the previous chapter, I introduced you to LINQ. I provided some examples to whet your appetite and shared some premature tips. You may be perplexed though by some of the syntax. If so, it is probably because the VB.NET you witnessed in that chapter is new and improved. The reason for the VB.NET language upgrade is that VB.NET 2005 just didn’t have the muscle to pull off LINQ. In this chapter, I introduce you to the more powerful VB.NET 2008.Please be aware that in this book, when I use the term VB.NET 2008, I am referring to VB.NET 9.0, which is shipped with Visual Studio 2008. So technically, the features I am cover-ing in this chapter are in the VB.NET 9.0 language specification.
New VB.NET 2008 Language Additions
To make LINQ seamlessly integrate with VB.NET, significant enhancements were needed for the VB.NET language. Virtually every significant enhancement to the VB.NET language made in version 2008 was made specifically to support LINQ. While all of these features have merit on their own, it is really the sum of the parts contributing to LINQ that makes VB.NET 2008 so noteworthy.
To truly understand much of the syntax of LINQ, it is necessary for me to cover some of the new VB.NET 2008 language features before proceeding with the workings of the compo-nents of LINQ. This chapter will cover the following language additions:
Ê UÊ iÜÊ«ÌÃ Ê UÊ >L`>ÊiÝ«ÀiÃÃÃ Ê UÊ Ý«ÀiÃÃÊÌÀiiÃ
Ê UÊ / iÊiÜÊÕÃ>}iÊvÊÌ iÊ@ei keyword
Ê UÊ "LiVÌÊÌ>â>ÌÊ>`Ê>ÞÕÃÊÌÞ«iÃ Ê UÊ ÝÌiÃÊiÌ `Ã