Professional ASP.NET 2.0 XML phần 5 potx

60 309 0
Professional ASP.NET 2.0 XML phần 5 potx

Đ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

Let’s start by discussing the DataSet object and the XML support it offers. ADO.NET and XML One of the key objects in ADO.NET is the DataSet. A System.Data.DataSet object stores data in a hierar- chical object model and hence is similar to a relational database in structure. Besides storing data in a discon- nected cache, a DataSet also stores information such as the constraints and relationships that are defined for the DataSet. You use a DataSet to access data from the tables when disconnected from the data source. You can access the ADO.NET DataSet objects in two pathways regardless of the data source: the DataSet properties, methods, and events; and the XML DOM through the use of XmlDataDocument object. Both of these techniques have parallel access methods that permit you to follow sequential or hierarchical paths through your data. ADO.NET supports the ability to construct DataSet objects from either XML streams or documents. These XML sources can include data or schema, or both. The schema is expressed as Extensible Schema Definition language (XSD) — another form of XML. You can also export data from a DataSet to an XML document, with or without the schema. This is handy when you have to send data through a firewall; in most situations, a firewall won’t permit you to pass binary data. Now that you have an understanding of the DataSet, the next section goes into more detail on how to populate a DataSet with XML data from an XML document and how to save the contents of a DataSet as XML. Loading XML into a DataSet There are several ways in which you can populate a DataSet with XML, but the most common is probably to use one of the eight different versions of the DataSet.ReadXml() method. Here are the first four. ❑ ReadXml(Stream): This loads the DataSet with the XML in the stream object — that is, any object that inherits from System.IO.Stream, such as System.IO.FileStream; however, it could just as easily be a stream of data coming down from a Web site, and so on. ❑ ReadXml(String): This loads the DataSet with the XML stored in the file whose name you provide. ❑ ReadXml(TextReader): This loads the DataSet with the XML processed by the given text reader—that is, any object that inherits from System.IO.TextReader. ❑ ReadXml(XmlReader): This loads the DataSet with the XML processed by the given XML reader. As you’ve seen, the XmlValidatingReader class inherits from System.Xml.XmlReader, so you can pass an XmlValidatingReader to this function. A DataSet can either be a typed DataSet or an untyped DataSet. A typed DataSet is a class derived from a DataSet class and has an associated XML Schema. On the other hand, an untyped DataSet does not have an associated XML Schema. You see more on typed DataSets later in this chapter. 214 Chapter 8 11_596772 ch08.qxd 12/13/05 11:14 PM Page 214 The other four ReadXml() overloads correspond to the previous four, but with an additional parameter of type XmlReadMode, which is the focus in the next section. XmlReadMode The System.Data.XmlReadMode enumeration is used to determine the behavior of the XML parser when loading documents from various sources. Table 8-1 shows the members of the XmlReadMode enumeration, and the impact they have on the DataSet and how it loads the XML. Table 8-1. Members of XmlReadMode Enumeration Member Description Auto This is the default. It attempts to select one of the other previous options automatically. If the data being loaded is a DiffGram, the XmlReadMode is set to DiffGram. If the DataSet has already been given a schema by some means, or the XML document has an inline schema defined, the XmlReadMode is set to ReadSchema. If the DataSet doesn’t contain a schema, there is no inline schema defined and the XML document is not a DiffGram, the XmlReadMode is set to InferSchema. Because of this indirection, the XmlReadMode.Auto may be slower than using an explicit mode. ReadSchema This option loads any inline schema supplied by the DataSet and then load the data. If any schema information exists in the DataSet prior to this operation, the schema can be extended by the inline XML schema. If new table definitions exist in the inline schema that already exists in the DataSet, however, an exception will be thrown. IgnoreSchema Ignores any inline schema and loads the data into the existing DataSet schema. Any data that does not match the schema is discarded. If you are bulk loading data from existing XML sources, it might be useful to enable this option to get better performance. InferSchema This option forces the DataSet to infer the schema from the XML document, ignoring any inline schema in the document, and extending any schema already in place in the DataSet. DiffGram An XML representation of a “before” and “after” state for data. If you specify this argument, the DataSet loads a DiffGram and applies the changes it indicates to the DataSet. Fragment This option reads XML fragments like an XML document with- out a single root element. An example of this is the XML output generated by FOR XML queries. In this option, the default namespace is read as the inline schema. 215 XML and ADO.NET 11_596772 ch08.qxd 12/13/05 11:14 PM Page 215 Now that you have some information on the ReadXml() method and how to process XML, load some XML data into a DataSet. Before looking at the code, it is useful to examine the XML file (named Products.xml) to be used. It is as follows: <?xml version=”1.0” standalone=”yes”?> <DocumentElement> <Products> <ProductID>1</ProductID> <ProductName>Chai</ProductName> <SupplierID>1</SupplierID> <CategoryID>1</CategoryID> <QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit> <UnitPrice>18.0000</UnitPrice> <UnitsInStock>39</UnitsInStock> <UnitsOnOrder>0</UnitsOnOrder> <ReorderLevel>10</ReorderLevel> <Discontinued>false</Discontinued> </Products> </DocumentElement> You can download the complete code of Products.xml from the Wrox Web site along with the support material for this book. In Listing 8-1, you load the Products.xml file into a DataSet and then display that information in a GridView control. Listing 8-1: Reading XML into a DataSet Using ReadXml <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Configuration”%> <%@ Import Namespace=”System.Data”%> <script runat=”server”> void Page_Load(Object sender, EventArgs e) { DataSet productsDataSet; string filePath = Server.MapPath(“App_Data/Products.xml”); productsDataSet = new DataSet(); //Read the contents of the XML file into the DataSet productsDataSet.ReadXml(filePath); gridProducts.DataSource = productsDataSet.Tables[0].DefaultView; gridProducts.DataBind(); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head runat=”server”> <title>Reading XML Data into a DataSet object </title> </head> Depending on how much decision making needs to take place, using the default Auto mode may perform more slowly than explicitly setting the read mode. A good rule of thumb is to supply the read mode explicitly whenever you know what it will be ahead of time. 216 Chapter 8 11_596772 ch08.qxd 12/13/05 11:14 PM Page 216 <body> <form id=”form1” runat=”server”> <div> <asp:GridView id=”gridProducts” runat=”server” AutoGenerateColumns=”False” CellPadding=”4” HeaderStyle-BackColor=”blue” HeaderStyle-ForeColor=”White” HeaderStyle-HorizontalAlign=”Center” HeaderStyle-Font-Bold=”True”> <Columns> <asp:BoundField HeaderText=”Product ID” DataField=”ProductID” /> <asp:BoundField HeaderText=”Price” DataField=”UnitPrice” ItemStyle-HorizontalAlign=”Right” /> <asp:BoundField HeaderText=”Name” DataField=”ProductName” /> <asp:BoundField HeaderText=”Description” DataField=”QuantityPerUnit” /> </Columns> </asp:GridView> </div> </form> </body> </html> In Listing 8-1, you load an XML file into a DataSet using the ReadXml() method and then simply bind the DataSet onto a GridView control that renders the DataSet contents onto the browser. Navigate to the page and you should see an output similar to Figure 8-1. Figure 8-1 If you call ReadXml() to load a very large file, you may encounter slow performance. To ensure best performance for ReadXml(), on a large file, call the DataTable.BeginLoadData() method for each table in the DataSet and then call ReadXml(). Finally, call DataTable.EndLoadData() for each table in the DataSet as shown in the following example. 217 XML and ADO.NET 11_596772 ch08.qxd 12/13/05 11:14 PM Page 217 foreach (DataTable t in ds.Tables) t.BeginLoadData(); ds.ReadXml(“file.xml”); foreach (DataTable t in ds.Tables) t.EndLoadData(); The BeginLoadData() method turns off notifications, index maintenance, and constraints while loading data. The EndLoadData() method turns on notifications, index maintenance, and constraints after loading data. DataSet Schemas In the previous section, you learned that you can load a DataSet with XML data by using the ReadXml() method of the DataSet object. But what do you do when you want to load a DataSet schema from an XML document? You use either the ReadXmlSchema() or the InferXmlSchema() method of the DataSet to load DataSet schema information from an XML document. Before looking at these methods, it is important to understand what schema inference is. Schema Inference Schema inference is a process that’s performed when a DataSet object without an existing data structure attempts to load data from an XML document. The DataSet will make an initial pass through the XML document to infer the data structure and then a second pass to load the DataSet with the information contained in the document. There is a set of rules for inferring DataSet schemas that is always followed. Therefore, you can accurately predict what the schema inferred from a given XML document will look like. Inference Rules When inferring a schema from an XML document, a DataSet follows these rules. ❑ Elements with attributes become tables. ❑ Elements with child elements become tables. ❑ Repeating elements become columns in a single table. ❑ Attributes become columns. ❑ If the document (root) element has no attributes and no child elements that can be inferred to be columns, it is inferred to be a DataSet; otherwise, the document element becomes a table. ❑ For elements inferred to be tables that have no child elements and contain text, a new column called Tablename_Text is created for the text of each of the elements. If an element with both child nodes and text is inferred to be a table, the text is ignored. ❑ For elements that are inferred to be tables nested within other elements inferred to be tables, a nested DataRelation is created between the two tables. 218 Chapter 8 11_596772 ch08.qxd 12/13/05 11:14 PM Page 218 Inference Rules in Action Consider a sample XML document to understand what kind of schema the DataSet will infer from that XML document. <?xml version=”1.0” standalone=”yes”?> <Products> <Product> <ProductID>1</ProductID> <ProductName>Chai</ProductName> </Product> </Products> If you load the XML into a DataSet object, you will notice that the DataSet automatically infers the schema; then, if you write the schema of the DataSet object to an XSD file using the WriteXmlSchema() method, you will see the output shown in Listing 8-2. Listing 8-2: XSD Schema Produced through Schema Inference <?xml version=”1.0” standalone=”yes”?> <xs:schema id=”Products” xmlns=”” xmlns:xs=”http://www.w3.org/2001/XMLSchema” xmlns:msdata=”urn:schemas-microsoft-com:xml-msdata”> <xs:element name=”Products” msdata:IsDataSet=”true” msdata:UseCurrentLocale=”true”> <xs:complexType> <xs:choice minOccurs=”0” maxOccurs=”unbounded”> <xs:element name=”Product”> <xs:complexType> <xs:sequence> <xs:element name=”ProductID” type=”xs:string” minOccurs=”0” /> <xs:element name=”ProductName” type=”xs:string” minOccurs=”0” /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> As you can see from the XSD schema output, the schema inference process has automatically inferred a DataSet named Products, with a single table named Product. The most interesting aspect is that this provides a schema inference tool that can be used in other contexts. For example, you can utilize the schema inference tool to generate a schema that can be used to create a typed DataSet later in the chapter. Supplied Schemas Instead of allowing the DataSet to infer the schema, you can supply a schema to a DataSet explicitly. This is important because there are some limitations with the schema inference mechanism. For example, schema inference can only go so far before it deviates from how you would really like the data to be organized. It can’t infer data types, and it won’t infer existing column relationships — instead, it creates new columns and new relationships. 219 XML and ADO.NET 11_596772 ch08.qxd 12/13/05 11:14 PM Page 219 Because of the inherent problems related to schema inference, there are times you might want to explicitly supply a schema to your DataSet object. There are several ways you can accomplish this. Obviously, you can create one yourself by creating tables and columns in a DataSet object and by using the WriteXmlSchema() method as described previously. Alternatively, you can supply an XSD file (or an XmlSchema class) to the DataSet, or you can give the responsibility of generating the internal relational structure to a DataAdapter. When you supply a schema upfront, you can enforce constraints and richness of programming through Intellisense. The next section starts by looking at the last of those options first. The FillSchema Method The DataAdapter has a FillSchema() method that executes a query on the database to fill the schema information of table/tables into a DataSet. For an example of a situation in which this facility might be useful, imagine that you have an XML document whose data you would eventually like to add to a SQL Server database. As a first step toward doing that, you simply fill the DataSet with the schema of the ContactType table and then load the XML document with the DataSet’s ReadXml() method. After that, you can easily use the DataSet to add to the SQL Server database. Before looking at the ASP.NET page, examine the ContactType.xml used for the purposes of this example. <?xml version=”1.0” standalone=”yes”?> <ContactType> <Table> <ContactTypeID>1</ContactTypeID> <Name>Accounting Manager</Name> <ModifiedDate>6/1/1998</ModifiedDate> </Table> <Table> <ContactTypeID>2</ContactTypeID> <Name>Assistant Sales Agent</Name> <ModifiedDate>6/1/1998</ModifiedDate> </Table> </ContactType> Now that you have looked at the XML file, Listing 8-3 shows the ASP.NET page in action. Listing 8-3: Loading Schemas through FillSchema Method <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Web.Configuration”%> <%@ Import Namespace=”System.Data”%> <%@ Import Namespace=”System.Data.SqlClient”%> <script runat=”server”> void Page_Load(Object sender, EventArgs e) { string filePath = Server.MapPath(“App_Data/ContactType.xml”); string connString = WebConfigurationManager. ConnectionStrings[“adventureWorks”].ConnectionString; string sql = “Select ContactTypeID, Name from Person.ContactType”; DataSet contactsDataSet = new DataSet(“ContactType”); using (SqlConnection sqlConn = new SqlConnection(connString)) { SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn); adapter.FillSchema(contactsDataSet,SchemaType.Source); } 220 Chapter 8 11_596772 ch08.qxd 12/13/05 11:14 PM Page 220 contactsDataSet.ReadXml(filePath); DataTable table = contactsDataSet.Tables[0]; int numCols = table.Columns.Count; foreach (DataRow row in table.Rows) { for (int i = 0; i < numCols; i++) { Response.Write(table.Columns[i].ColumnName + “ = “ + row[i].ToString() + “<br>”); } Response.Write(“<br>”); } } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head runat=”server”> <title>Loading XML Schema using FillSchema method</title> </head> <body> <form id=”form1” runat=”server”> <div> </div> </form> </body> </html> For the code to work, you need to ensure that the web.config file has the <connectionStrings> element present as follows: <connectionStrings> <add name=”adventureWorks” connectionString=”server=localhost;integrated security=true;database=AdventureWorks;”/> </connectionStrings> After you have the connection string in the web.config file, you can then easily retrieve the connection string from within the ASP.NET page. string connString = ConfigurationManager. ConnectionStrings[“adventureWorks”].ConnectionString; After you have loaded the required schema from the ContactType table through the FillSchema() method, you can then simply load the ContactType.xml document into the DataSet by calling the ReadXml() method. Similar to loading the DataSet with the schema of the database table, you can also load a DataTable with the schema of the database table and then load that DataTable with XML data from an external XML document. This is made possible by the new XML support offered by the DataTable. The ReadXmlSchema Method You use the ReadXmlSchema() method when you want to load only DataSet schema information (and no data) from an XML document. This method loads a DataSet schema using the XSD schema. The ReadXmlSchema() method takes a stream, an XmlReader, or a file name as a parameter. In the event of absence of an inline schema in the XML document, the ReadXmlSchema() method interprets the schema 221 XML and ADO.NET 11_596772 ch08.qxd 12/13/05 11:14 PM Page 221 from the elements in the XML document. When you use the ReadXmlSchema() method to load a DataSet that already contains a schema, the existing schema is extended, and new columns are added to the tables. Any tables that do not exist in the existing schema are also added. Note the ReadXmlSchema() method throws an exception if the types of the column in the DataSet and the column in the XML document are incompatible. The InferXmlSchema Method You can also use the InferXmlSchema() method to load the DataSet schema from an XML document. This method has the same functionality as that of the ReadXml() method that uses the XmlReadMode enumeration value set to InferSchema. The InferXmlSchema() method, besides enabling you to infer the schema from an XML document, enables you to specify the namespaces to be ignored when inferring the schema. This method takes two parameters. The first parameter is an XML document location, a stream, or an XmlReader; the second parameter is a string array of the namespaces that need to be ignored when inferring the schema. Transforming DataSet to XML You can easily fill a DataSet with data from an XML stream or document. The information supplied from the XML stream or document can be combined with existing data or schema information that is already present in the DataSet. On the other side, you can use the WriteXml() method of the DataSet object to serialize the XML representation of the DataSet to a file, a stream, an XmlWriter object, or a string. While serializing the contents, you can optionally include the schema information. To control the actual behavior of the WriteXml() method, you set the XmlWriteMode enumeration to any of the values shown in Table 8-2. The values supplied to the XmlWriteMode enum determine the layout of the XML output. The DataSet representation includes tables, relations, and constraints definitions. The rows in the DataSet’s tables are written in their current versions unless you choose to employ the DiffGram format. Table 8-2 summarizes the writing options available with XmlWriteMode. Table 8-2. Members of XmlWriteMode Enumeration Member Description DiffGram Allows you to write the entire DataSet as a DiffGram, including original and current values IgnoreSchema Writes the contents of the DataSet as XML data, without an XSD schema WriteSchema Writes the contents of the DataSet as XML data with relational structure as inline XSD schema Using ADO.NET, you can create an XML representation of a DataSet, with or with- out its schema, and transport the DataSet across HTTP for use by another application or XML-enabled platform. In an XML representation of a DataSet, the data is written in XML and the schema is written using the XML Schema definition language (XSD). Using industry standards such as XML and XML schema, you can seamlessly interact with XML-enabled applications that may be running in a completely different platform. 222 Chapter 8 11_596772 ch08.qxd 12/13/05 11:14 PM Page 222 As you can see, ADO.NET allows you to write XML data with or without the XML schema. When you write DataSet data as XML data, the current version of the DataSet rows is written; however, ADO.NET enables you to write the DataSet data as a DiffGram, which means that both original as well as current versions of the rows would be included. Before proceeding further with an example, it is important to understand what DiffGrams are. DiffGrams A DiffGram is in XML format and is used by the DataSet to store the contents. A DiffGram is used to discriminate between the original and current versions of data. When you write a DataSet as a DiffGram, the DiffGram is populated with all information that is required to re-create the contents of the dataset. These contents include the current and original values of the rows and the error information and order of the rows; however, the DiffGram format doesn’t get populated with the information to re- create the XML schema. A DataSet also uses the DiffGram format to serialize data for transmission across the network. The DiffGram format consists of the following data blocks: ❑ <DataInstance> represents a row of the DataTable object or a dataset and contains the current version of data. ❑ <diffgr:before> contains the original version of the dataset or a row. ❑ <diffgr:errors> contains the information of the errors for a specific row in the <DataInstance> block. Note the element or row that has been edited or modified is marked with the <diff:hasChanges> annota- tion in the current data section. Now that you have the basic knowledge of DiffGram, you will learn how you can write a dataset as XML data. If you want to write the XML representation of the DataSet to an XmlWriter, a stream, or a file, you need to use the WriteXml() method. The WriteXml() method takes two parameters. The first parameter is mandatory and is used to specify the destination of XML output. The second parameter is optional and is used to specify how the XML output would be written. The second parameter of the WriteXml() method is the XmlWriteMode enumeration to which you can pass in any of the values shown in Table 8-2. Listing 8-4 shows you an example on how to serialize the DataSet to an XML file using the WriteXml() method of the DataSet. It also shows how easily you can cache the contents of the DataSet in a local XML file that obviates the need to retrieve the data from the database every time. Listing 8-4: Serializing DataSet to XML Using WriteXml <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Web.Configuration”%> <%@ Import Namespace=”System.Data”%> <%@ Import Namespace=”System.Data.SqlClient”%> <%@ Import Namespace=”System.IO”%> <script runat=”server”> The DiffGram format is used by default when you send and extract a DataSet from a Web service. In addition, you can explicitly specify that the dataset be read or written as a DiffGram when using the ReadXml() and WriteXml() methods. 223 XML and ADO.NET 11_596772 ch08.qxd 12/13/05 11:14 PM Page 223 [...]... void Page_Load(Object sender, EventArgs e) { string xmlPath = Server.MapPath(“App_Data/ContactType .xml ); string xmlSchemaPath = Server.MapPath(“App_Data/ContactType.xsd”); SaveContacts(xmlPath, xmlSchemaPath); XmlDataDocument xmlDoc = new XmlDataDocument(); xmlDoc.DataSet.ReadXmlSchema(xmlSchemaPath); xmlDoc.Load(xmlPath); DataSet contactsDataSet = xmlDoc.DataSet; //Bind the DataSet to the DataGrid... instance of the XmlDataDocument object An example of this is illustrated here XmlDataDocument xmlDoc = new XmlDataDocument(); xmlDoc.Load(filePath); DataSet dataset = xmlDoc.DataSet; You can turn an XmlDataDocument into a DataSet object using the XmlDataDocument’s DataSet property The property instantiates, populates, and returns a DataSet object The DataSet is associated with the XmlDataDocument the... the IXmlSerializable interface In addition, the DataTable class now supports the ReadXml/WriteXml methods that could only be emulated in NET Framework 1.x Table 8-6 outlines the important XML related methods supported by the DataTable class Table 8-6 DataTable’s XML Methods Method Description ReadXml Reads XML and data into the DataTable from sources such as a Stream, XmlWriter, or a TextWriter ReadXmlSchema... this ❑ Support provided by the XmlDataSource control for displaying XML data ❑ Applying an XSL style sheet to transform XML data using an XmlDataSource control ❑ Binding an XmlDataSource control to a GridView control ❑ Caching XML data in an XmlDataSource control ❑ Dynamic retrieval of XML data from the client side ❑ What Atlas technology is and its role in retrieving XML data This chapter will also... returns an XML representation of the DataRow object in the form of an XmlElement object Associate an XmlDataDocument object with the DataSet by passing in the DataSet object to the constructor of the XmlDataDocument object Listing 8-9 shows an example of how to extract XML data using the GetElementFromRow() method of the XmlDataDocument object Listing 8-9: Extracting XML Elements from an XmlDataDocument... Namespace=”System.Data”%> Import Namespace=”System .Xml %> XML and ADO.NET void Page_Load(Object sender, EventArgs e) { DataSet contactTypesDataSet = GetContactTypes(); //Associate the DataSet with an XmlDataDocument object XmlDataDocument xmlDoc = new XmlDataDocument(contactTypesDataSet); //Loop through the DataTable and retrieve the XML Data DataTable table = xmlDoc.DataSet.Tables[0]; StringBuilder... DataSet There are a few ways to bind a DataSet object and XmlDataDocument object together The first option is that you pass a non-empty DataSet object to the constructor of the XmlDataDocument class as follows XmlDataDocument xmlDoc = new XmlDataDocument(dataset); Similar to its base class, XmlDataDocument provides a XML DOM approach to work with XML data An alternate way of synchronizing the two objects... an XmlDataDocument object is instantiated After that, the ReadXmlSchema() method of the DataSet object is used to load the XML schema After the schema is loaded, the actual XML data is then loaded using the Load() method of XmlDataDocument object Now that the schema as well as the XML data is loaded into the XmlDataDocument object, you can now retrieve the DataSet using the DataSet property of the XmlDataDocument... class is derived from the XmlDocument class, it supports all the properties and methods of the XmlDocument class Additionally, the XmlDataDocument class has its own properties and methods for providing a relational view of the data contained in it Table 8 -5 discusses the properties and methods of the XmlDataDocument in this context Table 8 -5 Important Properties and Methods of the XmlDataDocument Class... literal controls ltlXmlData.Text = Server.HtmlEncode(contactsDataSet.GetXml()); ltlXmlSchema.Text = Server.HtmlEncode(contactsDataSet.GetXmlSchema()); } 227 Chapter 8 Getting XML as a String from a DataSet . XML document, the ReadXmlSchema() method interprets the schema 22 1 XML and ADO .NET 11 _59 67 72 ch08.qxd 12/ 13 / 05 11:14 PM Page 22 1 from the elements in the XML document. When you use the ReadXmlSchema(). the inline schema. 21 5 XML and ADO .NET 11 _59 67 72 ch08.qxd 12/ 13 / 05 11:14 PM Page 21 5 Now that you have some information on the ReadXml() method and how to process XML, load some XML data into a. mapping. With ADO .NET, there are two places that you can implement mapping: at the query level, or in the DataAdapter. 22 5 XML and ADO .NET 11 _59 67 72 ch08.qxd 12/ 13 / 05 11:14 PM Page 22 5 The SQL language

Ngày đăng: 12/08/2014, 23:22

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan