570 likes | 709 Views
More Visual Studio Data Binding and Web Services. Dr Kevin McManus http://staffweb.cms.gre.ac.uk/~mk05/web/dotnet/#lecture3. Data Binding. Data binding in .NET is much improved on previous Microsoft implementations Use data binding to make a link between the user interface and the data model
E N D
More Visual StudioData Binding and Web Services Dr Kevin McManus http://staffweb.cms.gre.ac.uk/~mk05/web/dotnet/#lecture3 University of Greenwich
Data Binding • Data binding in .NET is much improved on previous Microsoft implementations • Use data binding to make a link between the user interface and the data model • Create a C# Web Site project • Bind a property and a method to GUI components • DataBind() University of Greenwich
Data Binding add a calendar, three buttons and a label to the design surface University of Greenwich
Data Binding add a level 1 header add some executable code to the header, calendar and label give the components sensible names and values - this can be done using the property browser in design view University of Greenwich
Data Binding property <body> <h1>Airline: <%# airline %></h1> <form id="form1" runat="server"> <div> <asp:Calendar ID="calDepart" runat="server" SelectedDate="<%# setCalendar() %>"></asp:Calendar> <br /> </div> <asp:Button ID="btnToday" runat="server" Text="Today" /> <asp:Button ID="btnTomorrow" runat="server" Text="Tomorrow" /> <asp:Button ID="btnNextWeek" runat="server" Text="Next Week" /><br /> <br /> <asp:Label ID="lblDepartDate" runat="server" Text="Departure date"> <%# calDepart.SelectedDate.ToShortDateString() %> </asp:Label> </form> </body> methods add code to the code behind module to handle all this double click the GUI widgets to get the event handlers University of Greenwich
Data Binding protected int futureDay = 0; public string airline { get { return "Acme Airways"; } } public DateTime setCalendar() { return DateTime.Today.AddDays(futureDay); } private void Page_Load(object sender, System.EventArgs e) { DataBind(); } long winded property constructor method to set the calendar data bind the page on load University of Greenwich
Data Binding private void btnToday_Click(object sender, System.EventArgs e) { futureDay = 0; DataBind(); } private void btnTomorrow_Click(object sender, System.EventArgs e) { futureDay = 1; DataBind(); } private void btnNextWeek_Click(object sender, System.EventArgs e) { futureDay = 7; DataBind(); } private void calDepart_SelectionChanged(object sender, System.EventArgs e) { lblDepartDate.DataBind(); } data bind everything University of Greenwich
Data Binding couldn't resist correcting the markup for this form gotta have a green bar from the W3 note how this is all postback driven every mouse click fires an HTTP request University of Greenwich
Connecting to Databases • Visual Studio integrates with SQL Server • edit SQL Server data directly in VS.NET • execute SQL statements • VS Server Explorer provides easy connection to databases View -> Server Explorer • Data binding links controls on the user interface to data stored in SQL Server • now largely automated in VS05 University of Greenwich
Binding to a Datagrid • Create a new C# Web Application project • give it a meaningful name • give the web form a meaningful name • Use the Server Explorer to locate your SQL Server database • sql-server.cms.gre.ac.uk University of Greenwich
Create a new C# Web Application project give it a meaningful name give the web form a meaningful name Use the Server Explorer to locate your SQL Server database sql-server.cms.gre.ac.uk Binding to a Datagrid University of Greenwich
Binding to a Datagrid drag a table onto the design surface automatically creates a datagrid and invites you to configure it University of Greenwich
Binding to a Datagrid configure what you want in the grid choose from some imaginatively named preset styles University of Greenwich
Telephone Directory • In ye olden days you had to do a lot of this stuff manually • dive in and data bind stuff • All now tooled up and automated • Repeat the exercise as a Web Service • Right click the project in the solution explorer and add a web service • rename it phoneBook.asmx University of Greenwich
Telephone Directory Web Service Add a web service to the project Give it a sensible name University of Greenwich
Telephone Directory Web Service using System.Data; using System.Data.SqlClient; [WebMethod] publicDataSet getDirectorySql() { // Open a connection the the SQL Server SqlConnection sqlConn = newSqlConnection(); sqlConn.ConnectionString = "server=sql-server;integrated security=SSPI;database=mk05"; // Create a SQL query SqlCommand sqlCmd = sqlConn.CreateCommand(); sqlCmd.CommandType = CommandType.Text; sqlCmd.CommandText = "SELECT fname, sname, phone, loc FROM phoneBook"; // Set up data adapter and fill the dataset SqlDataAdapter dataAdptr = newSqlDataAdapter(); dataAdptr.SelectCommand = sqlCmd; DataSet dsStaff = newDataSet("directory"); dataAdptr.Fill(dsStaff, "staff"); // And return it to the client return dsStaff; } add data handling system libraries connection string provided by our sysops University of Greenwich
the DataSet is returned in XML format with schema included University of Greenwich
.NET XML Containers • Three XML document stores each with different features • XmlDocument • the primary document store • supports W3C standards for XML document (DOM), plus useful additional features that make it easier to access the contents • XmlDataDocument • provides a bridge between XML and relational data by allowing the content to be accessed as a DataSet instance as well as a standard XML document • XPathDocument. • fast and efficient document store designed to be accessed only using XPath methods and the XPathNavigator • XPathNavigator can be used over any of the three document stores University of Greenwich
Telephone Directory Web Service • Re-write the web service to return an XmlDocument • instead of a data set • Convert the data set read from SQL Server into a string • Load the string into an XmlDocument University of Greenwich
Telephone Directory Web Service using System.Xml; [WebMethod] publicXmlDocument getDirectoryDom() { // Open a connection the the SQL Server SqlConnection sqlConn = newSqlConnection(); sqlConn.ConnectionString = "server=sql-server;integrated security=SSPI;database=mk05"; // Create a SQL query SqlCommand sqlCmd = sqlConn.CreateCommand(); sqlCmd.CommandType = CommandType.Text; sqlCmd.CommandText = "SELECT fname, sname, phone, loc FROM phoneBook"; // Set up data adapter and fill the dataset SqlDataAdapter dataAdptr = newSqlDataAdapter(); dataAdptr.SelectCommand = sqlCmd; DataSet dsStaff = newDataSet("directory"); dataAdptr.Fill(dsStaff, "staff"); // Create an XML document and read the data set into it XmlDocument xmlDom = newXmlDocument(); xmlDom.LoadXml(dsStaff.GetXml()); return xmlDom; } add XML system library this is all the same as before get the data set as a string and then load it into an XML document
clean XML returned we could easily manipulate this XML further using DOM programming University of Greenwich
Telephone Directory Web Service • Microsoft Access database uses an OLEDB JET connection string • System.Data.OleDb • old fashioned • insecure • popular • Next example uses DOM to read data from Access in XML format University of Greenwich
Telephone Directory Web Service using System.Data.OleDb [WebMethod] publicXmlDocument getDirectoryOledb() { // Open OLEDB connection to Access database OleDbConnection DBconnection = newOleDbConnection ("PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA Source=e:\\webareas\\mk05\\phoneBook.mdb"); DBconnection.Open(); // Create and execute SQL query OleDbCommand cmdSelectUsers = newOleDbCommand ("SELECT firstname, surname, phone, location FROM staff, DBconnection); OleDbDataReader dataStaff = cmdSelectUsers.ExecuteReader(); // Instantiate an XML document XmlDocument xmlDom = newXmlDocument(); xmlDom.AppendChild(xmlDom.CreateElement("", "phoneBook", "")); XmlElement xmlRoot = xmlDom.DocumentElement; // Create local references XmlElement xmlStaff, xmlFname, xmlSname, xmlTel, xmlLoc; XmlText xmlTxt; string firstname, surname, telephone, location; add the OleDb sytsem library identify the root element University of Greenwich
while ( dataStaff.Read() ) { // Retrieve the records from the data set firstname = dataStaff.GetString(0); surname = dataStaff.GetString(1); telephone = dataStaff.GetInt32(2).ToString(); location = dataStaff.GetString(3); // Create XML elements for the records and append them to the DOM xmlStaff = xmlDom.CreateElement("staff"); xmlFname = xmlDom.CreateElement("fname"); xmlText = xmlDom.CreateTextNode(firstname); xmlFname.AppendChild(xmlText); xmlStaff.AppendChild(xmlFname); xmlSname = xmlDom.CreateElement("sname"); xmlText = xmlDom.CreateTextNode(surname); xmlSname.AppendChild(xmlText); xmlStaff.AppendChild(xmlSname); xmlTel = xmlDom.CreateElement("phone"); xmlText = xmlDom.CreateTextNode(telephone); xmlTel.AppendChild(xmlText); xmlStaff.AppendChild(xmlTel); xmlLoc = xmlDom.CreateElement("loc"); xmlText = xmlDom.CreateTextNode(location); xmlLoc.AppendChild(xmlText); xmlStaff.AppendChild(xmlLoc); xmlRoot.AppendChild(xmlStaff); } dataStaff.Close(); DBconnection.Close(); return xmlDom; } loop over the data set create and append elements into the DOM return the DOM
looks remarkably similar to the SQL Server web service University of Greenwich
Telephone Directory Web Service • Create a .NET web service that accesses XML from a URL • That URL could itself be a web service • getDirectory.php reads a staff telephone directory from a MySQL database • returns a DOM object • is a RESTful web service • uses the PHP5 DOM model • stuweb.cms.gre.ac.uk University of Greenwich
<?php header('Content-type: text/xml'); require'/home/mkg01/include/mysql.php'; $link = mysql_connect($host, $user, $passwd); mysql_select_db($dbName); $query = 'SELECT firstname, surname, phone, location FROM staff ORDER BY surname'; $result = mysql_query($query,$link); if ( mysql_num_rows($result) > 0 ) { $xmlDom = new DOMDocument(); $xmlDom->appendChild($xmlDom->createElement('directory')); $xmlRoot = $xmlDom->documentElement; while ( $row = mysql_fetch_row($result) ) { $xmlPerson = $xmlDom->createElement('staff'); $xmlFname = $xmlDom->createElement('fname'); $xmlText = $xmlDom->createTextNode($row[0]); $xmlFname->appendChild($xmlText); $xmlPerson->appendChild($xmlFname); $xmlSname = $xmlDom->createElement('sname'); $xmlText = $xmlDom->createTextNode($row[1]); $xmlSname->appendChild($xmlText); $xmlPerson->appendChild($xmlSname); $xmlTel = $xmlDom->createElement('phone'); $xmlText = $xmlDom->createTextNode($row[2]); $xmlTel->appendChild($xmlText); $xmlPerson->appendChild($xmlTel); $xmlLoc = $xmlDom->createElement('loc'); $xmlText = $xmlDom->createTextNode($row[3]); $xmlLoc->appendChild($xmlText); $xmlPerson->appendChild($xmlLoc); $xmlRoot->appendChild($xmlPerson); } } echo$xmlDom->saveXML(); ?> output XML mime type query MySQL database loop over the query results and create a DOM document just like the previous C# example getDirectory.php write the DOM back to the client
a now familiar sight University of Greenwich
Telephone Directory Web Service [WebMethod] public XmlDocument getDirectoryUrlDataSet() { // Instantiate a data set and read XML into it from a URL DataSet dataSetUrl = new DataSet("directory"); dataSetUrl.ReadXml ("http://stuweb.cms.gre.ac.uk/~mkg01/staffWS/getDirectory.php"); // Create an XML document and read the data set into it XmlDocument xmlDom = new XmlDocument(); xmlDom.LoadXml(dataSetUrl.GetXml()); // And return it to the client return xmlDom; } This is not the best way to read from an external resource as there is no way to validate the incoming data Use an XmlTextReader instead University of Greenwich
Telephone Directory Web Service [WebMethod] publicXmlDocument getDirectoryUrlTextReader() { // Instantiate an XML text reader and read XML into it from a URL XmlTextReader myXmlTextReader = newXmlTextReader ("http://stuweb.cms.gre.ac.uk/~mkg01/staffWS/getDirectory.php"); // Create an XML document and load the XML text reader into it XmlDocument xmlDom = newXmlDocument(); xmlDom.Load(myXmlTextReader); // And return it to the client return xmlDom; } XmlTextReader is a stream XML processor rather like SAX although SAX is not supported by .NET. This checks that the XML is well formed and enables the XML to be validated as it is read. University of Greenwich
Telephone Directory Web Service [WebMethod] publicXmlDocument getDirectoryUrlValidating() { // Instantiate an XML text reader and read XML into it from a URL XmlTextReader xmlTR = newXmlTextReader ("http://stuweb.cms.gre.ac.uk/~mkg01/staffWS/getDirectory.php"); XmlValidatingReader xmlVR = newXmlValidatingReader(xmlTR); xmlVR.ValidationType = System.Xml.ValidationType.DTD; // Assign a validation failure event handler xmlVR.ValidationEventHandler += new ValidationEventHandler(ValidationHandler); xmlVR.Read(); // Create an XML document and load the XML text reader into it XmlDocument xmlDom = newXmlDocument(); xmlDom.Load(xmlTR); // And return it to the client return xmlDom; } method to handle a validation failure – although it is less than clear what to do with a validation failure in a web service University of Greenwich
XML Aggregation • Web services are used to create interfaces to data resources • Using DOM structures we can operate on the data • Data from many sources can be aggregated into a single DOM object • .NET provides alternative programmatic approaches to XML • DataSet • XmlNavigator • XSLT University of Greenwich
XML Aggregation reading direct from the method we created earlier [WebMethod] publicXmlDocument getDirectories() { // Instantiate an XML document and read the SQL database into it XmlDocument xmlDom1 = newXmlDocument(); xmlDom1 = getDirectoryDom(); // Instantiate an XML text reader and read XML into it from a URL XmlTextReader myXmlTextReader = new XmlTextReader ("http://stuweb.cms.gre.ac.uk/~mkg01/staffWS/getDirectory.php"); // Create an XML document and load the XML text reader into it XmlDocument xmlDom2 = newXmlDocument(); xmlDom2.Load(myXmlTextReader); foreach ( XmlElement xmlNode in xmlDom2.DocumentElement.ChildNodes ) { xmlDom1.DocumentElement.AppendChild(xmlNode); } // And return it to the client return xmlDom1; } loop over each node in the second DOM and append the node to the root in the first DOM
XML Aggregation Ooops! Clearly something is not happy here. The nodes do not belong to the document we are adding them into – we need to import them or else roll up out sleeves and do it manually with some DOM programming University of Greenwich
XML Aggregation • This does the trick • Although we could always do it the hard way • which would allow us to adjust the DOM structure as we migrate nodes // Loop over each child node in second XML document foreach ( XmlNode xmlNode2 in xmlDom2.DocumentElement.ChildNodes ) { // Import each child node into the first XML document XmlNode xmlNode1 = xmlDom1.ImportNode(xmlNode2, true); xmlDom1.DocumentElement.AppendChild(xmlNode1); } University of Greenwich
XML Aggregation // Create some pointers XmlElement xmlStaff, xmlEle; XmlText xmlTxt; // Loop over the staff nodes in the second XML document foreach ( XmlNode xmlStaffNode in xmlDom2.DocumentElement.ChildNodes ) { // Create a new staff element xmlStaff = xmlDom1.CreateElement(xmlStaffNode.Name); // Loop over the children of the staff element in the second document foreach ( XmlNode xmlNode in xmlStaffNode.ChildNodes ) { // Create new child elements xmlEle = xmlDom1.CreateElement(xmlNode.Name); xmlTxt = xmlDom1.CreateTextNode(xmlNode.InnerText); xmlEle.AppendChild(xmlext); xmlStaff.AppendChild(xmlEle); } // Append the new staff element to the first XML document xmlDom1.DocumentElement.AppendChild(xmlStaff); } University of Greenwich
hurrah! either way we manage to aggregate the content
XML Aggregation • Of course we could try a different approach using data sets instead of XML documents • Data sets provide a useful merge method • although this will complain if data types do not match • e.g. are telephone numbers strings? • converting everything to strings is one work around University of Greenwich
[WebMethod] publicXmlDocument getDirectories2() { // Create an XML text reader to read the ASP.NET Access web service XmlTextReader xmlReader1 = newXmlTextReader ("http://cms-stu-iis.gre.ac.uk/mk05/..../getDirectoryOledb"); DataSet dataSetReader1 = new DataSet(); dataSetReader1.ReadXml(xmlReader1); // Create an XML text reader to read the PHP web service XmlTextReader xmlReader2 = newXmlTextReader ("http://stuweb.cms.gre.ac.uk/~mkg01/staffWS/getDirectory.php"); // Create another data set and load it from the text reader DataSet dataSetReader2 = newDataSet(); dataSetReader2.ReadXml(xmlReader2); // Merge the two data sets dataSetReader1.Merge(dataSetReader2); // Instantiate an XML document and load the merged data set into it XmlDocument xmlDom = newXmlDocument(); xmlDom.LoadXml(dataSetReader1.GetXml()); // And return it to the client return xmlDom; } data sets provide a merge method although it will complain if the data types don’t match University of Greenwich
<?php header('Content-type: text/xml'); require'/home/mkg01/include/mysql.php'; $link = mysql_connect($host, $user, $passwd); mysql_select_db($dbName); $query = 'SELECT firstname, surname, phone, location FROM staff ORDER BY surname'; $result = mysql_query($query,$link); if ( mysql_num_rows($result) > 0 ) { $xmlDom = new DOMDocument(); $xmlDom->appendChild($xmlDom->createElement('directory')); $xmlRoot = $xmlDom->documentElement; while ( $row = mysql_fetch_row($result) ) { $xmlPerson = $xmlDom->createElement('staff'); $xmlFname = $xmlDom->createElement('fname'); $xmlText = $xmlDom->createTextNode($row[0]); $xmlFname->appendChild($xmlText); $xmlPerson->appendChild($xmlFname); $xmlSname = $xmlDom->createElement('sname'); $xmlText = $xmlDom->createTextNode($row[1]); $xmlSname->appendChild($xmlText); $xmlPerson->appendChild($xmlSname); $xmlTel = $xmlDom->createElement('phone'); $xmlText = $xmlDom->createTextNode($row[2]); $xmlTel->appendChild($xmlText); $xmlPerson->appendChild($xmlTel); $xmlLoc = $xmlDom->createElement('loc'); $xmlText = $xmlDom->createTextNode($row[3]); $xmlLoc->appendChild($xmlText); $xmlPerson->appendChild($xmlLoc); $xmlRoot->appendChild($xmlPerson); } } do the same thing with PHP we've seen all this before soapAggregator.php University of Greenwich
create a SOAP client using the ASP.NET WSDL $client = new SoapClient("http://cms-stu-iis.gre.ac.uk.../phoneBook.asmx?WSDL"); $xmlString = $client->getDirectoryDom()->getDirectoryDomResult->any; $xmlDom2 = new DOMDocument(); $xmlDom2->loadXML($xmlString); foreach ( $xmlDom2->documentElement->childNodes as $staffNode ) { $xmlPerson = $xmlDom1->createElement($staffNode->nodeName); foreach ( $staffNode->childNodes as $xmlNode ) { $xmlElement = $xmlDom1->createElement($xmlNode->nodeName); $xmlText = $xmlDom1->createTextNode($xmlNode->nodeValue); $xmlElement->appendChild($xmlText); $xmlPerson->appendChild($xmlElement); } $xmlRoot->appendChild($xmlPerson); } echo$xmlDom1->saveXML(); ?> merge the two DOM objects soapAggregator.php University of Greenwich
Adding A Processing Instruction [WebMethod] publicXmlDocument showDirectory() { // Instantiate an XML document and read the SQL database into it XmlDocument xmlDom = new XmlDocument(); xmlDom = getDirectoryDom(); // Create a PI and append it to the document XmlProcessingInstruction PI = xmlDom.CreateProcessingInstruction ("xml-stylesheet", "href='directory.xsl' type='text/xsl'"); xmlDom.InsertBefore(PI, xmlDom.DocumentElement); // Return the document to the client return xmlDom; } University of Greenwich
oh no! because this is a Web Method the processing instructions has been stripped out University of Greenwich
Adding Processing Instruction • It is not entirely surprising that the processing instruction is filtered out from a web service • Add a Generic Handler to the project • .ashx • Add code to aggregate XML and add a processing instruction into the page load event • Return all of the XML using the Response object • Don’t forget to also use the Response object to send out the correct mime type • text/xml University of Greenwich
Adding Processing Instruction using System.IO; privatevoid Page_Load(object sender, EventArgs e) { // Instantiate an XML text reader and read XML into it from a URL XmlTextReader xmlTextReader1 = newXmlTextReader ("http://cms-stu-iis.gre.ac.uk/mk05/.../getDirectoryDom"); // Create an XML document and load the XML text reader into it XmlDocument xmlDom1 = newXmlDocument(); xmlDom1.Load(xmlTextReader1); // Instantiate an XML text reader and read XML into it from a URL XmlTextReader xmlTextReader2 = newXmlTextReader ("http://stuweb.cms.gre.ac.uk/~mkg01/staffWS/getDirectory.php"); // Create an XML document and load the XML text reader into it XmlDocument xmlDom2 = newXmlDocument(); xmlDom2.Load(xmlTextReader2); at this stage we have two XML documents obtained from two web services University of Greenwich
Adding Processing Instruction // Loop over each child node in second XML document foreach ( XmlNode xmlNode2 in xmlDom2.DocumentElement.ChildNodes ) { // Import each child node into the first XML document XmlNode xmlNode1 = xmlDom1.ImportNode(xmlNode2, true); xmlDom1.DocumentElement.AppendChild(xmlNode1); } // Create and insert processing instruction XmlProcessingInstruction PI = xmlDom1.CreateProcessingInstruction ("xml-stylesheet", "href=\"directory.xsl\" type=\"text/xsl\""); xmlDom1.InsertBefore(PI, xmlDom1.DocumentElement); // Write it all back to the client StringWriter sw = newStringWriter(); XmlTextWriter xtw = newXmlTextWriter(sw); xmlDom1.WriteTo(xtw); context.Response.ContentType = "text/xml"; context.Response.Write(sw); } output mime type output XML University of Greenwich
Adding Processing Instruction oh hurrah! here the XML returning to the client contains the PI linking to a simple XSLT stylesheet University of Greenwich
Adding Processing Instruction • Try the same trick in PHP… • Add one extra line to getDirectory.php • The nice thing about DOM is the way the code works pretty much the same in both PHP and C# • Add the same DOM code into PHP and aggregate some XML $xmlDom->appendChild($xmlDom->createProcessingInstruction ('xml-stylesheet', 'href="directory.xsl" type="text/xsl"')); University of Greenwich
Adding Processing Instruction XML merged and XSLT processed using PHP with the same DOM code as used in C# University of Greenwich