Applies to:
Yukon Beta 1 Customers
Summary
Prerequisites
Introduction to the Example
Quick Start
Yukon Beta 1 Server Setup Steps:
Step 1 - Creating the Additional T-SQL Stored
Procedures Needed for the Example
Step 2 - Creating the .NET Assembly for SQLCLR
Step 3 - Linking C# Classes to Stored Procedures
Step 4 - Reserving the HTTP Namespace
Step 5 - Creating the HTTP ENDPOINT
SOAP Client Applications:
Step 6 - Creating Client Application using
HttpWebRequest and HttpWebResponse
Step 7 - Creating Client Application Using Proxy Class
Generated by WSDL.EXE
Additional Native HTTP Information
Summary
Starting
with Yukon Beta 1, Microsoft SQL Server provides the ability to expose
native
HTTP methods inside the server. These requests and responses are made
within SOAP packets, which are an extension of XML.
Yukon Beta
1 also provides the ability to allow managed code to be run inside the SQL
Server context. This gives the developer the opportunity to move business
logic inside of the server, instead of using the traditional n-tiered
approach. Developing using managed code also provides the means for
developers to leverage all the advantages of using a managed code
environment.
This document was designed to introduce these new technologies using simple
examples on top of the AdventureWorks database. The following examples
are shown within this document:
| Server Side Technologies |
Create classes in C# that use the SqlContext class to run TSQL statements
within the SQL Server context, but from a managed code environment.
Setup the classes created in C# as stored
procedures. The SQLCLR framework is setup so that
implementation details such as the programming language used to create the
stored procedure are hidden from the calling clients. Stored
procedures created in managed code
or T-SQL are called by the client in the same way.
Reserve a URL namespace for the HTTP Endpoint to be created on
Create an HTTP ENDPOINT which exposes stored procedures created in both C#
and TSQL as well as providing for ad hoc TSQL queries to be passes
|
| |
| Client Side Technologies |
Create a console application in VS.NET that uses the HttpWebRequest and
HttpWebResponse classes to send and receive SOAP packets. This will give
the reader the ability to see the SOAP packets that are passed to and from the
SQL Server.
Create a Windows application on top of the proxy class generated by WSDL
to call the methods exposed by the HTTP ENDPOINT.
|
Prerequisites
To complete this example, there are two prerequisites:
| 1. |
Yukon Beta 1 Properly Installed on a Windows 2003 Server |
| 2. |
Perform this example on the machine with Yukon Beta 1 installed since the
CLR
Framework is needed to create the application |
| 3. |
For the Books Online links within this document to work, Yukon Beta 1 Books
Online is required to be installed. |
Visual Studio is not needed to complete this example since the .NET compiler
shipped with Yukon Beta 1 is used to compile any managed code within this
example.
There are .BAT files included with this example to assist with the compilation
of any C# code.
Introduction to the Example
These samples create additional stored procedures
in the AdventureWorks database and create a HTTP ENDPOINT to expose these
stored
procedures. The
stored procedures that will be used are as
follows:
| Stored Procedure |
Code Base |
Description |
| GetContact |
TSQL |
This stored procedure will return a Contact given a ContactId |
| GetContactCLR |
C# |
This stored procedure is built using C# and provides
the same functionality as GetContact using the SqlContext class and a
parameterized SELECT statement. |
| GetContactSPCLR |
C# |
This stored procedure is built using C# and calls the
GetContact stored procedure, basically acting as a managed code wrapper
that could be used to include additional business logic implemented in
C#. |
| InsertContact |
TSQL |
This stored procedure will insert a row into Contact
and return a scalar value representing the resulting ContactId. |
| InsertContactCLR |
C# |
This stored procedure is built using C# and passes a
parameterized SELECT statement using the SqlContext class and returns a
scalar value representing the resulting ContactId. |
| InsertContactSPCLR |
C# |
This stored procedure is built using C# and calls the
InsertContact stored procedure using the SqlContext class and returns a
scalar value representing the resulting ContactId. |
The HTTP ENDPOINT will expose the following methods:
| ENDPOINT Method |
Stored Procedure Called |
Description |
| GetContact |
GetContact |
This method calls the GetContact stored procedure
created within this example. |
| GetContactNoSchema |
GetContact |
This method calls the GetContact stored procedure, but
does not return the XML Schema back with the dataset. This option
could be used when higher performance is desired and the schema can be
sacrificed. |
| GetContactCLR |
GetContactCLR |
This method calls the GetContactCLR stored procedure which
is a stored procedure implemented in C#. |
| GetContactSPCLR |
GetContactSPCLR |
This method calls the GetContactSPCLR stored procedure. |
| InsertContact |
InsertContact |
This method calls the InsertContact stored procedure. |
| InsertContactCLR |
InsertContactCLR |
This method calls the InsertContactCLR stored procedure. |
| InsertContactSPCLR |
InsertContactSPCLR |
This method calls the InsertContactSPCLR stored procedure. |
| SqlBatch |
|
This endpoint will also allow adhoc SQL statements to be executed. |
The following picture depicts the layers that are being accessed in this
example.
Quick Start
If you would like to compile all of the code and run the client applications
without necessary stepping through the entire tutorial, perform the following
steps:
Step |
Description |
| 1. |
If Yukon Beta 1 was not installed to "C:\Program Files\Microsoft SQL
Server\MSSQL.1\MSSQL", edit .\2-CLRSPs\CompileAdventureWorksSQLCLR.bat to
change the reference within this batch file. |
| 2. |
Execute "ExecuteAll.bat" from the directory which the support files were
unzipped. This will compile and run all the code as well as opening up the
Windows Application created |
| 3. |
To use the console application which outputs the SOAP responses from Yukon Beta
1, navigate to the 6-HttpWebRequestApp directory and execute any or all of the
following:
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\BatchStatements.xml
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\GetContact.xml
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\GetContactNoSchema.xml
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\GetContactCLR.xml
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\GetContactSPCLR.xml
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\InsertContact.xml
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\InsertContactCLR.xml
httpwebrequestapp -uri=http://localhost/sql/AdventureWorks
-input=SampleXMLInputFiles\InsertContactSPCLR.xml
|
Step 1 - Creating the Additional T-SQL Stored
Procedures Needed for the Example
For this example, two stored procedures implemented in TSQL are added into the
AdventureWorks database.
The two stored procedures that will be added are GetContact and InsertContact.
These stored procedures return a record from the Contact
table and inserts a record, respectively.
To complete this step, perform the following tasks:
Step |
Description |
| 1. |
Open SQL Server Workbench |
| 2. |
Execute the following as an administrator on the SQL Server |
use AdventureWorks
-- Drop AdventureWorks.InsertContact
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[InsertContact]')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
begin
drop procedure InsertContact
print 'Dropped Stored Procedure: AdventureWorks.InsertContact'
end
go
-- Create AdventureWorks.InsertContact
create procedure InsertContact(
@Salutation nvarchar(8),
@FirstName nvarchar(50),
@MiddleName nvarchar(50),
@LastName nvarchar(50),
@NameStyle bit,
@Phone nvarchar(25),
@Suffix nvarchar(10),
@EmailAddress nvarchar(50),
@EmailPromotion int
)
AS
SET NOCOUNT ON
INSERT Contact(
Salutation,
FirstName,
MiddleName,
LastName,
NameStyle,
Phone,
Suffix,
EmailAddress,
EmailPromotion)
SELECT @Salutation,
@FirstName,
@MiddleName,
@LastName,
@NameStyle,
@Phone,
@Suffix,
@EmailAddress,
@EmailPromotion
SELECT @@IDENTITY
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[InsertContact]')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
begin
print 'Created Stored Procedure: AdventureWorks.InsertContact'
end
-- Drop AdventureWorks.GetContact
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetContact]')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
begin
drop procedure GetContact
print 'Dropped Stored Procedure: AdventureWorks.GetContact'
end
go
-- Create AdventureWorks.GetContact
create procedure GetContact(
@ContactID int
)
AS
SELECT ContactID,
Salutation,
FirstName,
MiddleName,
LastName,
NameStyle,
Phone,
Suffix,
EmailAddress,
EmailPromotion,
ModifiedDate
FROM Contact
WHERE ContactID = @ContactID
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetContact]')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
begin
print 'Created Stored Procedure: AdventureWorks.GetContact'
end
go
Step 2 - Creating the .NET Assembly for SQLCLR
This step will walk you through creating stored procedures in C# that will run
within the SQLCLR. The assembly created in this step contains the code
for the following stored procedures that will be linked within Yukon Beta 1:
CustOrdersDetailCLR, InsertOrderCLR, and InsertOrderSPCLR.
This step uses the System.Data.SqlServer namespace, which is new to Whidbey,
the next version of Visual Studio. Therefore, this project cannot be
opened
with previous versions of Visual Studio.
Therefore, in order to compile this assembly, the .NET Framework shipped with
Yukon Beta 1 must be used.
Step |
Description |
| 1. |
Within the support files that accompany this document, open up the file
2-CLRSPs\SPs.cs to review it. This file should look
like the code listed below this table. |
| 2. |
Within the support files, ensure that
2-CLRSPs\CompileAdventureWorksSQLCLR.bat
contains the correct path to csc.exe. SqlAccess.dll is used as a reference
from the \BINN directory of the Yukon Beta 1 Installation. Please verify that
this
path is also correct. Once this is done, execute it to compile
the .DLL.
|
| 3. |
Ensure that AdventureWorksSQLCLR.dll has been compiled. |
using System;
using System.Data;
using System.Data.SqlServer;
using System.Data.SqlTypes;
namespace AdventureWorksSQLCLR
{
public class SPs
{
private SPs()
{
// This ensures that this class will not be instantiated since there is
// no need to do so.
}
public static void GetContact(SqlInt32 ContactID)
{
// This function demonstrates how the SQLCLR can execute and display
// SELECT statements by mimicing the CustOrdersDetail stored procedure.
// Use System.Data.SqlServer.SqlContext to return a command object
// within the context of the calling user
SqlCommand cmd = SqlContext.GetCommand();
// Create the SQL Statement
string strSql = "SELECT ContactID, Salutation, FirstName, MiddleName, LastName,
NameStyle, Phone, Suffix, EmailAddress, EmailPromotion, ModifiedDate ";
strSql += " FROM Contact ";
strSql += " WHERE ContactID = @ContactID";
// Set the command text
cmd.CommandText = strSql;
// Create one input parameters using the
// System.Data.SqlServer.SqlParameter object
SqlParameter para = cmd.Parameters.Add("@ContactID", SqlDbType.Int);
para.Value = ContactID;
// Send this query to the relational engine using GetPipe()
SqlContext.GetPipe().Execute(cmd);
}
public static void GetContactSP(SqlInt32 ContactID)
{
// This function demonstrates how the SQLCLR can execute and display
// SELECT statements by mimicing the CustOrdersDetail stored procedure.
// Use System.Data.SqlServer.SqlContext to return a command object
// within the context of the calling user
SqlCommand cmd = SqlContext.GetCommand();
// Use the GetContact Stored Procedure
string strSql = "GetContact";
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = strSql;
// Create one input parameters using the
// System.Data.SqlServer.SqlParameter object
SqlParameter para = cmd.Parameters.Add("@ContactID", SqlDbType.Int);
para.Value = ContactID;
// Send this query to the relational engine using GetPipe()
SqlContext.GetPipe().Execute(cmd);
}
public static int InsertContact(SqlString Salutation, SqlString FirstName,
SqlString MiddleName, SqlString LastName, SqlBoolean NameStyle,
SqlString Phone, SqlString Suffix, SqlString EmailAddress, SqlInt32 EmailPromotion)
{
// This function demonstrates how the SQLCLR can execute and display
// scalar statements by inserting an order.
// Use System.Data.SqlServer.SqlContext to return a command object
// within the context of the calling user
SqlCommand cmd = SqlContext.GetCommand();
string strSql = @"SET NOCOUNT ON ";
strSql += " INSERT Contact(Salutation, FirstName, MiddleName,
LastName, NameStyle, Phone, Suffix, EmailAddress, EmailPromotion) ";
strSql += " SELECT @Salutation, @FirstName, @MiddleName,
@LastName, @NameStyle, @Phone, @Suffix, @EmailAddress, @EmailPromotion ";
strSql += " SELECT @@IDENTITY";
cmd.CommandText = strSql;
// Use the System.Data.SqlServer.SqlParameter object to create these input parameters
SqlParameter para = null;
para = cmd.Parameters.Add("@Salutation", SqlDbType.NVarChar);
para.Value = Salutation;
para = cmd.Parameters.Add("@FirstName", SqlDbType.NVarChar);
para.Value = FirstName;
para = cmd.Parameters.Add("@MiddleName", SqlDbType.NVarChar);
para.Value = MiddleName;
para = cmd.Parameters.Add("@LastName", SqlDbType.NVarChar);
para.Value = LastName;
para = cmd.Parameters.Add("@NameStyle", SqlDbType.Bit);
para.Value = NameStyle;
para = cmd.Parameters.Add("@Phone", SqlDbType.NVarChar);
para.Value = Phone;
para = cmd.Parameters.Add("@Suffix", SqlDbType.NVarChar);
para.Value = Suffix;
para = cmd.Parameters.Add("@EmailAddress", SqlDbType.NVarChar);
para.Value = EmailAddress;
para = cmd.Parameters.Add("@EmailPromotion", SqlDbType.Int);
para.Value = EmailPromotion;
// Execute this statement and convert the object returned to an integer
// and return it.
int orderID = Convert.ToInt32(cmd.ExecuteScalar());
// Business Logic on orderID could be programmed here.
// For example, if orderID > 0, use System.Web.Mail to send
// email to confirm the order?
return orderID;
}
public static int InsertContactSP(SqlString Salutation, SqlString FirstName,
SqlString MiddleName, SqlString LastName, SqlBoolean NameStyle,
SqlString Phone, SqlString Suffix, SqlString EmailAddress, SqlInt32 EmailPromotion)
{
// This function demonstrates how the SQLCLR can execute and display
// scalar statements by inserting an order, using the T-SQL SP created earlier.
// Use System.Data.SqlServer.SqlContext to return a command object
// within the context of the calling user
SqlCommand cmd = SqlContext.GetCommand();
// Use the InsertOrder Stored Procedure
string strSql = "InsertContact";
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = strSql;
// Use the System.Data.SqlServer.SqlParameter object to create these input parameters
SqlParameter para = null;
para = cmd.Parameters.Add("@Salutation", SqlDbType.NVarChar);
para.Value = Salutation;
para = cmd.Parameters.Add("@FirstName", SqlDbType.NVarChar);
para.Value = FirstName;
para = cmd.Parameters.Add("@MiddleName", SqlDbType.NVarChar);
para.Value = MiddleName;
para = cmd.Parameters.Add("@LastName", SqlDbType.NVarChar);
para.Value = LastName;
para = cmd.Parameters.Add("@NameStyle", SqlDbType.Bit);
para.Value = NameStyle;
para = cmd.Parameters.Add("@Phone", SqlDbType.NVarChar);
para.Value = Phone;
para = cmd.Parameters.Add("@Suffix", SqlDbType.NVarChar);
para.Value = Suffix;
para = cmd.Parameters.Add("@EmailAddress", SqlDbType.NVarChar);
para.Value = EmailAddress;
para = cmd.Parameters.Add("@EmailPromotion", SqlDbType.Int);
para.Value = EmailPromotion;
// Execute this statement and convert the object returned to an integer
// and return it.
int orderID = Convert.ToInt32(cmd.ExecuteScalar());
// Business Logic on orderID could be programmed here.
// For example, if orderID > 0, use System.Web.Mail to send
// email to confirm the order?
return orderID;
}
}
}
A Little More About the SqlContext Class
The SqlContext class encapsulates information about the caller's context, which
can be used from within a database object defined using the CLR.
This class should be used instead of ADO.NET classes when executing TSQL
statements or calling programmability objects within the current server.
When it is necessary to communicate with another server, use the ADO.NET
framework.
For more information about the SqlContext class, please refer to the following
the SqlContext Class article within SQL Books Online. To view the members of
the SqlContext class, refer to the "SqlContext
Members" article within SQL Books Online.
Step 3 - Linking C# Classes to Stored Procedures
This section will link the C# functions created in the previous step to stored
procedure names that be called in the same fashion as a traditional stored
procedure implemented in T-SQL. To perform this step, do the following steps:
Step |
Description |
| 1. |
Open SQL Server Workbench |
| 2. |
Execute the following as an administrator on the SQL Server after replacing
"{REPLACE WITH PATH TO SUPPORT FILES}" with the directory name where the
support files were extracted. |
use AdventureWorks
-- Drop all CLR stored procedures before dropping assembly since it cannot be dropped with dependencies
if object_id('GetContactCLR') is not null
begin
drop procedure GetContactCLR
print 'Dropped Stored Procedure: GetContactCLR'
end
go
if object_id('GetContactSPCLR') is not null
begin
drop procedure GetContactSPCLR
print 'Dropped Stored Procedure: GetContactSPCLR'
end
go
if object_id('InsertContactCLR') is not null
begin
drop procedure InsertContactCLR
print 'Dropped Stored Procedure: InsertContactCLR'
end
go
if object_id('InsertContactSPCLR') is not null
begin
drop procedure InsertContactSPCLR
print 'Dropped Stored Procedure: InsertContactSPCLR'
end
go
-- Drop the assembly if it already exists
if exists (select * from sys.assemblies where name = 'AdventureWorksSQLCLR')
begin
drop assembly AdventureWorksSQLCLR
print 'Dropped Assembly: AdventureWorksSQLCLR'
end
go
-- Execute CREATE ASSEMBLY which allows stored procedures to link to functions within this DLL
-- Note that the assembly name following CREATE ASSEMBLY matches the name of the .DLL
CREATE ASSEMBLY AdventureWorksSQLCLR
FROM '{REPLACE WITH PATH TO SUPPORT FILES}\2-CLRSPs\AdventureWorksSQLCLR.dll'
if exists (select * from sys.assemblies where name = 'AdventureWorksSQLCLR')
begin
print 'Created Assembly: AdventureWorksSQLCLR'
end
go
-- Create Procedure GetContactCLR to map to function in managed code
-- Syntax when using EXTERNAL NAME is (AssemblyName):[(Namespace).(Class)]::(Function)
-- Use EXECUTE AS OWNER to avoid having to give SELECT permissions on
-- Products, [Order Details] to the calling user
-- Note: This is scheduled to change post Beta 1 to "WITH EXECUTE AS OWNER"
CREATE PROCEDURE GetContactCLR
@ContactId int
WITH EXECUTE AS USER = dbo
as
EXTERNAL NAME AdventureWorksSQLCLR:[AdventureWorksSQLCLR.SPs]::GetContact
go
if object_id('GetContactCLR') is not null
print 'Created Stored Procedure: GetContactCLR'
go
-- Create GetContactSPCLR
CREATE PROCEDURE GetContactSPCLR
@ContactId int
WITH EXECUTE AS USER = dbo
as
EXTERNAL NAME AdventureWorksSQLCLR:[AdventureWorksSQLCLR.SPs]::GetContactSP
go
if object_id('GetContactSPCLR') is not null
print 'Created Stored Procedure: GetContactSPCLR'
go
-- Create the InsertContactCLR stored procedure
CREATE PROCEDURE InsertContactCLR
@Salutation nvarchar(8),
@FirstName nvarchar(50),
@MiddleName nvarchar(50),
@LastName nvarchar(50),
@NameStyle bit,
@Phone nvarchar(25),
@Suffix nvarchar(10),
@EmailAddress nvarchar(50),
@EmailPromotion int
WITH EXECUTE AS USER = dbo
as
EXTERNAL NAME AdventureWorksSQLCLR:[AdventureWorksSQLCLR.SPs]::InsertContact
go
if object_id('InsertContactCLR') is not null
begin
print 'Created Stored Procedure: InsertContactCLR'
end
go
-- Create the InsertContactSPCLR stored procedure
CREATE PROCEDURE InsertContactSPCLR
@Salutation nvarchar(8),
@FirstName nvarchar(50),
@MiddleName nvarchar(50),
@LastName nvarchar(50),
@NameStyle bit,
@Phone nvarchar(25),
@Suffix nvarchar(10),
@EmailAddress nvarchar(50),
@EmailPromotion int
WITH EXECUTE AS USER = dbo
as
EXTERNAL NAME AdventureWorksSQLCLR:[AdventureWorksSQLCLR.SPs]::InsertContactSP
go
if object_id('InsertContactSPCLR') is not null
begin
print 'Created Stored Procedure: InsertContactSPCLR'
end
go
A Little More About WITH EXECUTE AS
When stored procedures are created within the SQLCLR using the SqlContext
class,
the ownership chain is broken. In other words, SqlContext assumes the
security context of the user and needs security rights to perform any SQL
statements executed.
A common security practice is to give users access to the stored procedures
needed and not access to the particular tables.
Without using WITH EXECUTE AS USER = dbo, it would be necessary to
grant SELECT/INSERT/DELETE/etc rights to every calling user that
uses the stored procedure. Of course there are situations where this
may be desired, but developers should research how each SQLCLR stored
procedure should be executed.
Please refer to the SQL Books Online article "EXECUTE
AS" for more information.
Step 4 - Reserving the HTTP Namespace
Internally within HTTP.SYS, HTTP namespaces are used to determine which
process should handle incoming HTTP requests. Therefore, it is needed
to reserve an HTTP namespace so that HTTP requests can be
passed to the Yukon Beta 1 server.
In order to reserve the namespace, it can be done either implicitly or
explicitly.
Implicitly reserving a namespace is done by calling CREATE HTTP ENDPOINT
without
calling sp_reserve_http_namespace beforehand. Explicitly reserving a namespace
is done by executing the sp_reserve_http_namespace stored procedure. Although
it
is not necessary to explicitly reserve a namespace, it is the recommended
practice.
Explicitly reserving a namespace is done by calling sp_reserve_http_namespace.
This reserves a URL and all potential subtrees within the URL.
Subsequent HTTP ENDPOINT DDL statements will only need sysadmin security rights
and do not require Windows administrator rights.
If sp_reserve_http_namespace is not called, each HTTP ENDPOINT DDL statement
implicitly reserves the namespace on the kernel. The disadvantage to this is
that the client may need both sysadmin and Windows administrator rights with
every HTTP ENDPOINT DDL statement.
This example will reserve the \sql tree so that any subsequent HTTP ENDPOINTS
statements that use a URL anywhere underneath the \sql tree will not require
Windows administrator rights.
To reserve an HTTP namespace, perform the following steps:
Step |
Description |
| 1. |
Ensure that you are running as an administrator within the Windows Server where
the SQL Server resides |
| 2. |
Open SQL Workbench |
| 3. |
Execute the following as an administrator on the SQL Server |
exec sp_reserve_http_namespace N'http://*:80/sql'
go
For more information on namespace reservation, please refer to the article
" URL NAMESPACE
Reservation (Managing HTTP Endpoints)" within SQL Server Books Online.
The Books Online article mentioned above contains a reference to httpcfg.exe.
To
install httpcfg.exe, perform these steps:
Step |
Description |
| 1. |
Insert the Windows Server 2003 CD in the CD-ROM or DVD-ROM drive. |
| 2. |
When the CD opens, click Perform Additional Tasks. |
| 3. |
Click Browse this CD. |
| 4. |
Double-click Support. |
| 5. |
Double-click Tools. |
| 6. |
Double-click SUPTOOLS.MSI. |
| 7. |
Click Next, type your information in the Name and Organization boxes, click
Next, and then click Next on the following screen. |
| 8. |
Click Next again to start the installation. |
| 9. |
Click Finish. |
Step 5 - Creating the HTTP ENDPOINT
In this step, the stored procedures created in C# and TSQL are exposed on a
HTTP ENDPOINT. The ENDPOINT will also allow any ad hoc SQL batch queries to
be executed and handled in this example.
To create the endpoint, please follow these steps:
Step |
Description |
| 1. |
Open SQL Server Workbench |
| 2. |
Execute the following as an administrator on the SQL Server |
if exists(select * from sys.http_endpoints where name = 'sql_AdventureWorks_endpoint')
begin
drop http endpoint sql_AdventureWorks_endpoint
print 'Dropped HTTP ENDPOINT: sql_AdventureWorks_endpoint'
end
go
CREATE HTTP ENDPOINT sql_AdventureWorks_endpoint
AS
SITE = '*',
PATH = '/sql/AdventureWorks',
AUTHENTICATION = (INTEGRATED),
PORTS = ( CLEAR ),
CLEAR_PORT = 80,
STATE = STARTED
FOR SOAP (
WEBMETHOD 'http://AdventureWorks/'.'GetContact'
(name=AdventureWorks.dbo.GetContact,
schema=STANDARD ),
WEBMETHOD 'http://AdventureWorks/'.'GetContactNoSchema'
(name=AdventureWorks.dbo.GetContact,
schema=NONE ),
WEBMETHOD 'http://AdventureWorks/'.'GetContactCLR'
(name=AdventureWorks.dbo.GetContactCLR,
schema=STANDARD ),
WEBMETHOD 'http://AdventureWorks/'.'GetContactSPCLR'
(name=AdventureWorks.dbo.GetContactSPCLR,
schema=STANDARD ),
WEBMETHOD 'http://AdventureWorks/'.'InsertContact'
(name=AdventureWorks.dbo.InsertContact,
schema=STANDARD ),
WEBMETHOD 'http://AdventureWorks/'.'InsertContactCLR'
(name=AdventureWorks.dbo.InsertContactCLR,
schema=STANDARD ),
WEBMETHOD 'http://AdventureWorks/'.'InsertContactSPCLR'
(name=AdventureWorks.dbo.InsertContactSPCLR,
schema=STANDARD )
WITH
WSDL = ENABLED,
BATCHES = ENABLED,
DATABASE = 'AdventureWorks',
NAMESPACE = 'http://AdventureWorks/'
)
go
if exists(select * from sys.http_endpoints where name = 'sql_AdventureWorks_endpoint')
begin
print 'Created HTTP ENDPOINT: sql_AdventureWorks_endpoint'
end
go
For more information on creating endpoints, please refer to "Create
HTTP Endpoint"
within SQL Books Online.
Step 6 - Creating Client Application using
HttpWebRequest and HttpWebResponse
This section describes how to use the HttpWebRequest and HttpWebResponse
classes to communicate with the SQL Server and to see the SOAP packets
passed. This
is NOT the recommended way to build applications that
communicate
with Yukon Beta 1 since there are classes such as the ones auto-generated
by WSDL.exe that should be leveraged when building your applications on top of
SOAP.
However, this example allows the user to see the SOAP request
and response packets handled by Yukon Beta 1.
The easiest way to perform this step is to simply run
CompileHttpWebRequestApp.bat
from the 6-HttpWebRequestApp directory. This batch file creates
HttpWebRequestApp.exe
which is a console application.
If you are using Visual Studio .NET and would like to create the
HttpWebRequestApp project from
scratch that way you can execute it within a debugger, perform the following
steps:
Step |
Description |
| 1. |
Open Visual Studio .NET |
| 2. |
Click File->New->Project->Visual C# Projects->Console Application |
| 3. |
Set the Name to HttpWebRequestApp |
| 4. |
Right click on the project HttpWebRequestApp within the Solution Explorer and
click on Add Existing Item |
| 5. |
Add BOTH Arguments.cs and HttpWebRequestApp.cs from the
6-HttpWebRequestApp directory to the project |
The code that is now open within Visual Studio .NET performs the following
tasks:
Step |
Description |
| 1. |
When the application is first loaded, the arguments are parsed and validated
using the Arguments class defined in Arguments.cs. The Arguments class parses
the command line arguments into a StringDictionary class. This is helpful so
that any command line arguments can be accessed by name rather than by
position. |
| 2. |
Using the FileStream class, the input file specified is read. |
| 3. |
If an output file is specified, the output stream is set to write to this file
using a StreamWriter class. |
| 4. |
Using the WebRequest.Create() function, a new HttpWebRequest is instantiated
for the URI specified. |
| 5. |
The contents of the input file are written to the request stream of
HttpWebRequest. |
| 6. |
Using the HttpWebResponse.GetResponse() method, the response is synchronously
retrieved from the server. |
| 7. |
An instance of XmlDocument is used to properly format the contents of the
response retrieved from the server. |
| 8. |
XmlDocument.Save() is used to output the response from Yukon Beta 1. |
The following table describes the command line arguments used by the
application:
| Argument Name |
Required |
Description |
| Input |
X |
This specifies the input filename which contains the SOAP packet to be sent to
the database. There are sample .XML files within the \bin\Debug directory that
call all of the methods exposed from the HTTP Endpoint created earlier. |
| URI |
X |
This specifies the URI of the database. If you are using the local system, use
http://localhost/sql/AdventureWorks to connect to HTTP Endpoint created. |
| Output |
|
If specified, this is the output file to use instead of stdout |
| Timeout |
|
If specified, this specifies the timeout period to wait before canceling the
request, overriding the default timeout period. |
| Unformatted |
|
If specified, this causes the output from Yukon Beta 1 to displayed as is,
without any
formatting. |
To execute this program, either run the executable created by compiling the
project, or perform the following tasks to execute the program from within the
Visual Studio .NET debugger:
Step |
Description |
| 1. |
Right click on the HttpWebRequestApp project within the Solution Explorer |
| 2. |
Select Properties->Configuration Properties->Debugging |
| 3. |
Under Start Options, Set the Command Line Arguments to
“-input=..\..\SampleInputXMLFiles\GetContactSPCLR.xml
–uri=http://localhost/sql/AdventureWorks” |
| 4. |
Hit F5 |
The following additional XML input files have been included in the support
files in the 6-HttpWebRequestApp\SampleXMLInputFiles directory:
| Filename |
WebMethod Called |
| GetContact.xml |
GetContact |
| GetContactNoSchema.xml |
GetContactNoSchema |
| GetContactCLR.xml |
GetContactCLR |
| GetContactSPCLR.xml |
GetContactSPCLR |
| InsertContact.xml |
InsertContact |
| InsertContactCLR.xml |
InsertContactCLR |
| InsertContactSPCLR.xml |
InsertContactSPCLR |
| BatchStatements.xml |
Arbitrary ad hoc SQL statements |
The following is a listing of the code used to create this application:
using System;
using System.Net;
using System.IO;
using System.Xml;
namespace HttpWebRequestApp
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class HttpWebRequestApp
{
/// <summary>
/// The main entry point for the application.
/// </summary>
///
static private string mURI;
static private string mInputFile;
static private string mOutputFile;
static private bool mFormatted = true;
static private int mTimeout = 0;
static private TextWriter outWriter = Console.Out;
[STAThread]
static void Main(string[] args)
{
try
{
// Parse and Validate Arguments
ParseArguments(args);
// Read the contents of mInputFile
FileStream inputStream = new FileStream(mInputFile, FileMode.Open,
FileAccess.Read, FileShare.Read);
int inputLength = (int)inputStream.Length;
byte[] inputBody = new byte[inputLength];
inputStream.Read(inputBody, 0, inputLength);
inputStream.Close();
// Open the output file if specified
if(null != mOutputFile)
{
outWriter = new StreamWriter(new FileStream(mOutputFile,
FileMode.Create, FileAccess.Write));
}
// Create WebRequest and set headers
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(mURI);
req.Credentials = CredentialCache.DefaultCredentials;
req.Method = "POST";
req.ProtocolVersion = HttpVersion.Version11;
req.ContentType = "text/xml";
req.ContentLength = inputLength;
if(0 < mTimeout)
req.Timeout = mTimeout;
// Write the input to the request stream
Stream reqStream = req.GetRequestStream();
reqStream.Write(inputBody, 0, inputLength);
reqStream.Close();
// Synchronously get the response and output it
OutputWebResponse((HttpWebResponse)req.GetResponse());
}
catch(IOException e)
{
// IOException thrown when file IO exception on input/output file occurs
outWriter.WriteLine(e.Message);
}
catch(ArgumentException)
{
// ArgumentException Throw by ParseArguments when InputFile or URI not specified
DisplayUsage();
}
catch(WebException e)
{
// Display any web exceptions that occur when sending request to server
outWriter.WriteLine("WebException Occurred:");
outWriter.WriteLine();
outWriter.WriteLine();
if(null != e.Response)
{
OutputWebResponse((HttpWebResponse)e.Response);
}
else
{
outWriter.WriteLine(e.Message);
}
}
catch
{
// Display any other exceptions
outWriter.WriteLine("Unexpected Exception Occurred in Main\n\n");
throw;
}
finally
{
// If displaying to the Console, ReadLine()
if(Console.Out == outWriter)
{
Console.WriteLine("\n\nPress Enter to Continue");
Console.ReadLine();
}
}
}
static private void OutputWebResponse(HttpWebResponse resp)
{
try
{
// Use the response stream to input into a XmlDocument
Stream respStream = resp.GetResponseStream();
StreamReader sr = new StreamReader(respStream);
// Display the Status
outWriter.WriteLine("Response Received:");
outWriter.WriteLine();
outWriter.WriteLine("Status Code : {0}", (int)resp.StatusCode);
outWriter.WriteLine("Status Description: {0}", resp.StatusDescription);
outWriter.WriteLine();
if(mFormatted)
{
// Output a formatted XML document
// Use XmlDocument so that the returned XML is properly formatted
XmlDocument xmld = new XmlDocument();
xmld.LoadXml(sr.ReadToEnd());
xmld.Save(outWriter);
}
else
{
// Display output without formatting
outWriter.WriteLine(sr.ReadToEnd());
}
outWriter.Flush();
}
catch
{
outWriter.WriteLine("An Error Occured While Outputting WebResponse");
throw;
}
}
static private void ParseArguments(string[] args)
{
// Use the Arguments class set the member variables
Arguments myArgs = new Arguments(args);
if(null != myArgs["uri"])
mURI = myArgs["uri"];
if(null != myArgs["input"])
mInputFile = myArgs["input"];
if(null != myArgs["timeout"])
mTimeout = Convert.ToInt32(myArgs["timeout"]);
if(null != myArgs["output"])
mOutputFile = myArgs["output"];
if(null != myArgs["unformatted"] && "true" == myArgs["unformatted"])
mFormatted = false;
if(null != mInputFile && null != mURI)
{
// Necessary arguments were passed
if(!File.Exists(mInputFile))
{
throw new IOException("Invalid Filename Specified: " + mInputFile);
}
}
else
{
throw new ArgumentException("Invalid Arguments Specified");
}
}
static private void DisplayUsage()
{
// Display the usage to the screen
string help_text = " Usage: ";
help_text += " -uri=<uri> -input=<filename>
[-timeout=<waittime>] [-output=<filename>]
[-unformatted]\n";
help_text += "'uri' specifies the URI\n";
help_text += "'input' specifies the input file\n";
help_text += "'timeout' specifies the time in milliseconds
to wait before timing out\n";
help_text += "'output' specifies the output file. If
omitted, stdout will be used\n";
help_text += "'unformatted' causes the output from Yukon
Beta 1 to be displayed as is, without any formatting\n";
outWriter.WriteLine(help_text);
}
}
}
Step 7 - Creating Client Application Using Proxy Class
Generated by WSDL.EXE
This section provides a sample application created on top of the proxy class
generated using WSDL.EXE. This
is a better example of how to call Yukon's web services from a client
application that the previous step.
Unfortunately, aspects of this application are not supported in versions prior
to Whidbey, so this applcation
cannot be fully created within version of Visual Studio prior to the upcoming
next major release of VS. However,
the application can be compiled using the .NET Framework installed with Yukon.
There are two .cs files included to create this application,
AdventureWorksProxy.cs and Reference.cs. AdventureWorksProxy.cs
contains the application code. Reference.cs was created by WSDL.EXE by using
Visual Studio to add a web reference to
http://localhost/sql/AdventureWorks?wsdl.
This process creates a .cs file which contains a class that is a derivation of
System.Web.Services.Protocols.SoapHttpClientProtocol.
The class is created by analyzing the HTTP ENDPOINT and setting up appropriate
methods to call the web methods exposed.
The easiest way to perform this step is to simply run
CompileAdventureWorksProxyApp.bat from the 7-AdventureWorksProxyApp
directory. This batch file will use the .NET Framework installed with Yukon to
create the Windows application
and execute it.
Step |
Description |
| 1. |
Navigate to the 7-AdventureWorksProxyApp directory |
| 2. |
Execute CompileRunAdventureWorksProxyApp.bat. The application should also
execute from this batch file. This application should appear like the following
screen shot. |
This application works by calling the methods generated by WSDL.EXE. The
following is a code listing of ApplicationWorksProxy.cs.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
namespace AdventureWorksProxy
{
/// <summary>
/// Summary description for Form1.
/// </summary>
///
public class frmAdventureWorks : System.Windows.Forms.Form
{
private System.Windows.Forms.Label lblSqlBatch;
private System.Windows.Forms.TextBox txtSqlBatch;
private System.Windows.Forms.Label lblOutput;
private System.Windows.Forms.Button btnSqlBatch;
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
private System.Windows.Forms.TextBox txtOutput;
private System.Windows.Forms.Button btnGetContact;
private System.Windows.Forms.Label lblContactId;
private System.Windows.Forms.TextBox txtGetContactId;
private System.Windows.Forms.ControlArray cArrayGetContact;
private System.Windows.Forms.Button btnGetContactCLR;
private System.Windows.Forms.Button btnGetContactSPCLR;
private System.Windows.Forms.Button btnInsertContact;
private System.Windows.Forms.Button btnInsertContactCLR;
private System.Windows.Forms.ControlArray cArrayInsertContact;
private System.Windows.Forms.Button btnInsertContactSPCLR;
private System.Windows.Forms.Label lblSaluation;
private System.Windows.Forms.Label lblFirstName;
private System.Windows.Forms.Label lblLastName;
private System.Windows.Forms.Label lblNameStyle;
private System.Windows.Forms.Label lblPhone;
private System.Windows.Forms.Label lblSuffix;
private System.Windows.Forms.Label lblEmail;
private System.Windows.Forms.Label lblEmailPromotion;
private System.Windows.Forms.TextBox txtSaluation;
private System.Windows.Forms.TextBox txtFirstName;
private System.Windows.Forms.TextBox txtLastName;
private System.Windows.Forms.Label lblMiddleName;
private System.Windows.Forms.TextBox txtMiddleName;
private System.Windows.Forms.TextBox txtEmailPromotion;
private System.Windows.Forms.TextBox txtSuffix;
private System.Windows.Forms.TextBox txtPhone;
private System.Windows.Forms.TextBox txtEmail;
private System.Windows.Forms.ComboBox cboNameStyle;
private prxyAdventureWorks.sql_AdventureWorks_endpoint mProxy;
public frmAdventureWorks()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lblSqlBatch = new System.Windows.Forms.Label();
this.txtSqlBatch = new System.Windows.Forms.TextBox();
this.lblOutput = new System.Windows.Forms.Label();
this.btnSqlBatch = new System.Windows.Forms.Button();
this.txtOutput = new System.Windows.Forms.TextBox();
this.btnGetContact = new System.Windows.Forms.Button();
this.lblContactId = new System.Windows.Forms.Label();
this.txtGetContactId = new System.Windows.Forms.TextBox();
this.btnGetContactCLR = new System.Windows.Forms.Button();
this.cArrayGetContact = new System.Windows.Forms.ControlArray();
this.btnGetContactSPCLR = new System.Windows.Forms.Button();
this.btnInsertContact = new System.Windows.Forms.Button();
this.btnInsertContactCLR = new System.Windows.Forms.Button();
this.cArrayInsertContact = new System.Windows.Forms.ControlArray();
this.btnInsertContactSPCLR = new System.Windows.Forms.Button();
this.lblSaluation = new System.Windows.Forms.Label();
this.lblFirstName = new System.Windows.Forms.Label();
this.lblLastName = new System.Windows.Forms.Label();
this.lblNameStyle = new System.Windows.Forms.Label();
this.lblPhone = new System.Windows.Forms.Label();
this.lblSuffix = new System.Windows.Forms.Label();
this.lblEmail = new System.Windows.Forms.Label();
this.lblEmailPromotion = new System.Windows.Forms.Label();
this.txtSaluation = new System.Windows.Forms.TextBox();
this.txtFirstName = new System.Windows.Forms.TextBox();
this.txtLastName = new System.Windows.Forms.TextBox();
this.txtEmailPromotion = new System.Windows.Forms.TextBox();
this.txtSuffix = new System.Windows.Forms.TextBox();
this.txtPhone = new System.Windows.Forms.TextBox();
this.txtEmail = new System.Windows.Forms.TextBox();
this.lblMiddleName = new System.Windows.Forms.Label();
this.txtMiddleName = new System.Windows.Forms.TextBox();
this.cboNameStyle = new System.Windows.Forms.ComboBox();
this.SuspendLayout();
//
// lblSqlBatch
//
this.lblSqlBatch.Location = new System.Drawing.Point(520, 10);
this.lblSqlBatch.Name = "lblSqlBatch";
this.lblSqlBatch.Size = new System.Drawing.Size(216, 24);
this.lblSqlBatch.TabIndex = 0;
this.lblSqlBatch.Text = "SQL Batch Statements:";
//
// txtSqlBatch
//
this.txtSqlBatch.Location = new System.Drawing.Point(231, 34);
this.txtSqlBatch.Multiline = true;
this.txtSqlBatch.Name = "txtSqlBatch";
this.txtSqlBatch.Size = new System.Drawing.Size(786, 115);
this.txtSqlBatch.TabIndex = 0;
this.txtSqlBatch.Text = "select top 10 * from address";
//
// lblOutput
//
this.lblOutput.Font = new System.Drawing.Font("Microsoft Sans Serif",
14.25F, System.Drawing.FontStyle.Bold,
System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lblOutput.Location = new System.Drawing.Point(448, 421);
this.lblOutput.Name = "lblOutput";
this.lblOutput.Size = new System.Drawing.Size(84, 22);
this.lblOutput.TabIndex = 2;
this.lblOutput.Text = "Output:";
//
// btnSqlBatch
//
this.btnSqlBatch.Location = new System.Drawing.Point(74, 79);
this.btnSqlBatch.Name = "btnSqlBatch";
this.btnSqlBatch.Size = new System.Drawing.Size(136, 32);
this.btnSqlBatch.TabIndex = 1;
this.btnSqlBatch.Text = "Execute SQL Batch";
this.btnSqlBatch.Click += new System.EventHandler(this.btnSqlBatch_Click);
//
// txtOutput
//
this.txtOutput.Location = new System.Drawing.Point(11, 453);
this.txtOutput.Multiline = true;
this.txtOutput.Name = "txtOutput";
this.txtOutput.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtOutput.Size = new System.Drawing.Size(1023, 304);
this.txtOutput.TabIndex = 18;
//
// btnGetContact
//
this.btnGetContact.Location = new System.Drawing.Point(78, 168);
this.btnGetContact.Name = "btnGetContact";
this.btnGetContact.Size = new System.Drawing.Size(133, 32);
this.btnGetContact.TabIndex = 3;
this.btnGetContact.Text = "GetContact";
//
// lblContactId
//
this.lblContactId.Location = new System.Drawing.Point(236, 176);
this.lblContactId.Name = "lblContactId";
this.lblContactId.Size = new System.Drawing.Size(58, 18);
this.lblContactId.TabIndex = 9;
this.lblContactId.Text = "ContactId:";
//
// txtGetContactId
//
this.txtGetContactId.Location = new System.Drawing.Point(306, 176);
this.txtGetContactId.Name = "txtGetContactId";
this.txtGetContactId.Size = new System.Drawing.Size(136, 19);
this.txtGetContactId.TabIndex = 2;
this.txtGetContactId.Text = "1";
//
// btnGetContactCLR
//
this.btnGetContactCLR.Location = new System.Drawing.Point(78, 208);
this.btnGetContactCLR.Name = "btnGetContactCLR";
this.btnGetContactCLR.Size = new System.Drawing.Size(133, 32);
this.btnGetContactCLR.TabIndex = 4;
this.btnGetContactCLR.Text = "GetContactCLR";
//
// cArrayGetContact
//
this.cArrayGetContact.Name = "cArrayGetContact";
this.cArrayGetContact.Click += new System.EventHandler(this.cArrayGetContact_Click);
this.cArrayGetContact.AddRange(new System.Windows.Forms.Control[] {
this.btnGetContact, this.btnGetContactCLR, this.btnGetContactSPCLR
});
//
// btnGetContactSPCLR
//
this.btnGetContactSPCLR.Location = new System.Drawing.Point(78, 244);
this.btnGetContactSPCLR.Name = "btnGetContactSPCLR";
this.btnGetContactSPCLR.Size = new System.Drawing.Size(133, 32);
this.btnGetContactSPCLR.TabIndex = 5;
this.btnGetContactSPCLR.Text = "GetContactSPCLR";
//
// btnInsertContact
//
this.btnInsertContact.Location = new System.Drawing.Point(82, 297);
this.btnInsertContact.Name = "btnInsertContact";
this.btnInsertContact.Size = new System.Drawing.Size(128, 32);
this.btnInsertContact.TabIndex = 15;
this.btnInsertContact.Text = "InsertContact";
//
// btnInsertContactCLR
//
this.btnInsertContactCLR.Location = new System.Drawing.Point(82, 334);
this.btnInsertContactCLR.Name = "btnInsertContactCLR";
this.btnInsertContactCLR.Size = new System.Drawing.Size(128, 32);
this.btnInsertContactCLR.TabIndex = 16;
this.btnInsertContactCLR.Text = "InsertContactCLR";
//
// cArrayInsertContact
//
this.cArrayInsertContact.Name = "cArrayInsertContact";
this.cArrayInsertContact.Click += new System.EventHandler(this.cArrayInsertContact_Click);
this.cArrayInsertContact.AddRange(new System.Windows.Forms.Control[] {
this.btnInsertContact, this.btnInsertContactCLR, this.btnInsertContactSPCLR
});
//
// btnInsertContactSPCLR
//
this.btnInsertContactSPCLR.Location = new System.Drawing.Point(82, 372);
this.btnInsertContactSPCLR.Name = "btnInsertContactSPCLR";
this.btnInsertContactSPCLR.Size = new System.Drawing.Size(128, 32);
this.btnInsertContactSPCLR.TabIndex = 17;
this.btnInsertContactSPCLR.Text = "InsertContactSPCLR";
//
// lblSaluation
//
this.lblSaluation.Location = new System.Drawing.Point(230, 294);
this.lblSaluation.Name = "lblSaluation";
this.lblSaluation.Size = new System.Drawing.Size(72, 16);
this.lblSaluation.TabIndex = 16;
this.lblSaluation.Text = "Saluation:";
//
// lblFirstName
//
this.lblFirstName.Location = new System.Drawing.Point(231, 324);
this.lblFirstName.Name = "lblFirstName";
this.lblFirstName.Size = new System.Drawing.Size(72, 16);
this.lblFirstName.TabIndex = 17;
this.lblFirstName.Text = "First Name:";
//
// lblLastName
//
this.lblLastName.Location = new System.Drawing.Point(228, 376);
this.lblLastName.Name = "lblLastName";
this.lblLastName.Size = new System.Drawing.Size(72, 16);
this.lblLastName.TabIndex = 18;
this.lblLastName.Text = "Last Name:";
//
// lblNameStyle
//
this.lblNameStyle.Location = new System.Drawing.Point(230, 404);
this.lblNameStyle.Name = "lblNameStyle";
this.lblNameStyle.Size = new System.Drawing.Size(72, 16);
this.lblNameStyle.TabIndex = 19;
this.lblNameStyle.Text = "NameStyle:";
//
// lblPhone
//
this.lblPhone.Location = new System.Drawing.Point(598, 297);
this.lblPhone.Name = "lblPhone";
this.lblPhone.Size = new System.Drawing.Size(64, 16);
this.lblPhone.TabIndex = 20;
this.lblPhone.Text = "Phone:";
//
// lblSuffix
//
this.lblSuffix.Location = new System.Drawing.Point(598, 329);
this.lblSuffix.Name = "lblSuffix";
this.lblSuffix.Size = new System.Drawing.Size(64, 16);
this.lblSuffix.TabIndex = 21;
this.lblSuffix.Text = "Suffix";
//
// lblEmail
//
this.lblEmail.Location = new System.Drawing.Point(598, 353);
this.lblEmail.Name = "lblEmail";
this.lblEmail.Size = new System.Drawing.Size(64, 16);
this.lblEmail.TabIndex = 23;
this.lblEmail.Text = "Email:";
//
// lblEmailPromotion
//
this.lblEmailPromotion.Location = new System.Drawing.Point(598, 386);
this.lblEmailPromotion.Name = "lblEmailPromotion";
this.lblEmailPromotion.Size = new System.Drawing.Size(94, 17);
this.lblEmailPromotion.TabIndex = 22;
this.lblEmailPromotion.Text = "Email Promotion:";
//
// txtSaluation
//
this.txtSaluation.Location = new System.Drawing.Point(305, 294);
this.txtSaluation.MaxLength = 8;
this.txtSaluation.Name = "txtSaluation";
this.txtSaluation.Size = new System.Drawing.Size(74, 19);
this.txtSaluation.TabIndex = 6;
this.txtSaluation.Text = "Mr.";
//
// txtFirstName
//
this.txtFirstName.Location = new System.Drawing.Point(305, 324);
this.txtFirstName.MaxLength = 50;
this.txtFirstName.Name = "txtFirstName";
this.txtFirstName.Size = new System.Drawing.Size(253, 19);
this.txtFirstName.TabIndex = 7;
this.txtFirstName.Text = "Jason";
//
// txtLastName
//
this.txtLastName.Location = new System.Drawing.Point(306, 375);
this.txtLastName.MaxLength = 50;
this.txtLastName.Name = "txtLastName";
this.txtLastName.Size = new System.Drawing.Size(252, 19);
this.txtLastName.TabIndex = 9;
this.txtLastName.Text = "Stowe";
//
// txtEmailPromotion
//
this.txtEmailPromotion.Location = new System.Drawing.Point(693, 383);
this.txtEmailPromotion.MaxLength = 10;
this.txtEmailPromotion.Name = "txtEmailPromotion";
this.txtEmailPromotion.Size = new System.Drawing.Size(59, 19);
this.txtEmailPromotion.TabIndex = 14;
this.txtEmailPromotion.Text = "1";
//
// txtSuffix
//
this.txtSuffix.Location = new System.Drawing.Point(693, 319);
this.txtSuffix.MaxLength = 10;
this.txtSuffix.Name = "txtSuffix";
this.txtSuffix.Size = new System.Drawing.Size(75, 19);
this.txtSuffix.TabIndex = 12;
this.txtSuffix.Text = "I";
//
// txtPhone
//
this.txtPhone.Location = new System.Drawing.Point(693, 292);
this.txtPhone.MaxLength = 25;
this.txtPhone.Name = "txtPhone";
this.txtPhone.Size = new System.Drawing.Size(129, 19);
this.txtPhone.TabIndex = 11;
this.txtPhone.Text = "425-555-5555";
//
// txtEmail
//
this.txtEmail.Location = new System.Drawing.Point(693, 348);
this.txtEmail.MaxLength = 50;
this.txtEmail.Name = "txtEmail";
this.txtEmail.Size = new System.Drawing.Size(270, 19);
this.txtEmail.TabIndex = 13;
this.txtEmail.Text = "jstowe@microsoft.com";
//
// lblMiddleName
//
this.lblMiddleName.Location = new System.Drawing.Point(227, 350);
this.lblMiddleName.Name = "lblMiddleName";
this.lblMiddleName.Size = new System.Drawing.Size(97, 16);
this.lblMiddleName.TabIndex = 32;
this.lblMiddleName.Text = "Middle Name:";
//
// txtMiddleName
//
this.txtMiddleName.Location = new System.Drawing.Point(305, 350);
this.txtMiddleName.MaxLength = 50;
this.txtMiddleName.Name = "txtMiddleName";
this.txtMiddleName.Size = new System.Drawing.Size(252, 19);
this.txtMiddleName.TabIndex = 8;
this.txtMiddleName.Text = "P";
//
// cboNameStyle
//
this.cboNameStyle.Items.AddRange(new object[] {
"0", "1"
});
this.cboNameStyle.Location = new System.Drawing.Point(306, 404);
this.cboNameStyle.Name = "cboNameStyle";
this.cboNameStyle.Size = new System.Drawing.Size(37, 21);
this.cboNameStyle.TabIndex = 10;
this.cboNameStyle.Text = "0";
//
// frmAdventureWorks
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(1051, 771);
this.Controls.Add(this.cboNameStyle);
this.Controls.Add(this.txtMiddleName);
this.Controls.Add(this.lblMiddleName);
this.Controls.Add(this.txtEmailPromotion);
this.Controls.Add(this.txtEmail);
this.Controls.Add(this.txtSuffix);
this.Controls.Add(this.txtPhone);
this.Controls.Add(this.txtLastName);
this.Controls.Add(this.txtFirstName);
this.Controls.Add(this.txtSaluation);
this.Controls.Add(this.lblEmail);
this.Controls.Add(this.lblEmailPromotion);
this.Controls.Add(this.lblSuffix);
this.Controls.Add(this.lblPhone);
this.Controls.Add(this.lblNameStyle);
this.Controls.Add(this.lblLastName);
this.Controls.Add(this.lblFirstName);
this.Controls.Add(this.lblSaluation);
this.Controls.Add(this.btnInsertContactSPCLR);
this.Controls.Add(this.btnInsertContactCLR);
this.Controls.Add(this.btnInsertContact);
this.Controls.Add(this.btnGetContactSPCLR);
this.Controls.Add(this.btnGetContactCLR);
this.Controls.Add(this.txtGetContactId);
this.Controls.Add(this.lblContactId);
this.Controls.Add(this.btnGetContact);
this.Controls.Add(this.txtOutput);
this.Controls.Add(this.btnSqlBatch);
this.Controls.Add(this.lblOutput);
this.Controls.Add(this.txtSqlBatch);
this.Controls.Add(this.lblSqlBatch);
this.Name = "frmAdventureWorks";
this.Text = "AdventureWorks Proxy Application";
this.Load += new System.EventHandler(this.frmAdventureWorks_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new frmAdventureWorks());
}
private void frmAdventureWorks_Load(object sender, System.EventArgs e)
{
// Set the mProxy member variable to the endpoint with the default credentials
mProxy = new prxyAdventureWorks.sql_AdventureWorks_endpoint();
mProxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
}
private void btnSqlBatch_Click(object sender, System.EventArgs e)
{
try
{
// Execute the Batch Statements by using the sqlbatch() function generated
prxyAdventureWorks.SqlParameter[] p = null;
txtOutput.Text = "Executing Batch Statements...\r\n\r\n";
// Execute the adhoc statements in the textbox by using the sqlbatch() function generated
object[] results = mProxy.sqlbatch(txtSqlBatch.Text, ref p);
DisplayResults(results);
}
catch(InvalidOperationException ex)
{
txtOutput.Text += "An InvalidOperationException was caught.\r\n";
txtOutput.Text += "This could be due to a column of type XML
in the select list.\r\n";
txtOutput.Text += "This is a known issue within the version
of the .NET Framework shipped with Yukon.\r\n";
txtOutput.Text += "To work around this, try removing the columns
of type XML from the select list.\r\n";
txtOutput.Text += "\r\n";
txtOutput.Text += "Contents of the exception:\r\n";
txtOutput.Text += "\r\n";
txtOutput.Text += ex.Message;
}
}
private void DisplayResults(object[] results)
{
// The results generated by the proxy methods return an array of
// results like a dataset, messages, row count, etc.
// This function loops through these results and displays them as appropriate.
if(null == results)
return;
for (int intCount = 0; intCount < results.Length; intCount++)
{
// Use a stringbuilder class when adding on multiple strings for efficiency
StringBuilder sb = new StringBuilder();
object objResult = results[intCount];
// If the type of objResult is a primitive, then it is a return code.
if (objResult.GetType().IsPrimitive)
{
sb.Append("Return code = ");
sb.AppendLine(objResult.ToString());
}
else
{
// Display the different variations of objResult
switch (objResult.ToString())
{
case "System.Data.DataSet" :
sb.AppendLine("Printing result of SELECT:");
System.Data.DataSet resultDS = (System.Data.DataSet)objResult;
sb.Append(resultDS.GetXml());
break;
case "AdventureWorksProxy.prxyAdventureWorks.SqlRowCount" :
sb.Append("AdventureWorksProxy.prxyAdventureWorks.SqlRowCount = ");
sb.Append((((prxyAdventureWorks.SqlRowCount)results[intCount]).Count));
break;
case "System.Xml.XmlElement" :
sb.AppendLine("Printing result of SELECT ...FOR XML");
System.Xml.XmlElement xmlResult = (System.Xml.XmlElement)results[intCount];
sb.Append(xmlResult.OuterXml);
break;
case "AdventureWorksProxy.prxyAdventureWorks.SqlMessage" :
sb.AppendLine("Printing error msg, warning, or other informational msg:");
prxyAdventureWorks.SqlMessage errorMessage =
(prxyAdventureWorks.SqlMessage)results[intCount];
sb.Append("Message: ");
sb.AppendLine(errorMessage.Message);
sb.Append("Source: ");
sb.AppendLine(errorMessage.Source);
break;
case "AdventureWorksProxy.prxyAdventureWorks.SqlParameter" :
sb.AppendLine("Printing output parameters");
sb.Append("The type of the corresponding object array element is: ");
sb.AppendLine(objResult.ToString());
sb.Append("Output parameter's name is: ");
sb.AppendLine((((prxyAdventureWorks.SqlParameter)results[intCount]).Name));
sb.Append("Output parameter's value is: ");
sb.AppendLine((((prxyAdventureWorks.SqlParameter)results[intCount]).Value).ToString());
break;
default :
sb.Append("Unhandled result case: ");
sb.AppendLine(objResult.ToString());
break;
}
}
sb.AppendLine();
txtOutput.Text += sb.ToString();
}
}
private void cArrayGetContact_Click(object sender, System.EventArgs e)
{
// This function is called when the GetControl buttons are clicked.
// A control array is setup to include btnGetContact, btnGetContactCLR,
// btnGetContactSPCLR, so this function is invoked whenever those
// buttons are clicked.
// The function determines which button was clicked and then calls
// the appropriate generated function.
object[] results = null;
int contactId = Convert.ToInt32(txtGetContactId.Text);
string MethodCalled = ((System.Windows.Forms.Button)sender).Text;
txtOutput.Text = "Executing " + MethodCalled + "...\r\n\r\n";
switch (MethodCalled)
{
case "GetContact" :
results = mProxy.GetContact(contactId);
break;
case "GetContactCLR" :
results = mProxy.GetContactCLR(contactId);
break;
case "GetContactSPCLR" :
results = mProxy.GetContactSPCLR(contactId);
break;
}
DisplayResults(results);
}
private void cArrayInsertContact_Click(object sender, System.EventArgs e)
{
// This function is called when the GetControl buttons are clicked.
// A control array is setup to include btnInsertContact,
// btnInsertContactCLR, btnInsertContactSPCLR,
// so this function is invoked whenever those buttons are clicked.
// The function determines which button was clicked and then calls
// the appropriate generated function.
object[] results = null;
string MethodCalled = ((System.Windows.Forms.Button)sender).Text;
bool nameStyle = true ? ("1" == cboNameStyle.Text) : false;
int emailPromotion = 0;
if (txtEmailPromotion.Text.Length > 0)
{
emailPromotion = Convert.ToInt32(txtEmailPromotion.Text);
}
txtOutput.Text = "Executing " + MethodCalled + "...\r\n\r\n";
switch (MethodCalled)
{
case "InsertContact" :
results = mProxy.InsertContact(txtSaluation.Text, txtFirstName.Text,
txtMiddleName.Text, txtLastName.Text, nameStyle, txtPhone.Text,
txtSuffix.Text, txtEmail.Text, emailPromotion);
break;
case "InsertContactCLR" :
results = mProxy.InsertContactCLR(txtSaluation.Text, txtFirstName.Text,
txtMiddleName.Text, txtLastName.Text, nameStyle, txtPhone.Text,
txtSuffix.Text, txtEmail.Text, emailPromotion);
break;
case "InsertContactSPCLR" :
results = mProxy.InsertContactSPCLR(txtSaluation.Text, txtFirstName.Text,
txtMiddleName.Text, txtLastName.Text, nameStyle, txtPhone.Text,
txtSuffix.Text, txtEmail.Text, emailPromotion);
break;
}
DisplayResults(results);
}
}
}
Additional Native HTTP Information
For more information on using Native HTTP SOAP Support within Yukon, please
consule the Books Online Book "Accessing
SQL Server Using Native HTTP SOAP Support".
Within Books Online, this book can be navigated to by clicking Contents->SQL
Server Books Online->
Database Engine->Accessing SQL Server Using Native HTTP SOAP Support.
Within this book, there is a good example on how to create another VS.NET GUI
application to use the HTTP ENDPOINT as a web reference. This is another
tutorial to see how
WSDL and HTTP ENDPOINTs work within VS.NET.
The sample code is available here.