自訂 WSDL 應用程式的 C# 程式碼列表
未來的 Microsoft SQL Server 版本將移除這項功能。請避免在新的開發工作中使用這項功能,並規劃修改目前使用這項功能的應用程式。
[!附註]
如需將下列範例程式碼加入 Visual Studio 專案中的指示,請參閱<建立自訂的 WSDL 應用程式>。
using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Data.Sql;
using SqlDataRecord=Microsoft.SqlServer.Server.SqlDataRecord;
namespace MSSql
{
/// <summary>
/// This class provides a sample template of the sequence of actions required to generate a custom WSDL.
/// The design of this class is as such that different detailed implementations performing the same type of
/// work can be customized for the specific task.
/// </summary>
public class CustomWSDL
{
/// <summary>
/// The parameters listed below is the set of parameters that SQL Server will pass to the WSDL generation stored procedure.
/// This CLR stored procedure uses the new Visual Studio 2005 System.Data.SqlServer namespace functionalities to
/// call the original WSDL generation stored procedure. This method then modifies the WSDL to fit the specific
/// SOAP client needs.
/// </summary>
/// <param name="iEndPointID">The endpoint id stored in the SQL metadata.
/// <see cref="int"/>
/// </param>
/// <param name="bIsSSL">Specifies whether or not the WSDL GET connection was over SSL or not.
/// <see cref="bool"/>
/// </param>
/// <param name="strHost">The HTTP Host header value. Usually it is the name of the machine
/// <see cref="char*"/>
/// </param>
/// <param name="strQueryString">The URL query string that respresents if it is a WSDL Get.
/// <see cref="char*"/>
/// </param>
/// <param name="strUserAgent">The HTTP user-agent header value. Usually it is the name of the application
/// sending the WSDL request.
/// <see cref="char*"/>
/// </param>
//[SqlProcedure]
public static void GenerateWSDL(SqlInt32 iEndPointID, SqlBoolean bIsSSL, SqlString strHost, SqlString strQueryString, SqlString strUserAgent)
{
SqlPipe spPipe = SqlContext.Pipe; // connection pipe to the client
// Retrieve WSDL from SQL server
String strWSDL = RetrieveWSDL(iEndPointID, bIsSSL, strHost, strQueryString, strUserAgent, spPipe);
if (null == strWSDL)
{
spPipe.Send("Error retrieving original WSDL.");
return;
}
// customize the WSDL to fit your client application needs
strWSDL = UpdateWSDL(strWSDL, strQueryString.Value);
if (null == strWSDL)
{
spPipe.Send("Error while trying to customize WSDL.");
return;
}
// Return the new WSDL to the client.
ReturnWSDL(strWSDL, spPipe);
}
/// <summary>
/// Retrieves the original WSDL stored or generated by SQL Server and return the WSDL document as a String or NULL if error occurs.
/// </summary>
/// <param name="iEndPointID">The endpoint id stored in the SQL metadata.
/// <see cref="SqlInt32"/>
/// </param>
/// <param name="bIsSSL">Specifies whether or not the WSDL GET connection was over SSL or not.
/// <see cref="SqlBoolean"/>
/// </param>
/// <param name="strHost">The HTTP Host header value. Usually it is the name of the machine
/// <see cref="SqlString"/>
/// </param>
/// <param name="strQueryString">The URL query string that is specified on the URL.
/// <see cref="SqlString"/>
/// </param>
/// <param name="strUserAgent">The HTTP user-agent header value.
/// <see cref="SqlString"/>
/// </param>
/// <param name="spPipe">The connection back to the client. Used for sending information back to the client.
/// <see cref="SqlPipe"/>
/// </param>
/// <returns>System.String</returns>
private static String RetrieveWSDL(SqlInt32 iEndPointID, SqlBoolean bIsSSL, SqlString strHost, SqlString strQueryString, SqlString strUserAgent, SqlPipe spPipe)
{
String strReturnValue = null;
SqlConnection sqlConn = new SqlConnection ("context connection = true");
sqlConn.Open();
SqlCommand myCommand = sqlConn.CreateCommand();
if (null == myCommand)
{
spPipe.Send("Error creating SqlCommand object.");
goto ret;
}
//myPipe.Send("Testing custom wsdl creation.");
// example of the call to the default WSDL generation SP
// sp_http_generate_wsdl_defaultsimpleorcomplex 65540, 0, N'server', N'wsdl', N'myApp'
myCommand.CommandText = "sys.sp_http_generate_wsdl_defaultsimpleorcomplex";
myCommand.CommandType = CommandType.StoredProcedure;
//myPipe.Send("SP to be called: " + myCommand.CommandText);
if (!strQueryString.Value.StartsWith("wsdl", true, System.Globalization.CultureInfo.InvariantCulture))
{
spPipe.Send("Error: Not a WSDL request.");
goto ret;
}
// set the parameter values
myCommand.Parameters.Add(new SqlParameter("@EndpointID", SqlDbType.Int));
myCommand.Parameters[0].Value = iEndPointID;
myCommand.Parameters.Add(new SqlParameter("@IsSSL", SqlDbType.Bit));
myCommand.Parameters[1].Value = bIsSSL;
myCommand.Parameters.Add(new SqlParameter("@Host", SqlDbType.NVarChar, strHost.Value.Length));
myCommand.Parameters[2].Value = strHost;
if (strQueryString.Value.ToLower(System.Globalization.CultureInfo.InvariantCulture).IndexOf("extended") > 0)
{
// generated WSDL will use SqlTypes for datatypes
myCommand.Parameters.Add(new SqlParameter("@QueryString", SqlDbType.NVarChar, 11));
myCommand.Parameters[3].Value = "wsdlcomplex";
}
else
{
myCommand.Parameters.Add(new SqlParameter("@QueryString", SqlDbType.NVarChar, 4));
myCommand.Parameters[3].Value = "wsdl";
}
myCommand.Parameters.Add(new SqlParameter("@UserAgent", SqlDbType.NVarChar, strUserAgent.Value.Length));
myCommand.Parameters[4].Value = strUserAgent;
// execute query to retrieve WSDL
SqlDataReader oReader = myCommand.ExecuteReader();
if (null == oReader)
{
spPipe.Send("Error occurred during execution of SqlCommand.");
goto ret;
}
if (oReader.HasRows) // make sure there actually data from the server
{
if (oReader.Read()) // make sure the read doesn't fail
{
strReturnValue = oReader.GetSqlValue(0).ToString();
}
}
ret:
return strReturnValue;
}
/// <summary>
/// Updates/customizes the WSDL document for the specific client platform. Returns NULL if error occurs.
/// </summary>
/// <param name="strWsdlOrg">The original WSDL document in String format.
/// <see cref="System.String"/>
/// </param>
/// <param name="strUserAgent">The application name requesting for WSDL.
/// <see cref="System.String"/>
/// </param>
/// <returns>System.String</returns>
private static String UpdateWSDL(String strWsdlOrg, String strUserAgent)
{
// if the original WSDL was not dynamically generated by SQL 2005 in a previous call
// make sure that the soap:address information in the WSDL document is correct
// <wsdl:port name="my_endpoint" binding="tns:my_endpointSoap">
// <soap:address location="https://servername/sql/myendpoint" />
// </wsdl:port>
//Note that this particular sample uses String manipulation to modify the WSDL
// if you want to modify the WSDL document using an XMLDom object, you can use the following
// System.IO.StringReader srReader = new System.IO.StringReader(strWsdlOrg);
// System.Xml.XmlDocument xdWSDL = new System.Xml.XmlDocument();
// xdWSDL.Load(myReader);
String strLCUserAgent = strUserAgent.ToLower();
if (strLCUserAgent.IndexOf("everett") > -1)
{
// For client applications created using .NET Frameworks 1.1
return UpdateWsdlForVS2003(strWsdlOrg);
}
if (strLCUserAgent.IndexOf("jbuilder") > -1)
{
return UpdateWsdlForJBuilder(strWsdlOrg);
}
if (strLCUserAgent.IndexOf("glue") > -1)
{
return UpdateWsdlForGLUE(strWsdlOrg);
}
// default case
return strWsdlOrg;
}
/// <summary>
/// Sends the updated WSDL to the client.
/// </summary>
/// <param name="strWSDL">The WSDL document to be returned to the client in String format.
/// <see cref="String"/>
/// </param>
/// <param name="spPipe">The connection to be used to send the WSDL back to the client.
/// <see cref="SqlPipe"/>
/// </param>
private static void ReturnWSDL(String strWSDL, SqlPipe spPipe)
{
int iMaxLength = 4000; // The max length supported by SQL Server NVarChar is 4000 characters
// setup the required objects to send the new WSDL back to the client.
// the MetaData object is used to denote what each of the data records represent
SqlMetaData[] oMetaData = new SqlMetaData[1];
// XML_F52E2B61-18A1-11d1-B105-00805F49916B is the column name that denotes a FOR XML statement
// This is the required column name to return the WSDL document.
oMetaData[0] = new SqlMetaData("XML_F52E2B61-18A1-11d1-B105-00805F49916B",
SqlDbType.NVarChar,
iMaxLength,
1033, // note: using US-EN
SqlCompareOptions.None);
if (null == oMetaData[0])
{
spPipe.Send("Error creating the required SqlMetaData object for response.");
goto ret;
}
// making sure that we are not going outside of the range of available characters in the WSDL document
if (strWSDL.Length < iMaxLength)
iMaxLength = strWSDL.Length;
// variable to hold the value that will be set as part of a SqlDataRecord
object[] aoResponse = new object[1];
aoResponse[0] = new object();
if (null == aoResponse[0])
{
spPipe.Send("Error creating the object to hold the SqlDataRecord value.");
goto ret;
}
aoResponse[0] = strWSDL.Substring(0, iMaxLength);
//myPipe.Send("Try creating a SqlDataRecord.");
SqlDataRecord oRecord = new SqlDataRecord(oMetaData);
if (null == oRecord)
{
spPipe.Send("Error creating SqlDataRecord.");
goto ret;
}
oRecord.SetValues(aoResponse);
// spPipe.SendResultsStart(oRecord); // send the first block of data
spPipe.SendResultsStart(oRecord); // send the first block of data
spPipe.SendResultsRow(oRecord); // send the first block of data
int iccLeft = strWSDL.Length - iMaxLength;
int iLength = strWSDL.Length;
while (iccLeft > 0) // check to see if there are more blocks to send
{
if (iccLeft > iMaxLength)
{
oRecord.SetString(0, strWSDL.Substring(iLength - iccLeft, iMaxLength));
spPipe.SendResultsRow(oRecord);
iccLeft = iccLeft - iMaxLength;
}
else // iccLeft = iMaxLength or less
{
oRecord.SetString(0, strWSDL.Substring(iLength - iccLeft, iccLeft));
spPipe.SendResultsRow(oRecord);
iccLeft = 0;
}
}
spPipe.SendResultsEnd(); // let the client know that there is no more data to send.
ret:
return;
}
/// <summary>
/// Modifies the SQL 2005 generated WSDL for consumption by VS 2003. Returns NULL if error occurs
/// </summary>
/// <param name="strWsdlOrg">The SQL 2005 generated WSDL document in String format
/// <see cref="System.String"/>
/// </param>
/// <returns>System.String</returns>
private static String UpdateWsdlForVS2003(String strWsdlOrg)
{
const string strFacet = " maxOccurs=\"unbounded\" minOccurs=\"0\" processContents=\"lax\" ";
String strReturn = strWsdlOrg;
if (null == strReturn)
{
goto ret;
}
// For VS 2003 need to find the following text
// <xsd:complexType name="SqlRowSet">
// <xsd:sequence maxOccurs="unbounded">
// <xsd:element ref="xsd:schema" />
// <xsd:any />
// </xsd:sequence>
// </xsd:complexType>
//
// Need to change the above section to
// <xsd:complexType name="SqlRowSet">
// <xsd:sequence maxOccurs="unbounded">
// <xsd:any maxOccurs="unbounded" minOccurs="0" processContents="lax" />
// </xsd:sequence>
// </xsd:complexType>
// hardcoded to match the WSDL generated by SQL 2005
// will need to be modified accordingly if the WSDL was not generated by SQL 2005
string strTemp = "<xsd:element ref=\"xsd:schema\"/>";
// first look for the <xsd:complexType name="SqlRowSet"> node
int iIndex = strReturn.IndexOf("complexType name=\"SqlRowSet\"");
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now look for the <xsd:element> node from the <xsd:complexType name="SqlRowSet"> node
iIndex = strReturn.IndexOf(strTemp, iIndex);
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now that we have found what we want, remove it
strReturn = strReturn.Remove(iIndex, strTemp.Length);
// hardcoded to match the WSDL generated by SQL 2005
// will need to be modified accordingly if the WSDL was not generated by SQL 2005
strTemp = "/>";
// look for the end of the <xsd:any> node
iIndex = strReturn.IndexOf(strTemp, iIndex);
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// insert the attributes
strReturn = strReturn.Insert(iIndex, strFacet);
ret:
return strReturn;
}
/// <summary>
/// Modifies the SQL 2005 generated WSDL for consumption by JBuilder 9. Returns NULL if error occurs
/// </summary>
/// <param name="strWsdlOrg">The SQL 2005 generated WSDL document in String format
/// <see cref="System.String"/>
/// </param>
/// <returns>System.String</returns>
private static String UpdateWsdlForJBuilder(String strWsdlOrg)
{
String strReturn = strWsdlOrg;
if (null == strReturn)
{
goto ret;
}
// For JBuilder need to find the following text
// <xsd:complexType name="SqlRowSet">
// <xsd:sequence maxOccurs="unbounded">
// <xsd:element ref="xsd:schema" />
// <xsd:any />
// </xsd:sequence>
// </xsd:complexType>
//
// Need to change the above section to
// <xsd:complexType name="SqlRowSet">
// <xsd:sequence maxOccurs="unbounded">
// <xsd:any minOccurs="0" processContents="lax" />
// </xsd:sequence>
// </xsd:complexType>
// hardcoded to match the WSDL generated by SQL 2005
// will need to be modified accordingly if the WSDL was not generated by SQL 2005
string strTemp = "<xsd:element ref=\"xsd:schema\"/>";
string strFacet = " minOccurs=\"0\" processContents=\"lax\" ";
// first look for the <xsd:complexType name="SqlRowSet"> node
int iIndex = strReturn.IndexOf("complexType name=\"SqlRowSet\"");
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now look for the <xsd:element> node from the <xsd:complexType name="SqlRowSet"> node
iIndex = strReturn.IndexOf(strTemp, iIndex);
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now that we have found what we want, remove it
strReturn = strReturn.Remove(iIndex, strTemp.Length);
// hardcoded to match the WSDL generated by SQL 2005
// will need to be modified accordingly if the WSDL was not generated by SQL 2005
strTemp = "/>";
// look for the end of the <xsd:any> node
iIndex = strReturn.IndexOf(strTemp, iIndex);
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// insert the attributes
strReturn = strReturn.Insert(iIndex, strFacet);
ret:
return strReturn;
}
/// <summary>
/// Modifies the SQL 2005 generated WSDL for consumption by GLUE 5.0.1Pro. Returns NULL if error occurs
/// </summary>
/// <param name="strWsdlOrg">The SQL 2005 generated WSDL document in String format
/// <see cref="System.String"/>
/// </param>
/// <returns>System.String</returns>
private static String UpdateWsdlForGLUE(String strWsdlOrg)
{
const string strFacet = " maxOccurs=\"unbounded\" minOccurs=\"0\" processContents=\"lax\" ";
const string strXsdList = "<xsd:list itemType=\"sqltypes:sqlCompareOptionsEnum\" />";
String strReturn = strWsdlOrg;
if (null == strReturn)
{
goto ret;
}
// For GLUE need to find the following text
// <xsd:simpleType name="sqlCompareOptionsList">
// <xsd:list itemType="sqltypes:sqlCompareOptionsEnum">
// </xsd:simpleType>
//
// This needs to be changed to
// <xsd:simpleType name="sqlCompareOptionsList">
// <xsd:restriction base="xsd:string" />
// </xsd:simpleType>
// hardcoded to match the WSDL generated by SQL 2005
// will need to be modified accordingly if the WSDL was not generated by SQL 2005
// first look for the <xsd:simpleType name="sqlCompareOptionsList"> node
int iIndex = strReturn.IndexOf("simpleType name=\"sqlCompareOptionsList\"");
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now look for the <xsd:list> node from the <xsd:simpleType name="sqlCompareOptionsList"> node
iIndex = strReturn.IndexOf(strXsdList, iIndex);
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now that we have found <xsd:list>, remove it
strReturn = strReturn.Remove(iIndex, strXsdList.Length);
// now insert the new xsd:restriction info
// <xsd:restriction base="xsd:string" />
strReturn = strReturn.Insert(iIndex, "<xsd:restriction base=\"xsd:string\" />");
// Need to also find
// <xsd:complexType name="SqlRowSet">
// <xsd:sequence maxOccurs="unbounded">
// <xsd:element ref="xsd:schema" />
// <xsd:any />
// </xsd:sequence>
// </xsd:complexType>
//
// Need to change the above section to
// <xsd:complexType name="SqlRowSet">
// <xsd:sequence maxOccurs="unbounded">
// <xsd:any maxOccurs="unbounded" minOccurs="0" processContents="lax" />
// </xsd:sequence>
// </xsd:complexType>
string strTemp = "<xsd:element ref=\"xsd:schema\"/>";
// first look for the <xsd:complexType name="SqlRowSet"> node
iIndex = strReturn.IndexOf("complexType name=\"SqlRowSet\"");
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now look for the <xsd:element> node from the <xsd:complexType name="SqlRowSet"> node
iIndex = strReturn.IndexOf(strTemp, iIndex);
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// now that we have found what we want, remove it
strReturn = strReturn.Remove(iIndex, strTemp.Length);
// hardcoded to match the WSDL generated by SQL 2005
// will need to be modified accordingly if the WSDL was not generated by SQL 2005
strTemp = "/>";
// look for the end of the <xsd:any> node
iIndex = strReturn.IndexOf(strTemp, iIndex);
if (iIndex <= 0)
{
strReturn = null;
goto ret;
}
// insert the attributes
strReturn = strReturn.Insert(iIndex, strFacet);
ret:
return strReturn;
}
}
}