1. Trang chủ
  2. » Công Nghệ Thông Tin

Mastering Microsoft Visual Basic 2010 phần 7 pps

105 434 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 105
Dung lượng 905,51 KB

Nội dung

598 CHAPTER 14 AN INTRODUCTION TO LINQ ◆ XElement represents an XML element. ◆ XAttribute represents an attribute in an XML element. These objects can be used to access the document but also to create it. Instead of creating an XML document directly in your code, you can use the XML helper objects and a structural approach to create the same document. A simple XML document consists of elements, which may include attributes. To create a new XElement object, pass the element’s name and value to its constructor: New XElement(element_name, element_value) The following statement will create a very simple XML document: Dim XmlDoc = New XElement("Books") MsgBox(XmlDoc.ToString) You will see the string <Books /> in a message box. This is a trivial, yet valid, XML document. To create the same book collection as we did earlier by using the helper objects, insert the fol- lowing statements in a button’s Click event handler: Dim doc = _ New XElement("Books", _ New XElement("Book", _ New XAttribute("ISBN", "0000000000001"), _ New XElement("Price", 11.95), _ New XElement("Name", "Book Title 1"), _ New XElement("Stock", _ New XAttribute("InStock", 12), _ New XAttribute("OnOrder", 24))), _ New XElement("Book", _ New XAttribute("ISBN", "0000000000002"), _ New XElement("Price", 10.25), _ New XElement("Name", "Book Title 2"), _ New XElement("Stock", _ New XAttribute("InStock", 7), _ New XAttribute("OnOrder", 10)))) I’ve added a twist to the new document to demonstrate the use of multiple attributes in the same element. The Stock element contains two attributes, InStock and OnOrder.Eachele- ment’s value can be a basic data type, such as a string or a number, or another element. The Price element is a decimal value, and the Name element is a string. The Book element, however, contains three subelements: the Price, Name,andStock elements. The doc variable is of the XElement type. An XML document is not necessarily based on the XDocument class. The two basic operations you can perform with an XElement (and XDocument) object are to save it to a file and reload an XElement object from a file. The operations are performed with the Save and Load methods, which accept the file’s name as an argument. LINQ TO XML 599 Adding Dynamic Content to an XML Document The XML documents we’ve built in our code so far were static. Because XML support is built into VB, you can also create dynamic context, and this is where things get quite interesting. To insert some dynamic content into an XML document, insert the characters <%=. The editor will automatically insert the closing tag, which is %>. Everything within these two tags is treated as VB code and compiled. The two special tags create a placeholder in the document (or an expression hole), and the expression you insert in them is an embedded expression: You embed a VB expression in your document, and the compiler evaluates the expression and inserts the result in the XML document. Here’s a trivial XML document with an embedded expression. It’s the statement that creates adocumentwithaBook element (I copied it from a code segment presented in the preceding chapter), and I inserted the current date as an element: Dim doc = _ New XElement("Books", _ New XElement("Book", _ New XAttribute("ISBN", "0000000000001"), _ New XAttribute("RecordDate", <%= Today %>), _ New XElement("Price", 11.95), _ New XElement("Name", "Book Title 1"), _ New XElement("Stock", _ New XAttribute("InStock", 12), _ New XAttribute("OnOrder", 24)))) Let’s say you have an array of Product objects and you want to create an XML document with these objects. Listing 14.3 shows the array with the product names. Listing 14.3: An array of Product objects Dim Products() As Product = _ {New Product With {.ProductID = 3, .ProductName = "Product A", _ .ProductPrice = 8.75, _ .ProductExpDate = #2/2/2009#}, _ New Product With _ {.ProductID = 4, .ProductName = "Product B", _ .ProductPrice = 19.5}, _ New Product With _ {.ProductID = 5, .ProductName = "Product C", _ .ProductPrice = 21.25, _ .ProductExpDate = #12/31/2010#}} The code for generating an XML document with three elements is quite short, but what if you had thousands of products? Let’s assume that the Products array contains instances of the Product class. You can use the XMLSerializer class to generate an XML document with the 600 CHAPTER 14 AN INTRODUCTION TO LINQ array’s contents. An alternative approach is to create an inline XML document with embedded expressions, as shown in Listing 14.4. Listing 14.4: An XML document with Product objects Dim prods = <Products> <%= From prod In Products _ Select <Product> <ID><%= prod.ProductID %></ID> <Name><%= prod.ProductName %></Name> <Price><%= prod.ProductPrice %></Price> <ExpirationDate> <%= prod.ProductExpDate %></ExpirationDate> </Product> %> </Products> This code segment looks pretty ugly, but here’s how it works: In the first line, we start a new XML document. (The prods variable is actually of the XElement type, but an XElement is in its own right an XML document.) Notice that there’s no line continuation character at the end of the first line of the XML document. Then comes a LINQ query embedded in the XML document with the <%= and %> tags. Notice the line continuation symbol at the end of this line(_). When we’re in an expression hole, we’re writing VB code, so line breaks matter. That makes the line continuation symbol necessary. Here’s a much simplified version of the same code: Dim prods = <Products> <%= From prod In Products _ Select <Product>some product</Product> %> </Products> This code segment will generate the following XML document: <Products> <Product>some product</Product> <Product>some product</Product> <Product>some product</Product> </Products> The file contains no real data but is a valid XML document. The two tags with the percent sign switch into VB code, and the compiler executes the statements embedded in them. The embedded statement of our example is a LINQ query, which iterates through the elements of the Products array and selects literals (the XML tags shown in the output). To insert data between the tags, we must switch to VB again and insert the values we want to appear in the XML document. In other words, we must replace the string some product in the listing with some embedded expressions that return the values you want to insert in the XML document. These values are the properties of the Product class, as shown in Listing 14.3. The code shown in Listing 14.4 will produce the output shown in Listing 14.5. LINQ TO XML 601 Listing 14.5: An XML document with the data of the array initialized in Listing 14.4 <Products> <Product> <ID>3</ID> <Name>Product A</Name> <Price>8.75</Price> <ExpirationDate>2009-02-02T00:00:00</ExpirationDate> </Product> <Product> <ID>4</ID> <Name>Product B</Name> <Price>19.5</Price> <ExpirationDate>0001-01-01T00:00:00</ExpirationDate> </Product> <Product> <ID>5</ID> <Name>Product C</Name> <Price>21.25</Price> <ExpirationDate>2010-12-31T00:00:00</ExpirationDate> </Product> </Products> Transforming XML Documents A common operation is the transformation of an XML document. If you have worked with XML in the past, you already know Extensible Stylesheet Language Transformations (XSLT), which is a language for transforming XML documents. If you’re new to XML, you’ll probably find it easier to transform XML documents with the LINQ to XML component. Even if you’re familiar with XSLT, you should be aware that transforming XML documents with LINQ is straightforward. The idea is to create an inline XML document that contains HTML tags and an embedded LINQ query, like the following: Dim HTML = <htlm><b>Products</b> <table border="all"><tr> <td>Product</td><td>Price</td> <td>Expiration</td></tr> <%= From item In Products.Descendants("Product") _ Select <tr><td><%= item.<Name> %></td> <td><%= item.<Price> %></td> <td><%= Convert.ToDateTime( _ item.<ExpirationDate>.Value). _ ToShortDateString %> </td></tr> %></table> </htlm> HTML.Save("Products.html") Process.Start("Products.html") 602 CHAPTER 14 AN INTRODUCTION TO LINQ The HTML variable stores plain HTML code. HTML is a subset of XML, and the editor will treat it like XML: It will insert the closing tags for you and will not let you nest tags in the wrong order. The Select keyword in the query is followed by a mix of HTML tags and embedded holes for inline expressions, which are the fields of the item object. Note the VB code for formatting the date in the last inline expression. The output of the previous listing is shown in Figure 14.3. Figure 14.3 AsimpleXMLseg- ment (top) viewed a s an HTML table (bottom). Transformation courtesy of LINQ. <products> <product ProductID=“1” ProductName=“Chai” UnitPrice=“18.0000” UnitsInStock=“39” UnitsOnOrder=“0” > </product> <product ProductID=“2” ProductName=“Chang” UnitPrice=“19.0000” UnitsInStock=“19” UnitsOnOrder=“40” > </product> <product ProductID=“3” ProductName=“Aniseed Syrup” UnitPrice=“10.0000” UnitsInStock=“26” UnitsOnOrder=“70” > </product> <product ProductID=“4” ProductName=“Chef Anton’s Cajun Seasoning” UnitPrice=“22.0000” UnitsInStock=“128” UnitsOnOrder=“0” > </product> <product ProductID=“5” ProductName=“Chef Anton’s Gumbo Mix” UnitPrice=“21.3600” UnitsInStock=“46” UnitsOnOrder=“0” > </product> Products Product Price On Order Chai Chang Aniseed Syrup Chef Anton’s Cajun Seasoning Chef Anton’s Gumbo Mix Grandma’s Boysenberry Spread Uncle Bob’s Organic Dried Pears Northwoods Cranberry Sauce Mishi Kobe Niku Queso Cabrales Queso Manchego La Pastora Konbu 0 40 70 0 0 0 0 0 0 30 0 0 39 19 26 128 46 179 27 164 50 154 184 95 18.00 19.00 10.00 22.00 21.36 25.01 30.01 40.00 97.00 21.00 38.00 6.00 In Stock The last two statements save the HTML file generated by our code and then open it in Internet Explorer (or whichever application you’ve designated to handle by default the HTML documents). LINQ TO XML 603 Using Custom Functions with LINQ to XML The embedded expressions are not limited to simple, inline expressions. You can call custom functions to transform your data. In a hotel reservation system I developed recently, I had to transform an XML file with room details to an HTML page. The transformation involved quite a few lookup operations, which I implemented with custom functions. Here’s a simplified version of the LINQ query I used in the project. I’m showing the query that generates a simple HTML table with the elements of the XML document. The RoomType element is a numeric value that specifies the type of the room. This value may differ from one supplier to another, so I had to implement the lookup operation with a custom function. Dim hotels = <html> <table><tr><td>Hotel</td<td>Room Type</td><td>Price</td></tr> <%= From hotel In Hotels _ Select <tr><td><%= hotel.<HotelName>.Value %></td> <td><%= GetRoomType(hotel.<RoomTypeID>)</td> <td><%= CalculatePrice(hotel.<Base>)</td> </tr> %> </table> </html> The GetRoomType() and CalculatePrice() functions must be implemented in the same module that contains the LINQ query. In my case, they accept more arguments than shown here, but you get the idea. To speed up the application, I created HashTables using the IDs of the various entities in their respective tables in the database. The CalculatePrice() function, in particular, is quite complicated, because it incorporates the pricing policy. Yet, all the business logic implemented in a s tandard VB function was easily incorporated into the LINQ query that generates the HTML page with the available hotels and prices. Another interesting application of XML transformation is the transformation of XML data into instances of custom objects. Let’s say you need to work with an XML file that contains product information, and you want to create a list of Product objects out of it. Let’s also assume that the XML file has the following structure: <Products> <product ProductID="1" ProductName="Chai" CategoryID="1" UnitPrice="18.0000" UnitsInStock="39" UnitsOnOrder="0" > </product> <product ProductID="2" ProductName="Chang" CategoryID="1" QuantityPerUnit="24 - 12 oz bottles" UnitPrice="19.0000" UnitsInStock="19" UnitsOnOrder="40" > </product> </Products> 604 CHAPTER 14 AN INTRODUCTION TO LINQ First, you must load the XML file into an XElement variable with the following statement (I’m assuming that the XML file is in the same folder as the project): Dim XMLproducts = XElement.Load(" / /Products.xml") Now, you can write a LINQ query that generates anonymous types, like the following: Dim prods = From prod In XMLproducts.Descendants("product") Select New With {.Name = prod.Attribute("ProductName").Value, .Price = prod.Attribute("UnitPrice").Value, .InStock = prod.Attribute("UnitsInStock").Value, .OnOrder = prod.Attribute("UnitsOnOrder").Value}} The prods collection consists of objects with the four scalar properties. To make the example a touch more interesting, let’s say that you don’t want to create a ‘‘flat’’ object. The InStock and OnOrder properties will become properties of another object, the Stock property. The new anonymous type will have the following structure: Product.Name Product.Price Product.Stock.InStock Product.Stock.OnOrder To create an anonymous type with the revised structure, you must replace the InStock and OnOrder properties with a new object, the Stock object, which will expose the InStock and OnOrder properties. The revised query is shown next: Dim prods = From prod In XMLproducts.Descendants("product") Select New With {.Name = prod.Attribute("ProductName").Value, .Price = prod.Attribute("UnitPrice").Value, .Stock = New With { .InStock = prod.Attribute("UnitsInStock").Value, .OnOrder = prod.Attribute("UnitsOnOrder").Value}} A simple LINQ query allows you to move from XML into objects and replace the code that would normally use the XML axis methods (Elements and Descendents)withpureobjects.Of course, anonymous types can be used only in the context of the procedure in which they were created. If you want to pass the prods collection between procedures, you should create a new Product class and use it to create instances of this object, because the anonymous types can’t be used outside the routine in which they were created. The definition of the Product class, and the accompanying Stock class, is quite trivial: Public Class Product Public Property Name As String Public Property Price As Decimal Public Property Stock As Stock End Class LINQ TO XML 605 Public Class Stock Public InStock As Integer Public OnOrder As Integer End Class With the two class definitions in place, you can revise the LINQ query to populate the prod- ucts collection with instances of the Product class: Dim Products = From prod In XMLproducts.Descendants("product") Select New Product With { .Name = prod.Attribute("ProductName").Value, .Stock = New Stock With { .InStock = prod.Attribute("UnitsInStock").Value, .OnOrder = prod.Attribute("UnitsOnOrder").Value}} It shouldn’t come as a surprise that you can iterate through both collections with the same statements: For Each p In prods Debug.WriteLine("PRODUCT: " & p.Name & vbTab & " PRICE: " & p.Price.ToString & " STOCK = " & p.Stock.InStock & "/" & p.Stock.OnOrder) Next When executed, the preceding statements will generate the following output: PRODUCT: Grandma’s Boysenberry Spread PRICE: 25.0100 STOCK = 179/0 PRODUCT: Uncle Bob’s Organic Dried Pears PRICE: 30.0100 STOCK = 27/0 PRODUCT: Northwoods Cranberry Sauce PRICE: 40.0000 STOCK = 164/0 Working with XML Files In this section, we’re going to build a functional interface for viewing customers and orders. And this time we aren’t going to work with a small sample file. We’ll actually get our data from one of the sample databases that comes with SQL Server: the Northwind database. The structure of this database is discussed in Chapter 15, ‘‘Programming with ADO.NET,’’ in detail, but for now I’ll show you how to extract data in XML format from SQL Server. If you don’t have SQL Server installed or if you’re unfamiliar with databases, you can use the sample XML files in the folder of the VBLINQ project. Figure 14.4 shows the main form of the application, which retrieves the same data either from an XML file or directly from the database. You may be wondering why you would extract relational data and process them with LINQ instead of executing SQL statements against the database. XML is the standard data-exchange format, and you may get data from any other source in this format. You may get an XML file generated from someone’s database or even an Excel spreadsheet. In the past, you had to con- vert the data to another, more flexible format and then process it. With LINQ, you can directly query the XML document, transform it into other formats, and of course save it. 606 CHAPTER 14 AN INTRODUCTION TO LINQ Figure 14.4 Displaying related data from XML files Start SQL Server, and execute the following query: SELECT * FROM Customers FOR XML AUTO This statement selects all columns and all rows for the Customers table and generates an element for each row. The field values are stored in the document as attributes of the corre- sponding row. The output of this statement is not a valid XML document because its elements are not embedded in a root element. To request an XML document in which all elements are embedded in a root element, use the ROOT keyword: SELECT * FROM Customers FOR XML AUTO, ROOT(’AllCustomers’) I’m using the root element AllCustomers because the elements of the XML document are named after the table. The preceding statement will generate an XML document with the following structure: <AllCustomers> <Customers CustomerID="…" CompanyName="xxx" … /> <Customers CustomerID="…" CompanyName="xxx" … /> … </AllCustomers> It would make more sense to generate an XML document with the Customers root element and name the individual elements Customer. To generate this structure, use the following statement: SELECT * FROM Customers Customer FOR XML AUTO, ROOT(’Customers’) LINQ TO XML 607 Here’s a segment of the XML document with the customers: <Customers> <Customer CustomerID="ALFKI" CompanyName= "Alfreds Futterkiste" ContactName="Maria Anders" ContactTitle="Sales Representative" Country="Germany" /> <Customer CustomerID="ANATR" CompanyName= "Ana Trujillo Emparedados y helados" ContactName="Ana Trujillo" ContactTitle="Owner" Country="Mexico" /> Finally, you can create an XML document where the fields are inserted as elements, rather than attributes. To do so, use the ELEMENTS keyword: SELECT * FROM Customers Customer FOR XML AUTO, ELEMENTS ROOT(’Customers’) The other statements that generated the XML files with the rows of the tables Orders, Order Details, and Products are as follows: SELECT * FROM Orders Order FOR XML AUTO, ROOT(’Orders’) SELECT * FROM [Order Details] Detail FOR XML AUTO, ELEMENTS, ROOT(’Details’) SELECT ProductID, ProductName FROM Products FOR XML AUTO, ELEMENTS ROOT(’Products’) Notice that all files are attribute based, except for the Details.xml file, which is element based. I had no specific reason for choosing this structure; I just wanted to demonstrate both styles for processing XML in the sample project’s code. Also, the reason I’ve included the Prod- ucts table is because the Order Details table, which contains the lines of the order, stores the IDs of the products, not the product names. When displaying orders, as shown in Figure 14.4, you must show product names, not just product IDs. The four collections with the entities we extracted from the Northwind database are declared and populated at the form’s level via the following statements: Dim customers As XElement = XElement.Load(" \ \ \Customers.xml") Dim orders As XElement = XElement.Load(" \ \ \Orders.xml") Dim details As XElement = XElement.Load(" \ \ \Details.xml") Dim products As XElement = XElement.Load(" \ \ \Products.xml") As is apparent from the code, I’ve placed the four XML files created with the SQL state- ments shown earlier in the project’s folder. The Display Data button populates the top ListView control with the rows of the Customers table, via the following statements: Private Sub bttnShow_Click(…) Handles bttnShow.Click For Each c In customers.Descendants("Customer") Dim LI As New ListViewItem [...]... allows us to work with the database tables as if they were collections 6 27 628 CHAPTER 14 AN INTRODUCTION TO LINQ of custom types It bridges the gap between the object-oriented world of Visual Basic and the realm of relational databases There’s another similar component, LINQ to Entities, which is discussed in detail in Chapter 17, ‘‘Using the Entity Data Model.’’ LINQ to Entities takes the same principles... all major DBMSs support it 632 CHAPTER 15 PROGRAMMING WITH ADO.NET The recommended DBMS for Visual Studio 2010 is SQL Server 2008 In fact, the Visual Studio 2008 setup program offers to install a developer version of SQL Server 2008 called SQL Server 2008 Express However, you can use Access as well as non -Microsoft databases such as Oracle Although this chapter was written with SQL Server 2008, most... uses the name TableClasses The designer is initially an empty space But here’s how easy it is to create custom objects based on the database entities, as shown in Figure 14 .7 Figure 14 .7 Designing a LINQ to SQL Classes class with visual tools Server Explorer will display all the items in the database Select the Customers, Orders and Order Details tables from Server Explorer, and drop them onto the designer’s... Employee) ’ and create order’s header Dim order As New Order order.OrderDate = Now order.Customer = cust order.Employee = emp ’ select a random freight for the order in the range from $3 to $75 order.Freight = RND.Next(300, 75 00) / 100 Dim discount As Decimal ’ select a random discount value for the order discount = RND.Next(0, 45) / 100 Dim prod As Product ’ create a random number of detail lines in the... submits to the database When the user selects a customer on the ListBox control, the statements included in Listing 14 .7 are executed to display the number of orders placed by the customer and the total revenue generated by the selected customer, as well as the headers of all orders Listing 14 .7: Retrieving the orders of the selected customer Private Sub ListBox1_SelectedIndexChanged( ) Handles ListBox1.SelectedIndexChanged... ADO.NET Chapter 16: Developing Data-Driven Applications Chapter 17: Using the Data Entity Model Chapter 18: Building Data-Bound Applications Chapter 15 Programming with ADO.NET With this chapter, we start exploring applications that manipulate large sets of data stored in a database After a quick introduction to databases, you’ll learn about the basic mechanisms of interacting with databases As you will... section is a quick introduction to a component that allows you to handle database tables as objects and manipulate them with LINQ The VBLINQ2SQL project (available for download from www.sybex.com/go/masteringvb2010) contains a form that displays all products on a ListView control, as shown in Figure 14.9 LINQ TO SQL The Add Another Product button brings up the form shown in the same figure, which allows... follow the examples Obtaining the Northwind and Pubs Sample Databases SQL Server 2008 developers will wonder where the Northwind and Pubs databases have gone Microsoft has replaced both databases with a single new database called AdventureWorks Microsoft made the change to demonstrate new SQL Server features in an environment 633 ... ([t3].[OrderID] = [t3].[OrderID2]) GROUP BY [t3].[Country] ) AS [t4] ORDER BY [t4].[Country] @p0: Input Real (Size = -1; Prec = 0; Scale = 0) [1] @p1: Input Int (Size = -1; Prec = 0; Scale = 0) [19 97] This is a fairly complicated SQL query, unless you’re familiar with SQL If not, try to master LINQ and let the compiler generate the SQL queries for you If you haven’t used SQL before, you’ll find an... then you use the DataContext object to populate these classes You may be thinking already about a class generator that will take care of the mapping between the class properties and the table columns Visual Studio does that for you with a component called LINQ to SQL Classes A LINQ to SQL Classes component encapsulates a segment of a database, or the entire database, and lets you work against a database . Cabrales Queso Manchego La Pastora Konbu 0 40 70 0 0 0 0 0 0 30 0 0 39 19 26 128 46 179 27 164 50 154 184 95 18.00 19.00 10.00 22.00 21.36 25.01 30.01 40.00 97. 00 21.00 38.00 6.00 In Stock The last. custom objects based on the database entities, as shown in Figure 14 .7. Figure 14 .7 Designing a LINQ to SQL Classes class with visual tools Server Explorer will display all the items in the database output: PRODUCT: Grandma’s Boysenberry Spread PRICE: 25.0100 STOCK = 179 /0 PRODUCT: Uncle Bob’s Organic Dried Pears PRICE: 30.0100 STOCK = 27/ 0 PRODUCT: Northwoods Cranberry Sauce PRICE: 40.0000 STOCK

Ngày đăng: 12/08/2014, 21:20