15.10. Data TypesASP.NET web services can use any CLR-supported primitive data type as a parameter or a return value. Table 15-2 summarizes the valid types. Table 15-2. CLR-supported primitive data types
In addition to the primitive data types, you can use arrays and ArrayLists of the primitive types. Since data is passed between a web service and its clients using XML, whatever is used as either a parameter or return value must be represented in an XML schema or XSD. 15.10.1. ArraysThe examples shown so far in this chapter have used simple primitive types, such as strings and numbers , as parameters and return values. You can also use an array of primitive types as in the code shown here: [WebMethod] public string[] GetArray( ) { string[] TestArray = {"a","b","c"}; return TestArray; } The main limitation of using arrays, of course, is that you must know the number of elements at design time. If the number of elements is dynamic, then an ArrayList will be called for. If an ArrayList is used in the web service, it will be converted to an object array when the web service description is created. The client proxy will return an array of objects , which will then have to be converted to an array of the proper type (string in this case). The ArrayList is contained within the System.Collections namespace. To use an ArrayList , you must include the proper reference with the using keyword: using System.Collections; The code in Example 15-13 contains a web method called GetList . It takes a string as a parameter. This match string is then compared with all the firm names in the data store (the array defined at the top of the web service class shown in Example 15-4), and the web service returns all the firm names that contain the match string anywhere within the firm name . Example 15-13. GetList web method[WebMethod(Description="Returns all the stock symbols whose firm " + "matches the input string as *str*.")] public ArrayList GetList(string MatchString) { ArrayList a = new ArrayList( ); // Iterate through the array, looking for matching firm names. for (int i = 0; i < stocks.GetLength(0); i++) { // Search is case sensitive. if ( stocks[i,1].ToUpper( ).IndexOf(MatchString.ToUpper( )) >= 0) a.Add(stocks[i,1]); } a.Sort( ); return a; } The GetList web method first instantiates a new ArrayList and then iterates through the store of firms. The IndexOf method of the String class is used to do a case-sensitive search in a string, looking for the match string. If it finds a match, it will return the index of the first occurrence. If IndexOf does not find a match, it will return -1 . To implement a case-insensitive search, the code in Example 15-13 first converts both the MatchString and the firm name to uppercase. If the IndexOf method finds a match, the web method adds the firm name to the ArrayList . The firm name is contained in the second field of the array record, the field with index 1 (remember that array indices are zero-based ). After completing the search, the web method sorts the ArrayList before returning it to the client. (As an alternative, you could convert the ArrayList to a strongly typed array using ArrayList.ToArray( ) , before returning it.) To test this, run the web service. From the test page, click on the GetList link. You will get a page similar to Figure 15-8. Figure 15-8. GetList test pageIf you enter "or" (as shown in Figure 15-8), you will see the results that would be returned to a client, as shown in Figure 15-9. Notice that in the test output, Ford comes before General Motors even though their order has been reversed in the input data. That is a result of sorting the ArrayList prior to return. Figure 15-9. GetList test results15.10.2. Classes and StructsWeb services can use user -defined classes and structs as either parameters or return types. These are the rules to remember about which class variables can participate in a web service:
To demonstrate the use of classes with web services, add the class definitions shown in Example 15-14 to the StockTickerComplete example. Example 15-14. Class definitionpublic class Stock { public string StockSymbol; public string StockName; public double Price; public StockHistory[] History = new StockHistory[2]; private string strBroker; public string Broker { get { return strBroker; } set { strBroker = value; } } } public class StockHistory { public DateTime TradeDate; public double Price; } The first class definition, Stock , consists of two strings, a double, an array of type StockHistory , and a public property with an underlying private string. The StockHistory class consists of a date, called the tradeDate , and the stock price on that date.
The web method shown in Example 15-15, GetHistory , uses the Stock class to return stock history data for the stock symbol passed in to it. Example 15-15. GetHistory web method[WebMethod(Description="Returns stock history for " + "the stock symbol specified.")] public Stock GetHistory(string StockSymbol) { Stock stock = new Stock( ); // Iterate through the array, looking for the symbol. for (int i = 0; i < stocks.GetLength(0); i++) { // Do a case-insensitive string compare. if (String.Compare(StockSymbol, stocks[i,0], true) == 0) { stock.StockSymbol = StockSymbol; stock.StockName = stocks[i,1]; stock.Price = Convert.ToDouble(stocks[i,2]); stock.Broker = "Dewy, Cheatum, & Howe"; // Populate the StockHistory data. stock.History[0] = new StockHistory( ); stock.History[0].TradeDate = Convert.ToDateTime("5/1/2005"); stock.History[0].Price = Convert.ToDouble(23.25); stock.History[1] = new StockHistory( ); stock.History[1].TradeDate = Convert.ToDateTime("6/1/2005"); stock.History[1].Price = Convert.ToDouble(28.75); return stock; } } stock.StockSymbol = StockSymbol; stock.StockName = "Stock not found."; return stock; } In the GetHistory method listed in Example 15-15, each class is instantiated before it can be used. Iterating over the array of stocks finds the data to return. The class variables are populated from the array, and then the class itself is returned. If the stock symbol is not found, a message will be placed in a convenient field of the stock class and that is returned. In the Stock object returned by the web service, the private string strBroker is not visible. Furthermore, the public property must have both a get and a set to be visible. Suppose you modified the Stock class by initializing the value of the private string and removing the set accessor from the property, as in the following code snippet: private string strBroker = "Dewy,Cheatum, & Howe"; public string Broker { get { return strBroker; } } Of course, you would then have to also remove the line of code in the GetHistory method that assigns a value to the Broker property, since the public property would now be read-only. If you run the web service and put a breakpoint in the GetHistory method to examine the Stock object about to be returned, the debugger will show the Broker property was set. However, the Broker property will not be returned by the web service because it is not read/write. 15.10.3. DataSetsSince a web service can return any data that can be encoded in an XML file, it can return a DataSet since that is represented internally as XML by ADO.NET. A DataSet is the only type of ADO.NET data store that can be returned by a web service. As an exercise, we will modify an example shown in Chapter 10 to return a DataSet from the Northwind database.
Add the namespaces shown in Example 15-16 to the StockTickerComplete example. Example 15-16. Namespace references for using a DataSetusing System.Data; using System.Data.SqlClient; Now add the web method shown in Example 15-17. This web method, called GeTDataSet , takes no parameters and returns a DataSet object consisting of data about customers from the Northwind database. Example 15-17. GetDataSet web method[WebMethod(Description="Returns a data set from the Northwind database.")] public DataSet GetDataset( ) { // connect to the Northwind database string connectionString = "server=MyServer; uid=sa;pwd=secret;database= Northwind"; // get records from the Customers table string commandString = "Select CompanyName,ContactName, City, Country from Customers"; // create the data set command object and the DataSet SqlDataAdapter dataAdapter = new SqlDataAdapter(commandString, connectionString); DataSet dataSet = new DataSet( ); // fill the data set object dataAdapter.Fill(dataSet,"Customers"); return dataSet; } The code is copied nearly verbatim from the Page_Load method in the code in Example 10-1 (described fully in that chapter). The important thing to note here is that a DataSet object is created from a query and then returned by the web method to the consuming client. |