使用 WCF 服務模型從 SQL Server 接收輪詢型資料變更訊息
您可以設定 SQL 配接器來接收SQL Server資料表或檢視的定期資料變更訊息。 您可以指定配接器執行的輪詢語句來輪詢資料庫。 輪詢語句可以是 SELECT 語句或傳回結果集的預存程式。
如需配接器如何支援輪詢的詳細資訊,請參閱使用 SQL 配接器在SQL Server中輪詢。
注意
本主題示範如何使用 輪詢輸入 作業來使用輪詢訊息。 輪詢作業的訊息不是強型別。 如果您想要取得強型別輪詢訊息,您必須使用 TypedPolling 作業。 您也必須使用 TypedPolling 作業在單一應用程式中有多個輪詢作業。 如需有關如何執行TypedPolling作業的指示,請參閱使用 WCF 服務模型從 SQL Server 接收強型別輪詢型資料變更訊息。
重要
如果您想要在單一應用程式中有多個輪詢作業,您必須在連線 URI 中指定 InboundID 連線屬性,使其成為唯一的一部分。 您指定的輸入識別碼會新增至作業命名空間,使其成為唯一的。
本主題示範輪詢的方式
在本主題中,若要示範 SQL 配接器如何支援接收資料變更訊息,請建立 .NET 應用程式並產生 輪詢 作業的 WCF 服務合約。 如果您想要在產生 WCF 服務合約時指定輪詢相關的系結屬性,請將 PolledDataAvailableStatement 指定為:
SELECT COUNT(*) FROM Employee
PolledDataAvailableStatement必須傳回結果集,其中包含正值的第一個儲存格。 如果第一個儲存格不包含正值,配接器就不會執行輪詢語句。
在輪詢語句中,執行下列作業:
從 Employee 資料表中選取所有資料列。
執行預存程式 (MOVE_EMP_DATA) 將所有記錄從 Employee 資料表移至 EmployeeHistory 資料表。
執行預存程式 (ADD_EMP_DETAILS) ,將新記錄新增至 Employee 資料表。 此程式會採用員工名稱、指定和薪資作為參數。
若要執行這些作業,您必須為 PollingStatement 系結屬性指定下列專案:
SELECT * FROM Employee;EXEC MOVE_EMP_DATA;EXEC ADD_EMP_DETAILS John, Tester, 100000
執行輪詢語句之後,會選取 Employee 資料表中的所有記錄,並收到來自SQL Server的訊息。 配接器執行MOVE_EMP_DATA預存程式之後,所有記錄都會移至 EmployeeHistory 資料表。 然後,會執行ADD_EMP_DETAILS預存程式,將新記錄新增至 Employee 資料表。 下一個輪詢執行只會傳回單一記錄。 此週期會繼續執行,直到您關閉服務主機為止。
使用 SQL 配接器系結屬性設定輪詢查詢
下表摘要說明您用來設定配接器以接收資料變更訊息的 SQL 配接器系結屬性。 您必須將這些系結屬性指定為 .NET 應用程式的一部分以進行輪詢。
Binding 屬性 | Description |
---|---|
InboundOperationType | 指定您要執行 Polling、 TypedPolling或 Notification 輸入作業。 預設值為 Polling。 |
PolledDataAvailableStatement | 指定配接器執行的 SQL 語句,以判斷是否有任何資料可供輪詢。 SQL 語句必須傳回包含資料列和資料行的結果集。 只有在資料列可用時,才會執行 針對 PollingStatement 系結屬性指定的 SQL 語句。 |
PollingIntervalInSeconds | 指定間隔,以秒為單位,SQL 配接器會執行 針對 PolledDataAvailableStatement 系結屬性指定的語句。 預設值為 30 秒。 輪詢間隔會決定連續輪詢之間的時間間隔。 如果語句是在指定的間隔內執行,配接器會等候間隔中的剩餘時間。 |
PollingStatement | 指定要輪詢SQL Server資料庫資料表的 SQL 語句。 您可以指定輪詢語句的簡單 SELECT 語句或預存程式。 預設值是 null。 您必須指定 PollingStatement 的值,才能啟用輪詢。 只有在有資料可供輪詢時,才會執行輪詢語句,這是由 PolledDataAvailableStatement 系結屬性所決定。 您可以指定以分號分隔的任意數目 SQL 語句。 |
PollWhileDataFound | 指定 SQL 配接器是否忽略輪詢間隔,並持續執行 針對 PolledDataAvailableStatement 系結屬性指定的 SQL 語句,如果正在輪詢的資料表中有資料可用。 如果資料表中沒有可用的資料,配接器會還原為在指定的輪詢間隔執行 SQL 語句。 預設值為 false。 |
如需這些屬性的更完整描述,請參閱閱讀 BizTalk Adapter for SQL Server 配接器系結屬性。 如需如何使用 SQL 配接器輪詢SQL Server的完整描述,請進一步閱讀。
在 WCF 服務模型中設定輪詢
若要在使用 WCF 服務模型時接收 輪詢 作業,您必須:
從配接器所公開的中繼資料產生 WCF 服務合約 (介面) 。 若要這樣做,您可以使用新增配接器服務參考 Visual Studio 外掛程式。
從這個介面實作 WCF 服務。
使用服務主機裝載此 WCF 服務, (System.ServiceModel.ServiceHost) 。
關於本主題中使用的範例
本主題中的範例會輪詢 Employee 資料表。 此範例也會使用 MOVE_EMP_DATA 和 ADD_EMP_DETAILS 預存程式。 產生這些成品的腳本會隨附範例。 如需範例的詳細資訊,請參閱 SQL 配接器的範例。 SQL 配接器範例也會提供以本主題為基礎的範例 Polling_ServiceModel。
WCF 服務合約和類別
您可以使用 [新增配接器服務參考外掛程式] 來建立 WCF 服務合約 (介面) 和支援 輪詢 作業的類別。 如需產生 WCF 服務合約的詳細資訊,請參閱為SQL Server成品產生 WCF 用戶端或 WCF 服務合約。
WCF 服務合約 (介面)
下列程式碼顯示針對 輪詢 作業產生的 WCF 服務合約 (介面) 。
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://schemas.microsoft.com/Sql/2008/05/", ConfigurationName="PollingOperation")]
public interface PollingOperation {
// CODEGEN: Generating message contract since the wrapper namespace (https://schemas.microsoft.com/Sql/2008/05/Polling/) of message Polling
// does not match the default value (https://schemas.microsoft.com/Sql/2008/05/)
[System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="Polling")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
void Polling(Polling request);
}
訊息合約
如果指定,訊息合約命名空間會由連線 URI 中的 InboundID 參數修改。 在此範例中,您未在連線 URI 中指定輸入識別碼。 要求訊息會傳回 DataSet。
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Polling", WrapperNamespace="http://schemas.microsoft.com/Sql/2008/05/Polling/", IsWrapped=true)]
public partial class Polling {
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://schemas.microsoft.com/Sql/2008/05/Polling/", Order=0)]
[System.Xml.Serialization.XmlArrayAttribute(IsNullable=true)]
[System.Xml.Serialization.XmlArrayItemAttribute("DataSet", Namespace="http://schemas.datacontract.org/2004/07/System.Data", IsNullable=false)]
public System.Data.DataSet[] PolledData;
public Polling() {
}
public Polling(System.Data.DataSet[] PolledData) {
this.PolledData = PolledData;
}
}
WCF 服務類別
[新增配接器服務參考外掛程式] 也會產生檔案,該檔案具有從服務合約 (介面) 實作之 WCF 服務類別的存根。 檔案名為 SqlAdapterBindingService.cs。 您可以將邏輯直接插入此類別來處理 輪詢 作業。 下列程式碼顯示新增配接器服務參考外掛程式所產生的 WCF 服務類別。
namespace SqlAdapterBindingNamespace {
public class SqlAdapterBindingService : PollingOperation {
// CODEGEN: Generating message contract since the wrapper namespace (https://schemas.microsoft.com/Sql/2008/05/Polling/) of message Polling
// does not match the default value (https://schemas.microsoft.com/Sql/2008/05/)
public virtual void Polling(Polling request) {
throw new System.NotImplementedException("The method or operation is not implemented.");
}
}
}
接收輪詢作業的輸入訊息
本節提供如何撰寫 .NET 應用程式以使用 SQL 配接器接收輸入輪詢訊息的指示。
從 SQL 配接器接收輪詢訊息
使用 [新增配接器服務參考外掛程式] 來產生 WCF 服務合約, (介面) 和 輪詢 作業的協助程式類別。 如需詳細資訊,請參閱為SQL Server成品產生 WCF 用戶端或 WCF 服務合約。 您可以在產生服務合約和協助程式類別時選擇性地指定系結屬性。 這可確保它們已正確設定在產生的組態檔中。
從步驟 1 中產生的介面和協助程式類別實作 WCF 服務。 如果發生錯誤處理從輪詢作業收到的資料,這個類別的Polling方法可能會擲回例外狀況以中止輪詢交易;否則方法不會傳回任何專案。 您必須屬性 WCF 服務類別,如下所示:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
在 Polling 方法中,您可以直接實作應用程式邏輯。 您可以在 SqlAdapterBindingService.cs 中找到此類別。 此範例子類別中的這個程式碼是 SqlAdapterBindingService 類別。 在此程式碼中,接收為 DataSet 的輪詢訊息會寫入主控台。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class PollingService : SqlAdapterBindingNamespace.SqlAdapterBindingService { public override void Polling(Polling request) { Console.WriteLine("\nNew Polling Records Received"); Console.WriteLine("*************************************************"); DataSet[] dataArray = request.PolledData; foreach (DataTable tab in dataArray[0].Tables) { foreach (DataRow row in tab.Rows) { for (int i = 0; i < tab.Columns.Count; i++) { Console.WriteLine(row[i]); } } } Console.WriteLine("*************************************************"); Console.WriteLine("\nHit <RETURN> to stop polling"); } }
因為 SQL 配接器不接受認證作為連線 URI 的一部分,所以您必須實作下列類別來傳遞SQL Server資料庫的認證。 在應用程式的後半部,您會具現化此類別,以傳遞SQL Server認證。
class PollingCredentials : ClientCredentials, IServiceBehavior { public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { bindingParameters.Add(this); } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } protected override ClientCredentials CloneCore() { ClientCredentials clone = new PollingCredentials(); clone.UserName.UserName = this.UserName.UserName; clone.UserName.Password = this.UserName.Password; return clone; } }
建立 SqlAdapterBinding ,並藉由指定系結屬性來設定輪詢作業。 您可以在程式碼中明確執行此動作,或在組態中以宣告方式執行此動作。 您至少必須指定 InboundOperationType、 PolledDataAvailableStatement和 PollingStatement。
SqlAdapterBinding binding = new SqlAdapterBinding(); binding.InboundOperationType = InboundOperation.Polling; binding.PolledDataAvailableStatement = "SELECT COUNT (*) FROM EMPLOYEE"; binding.PollingStatement = "SELECT * FROM Employee;EXEC MOVE_EMP_DATA;EXEC ADD_EMP_DETAILS John, Tester, 100000";
藉由具現化您在步驟 3 中建立的PollingCredentials類別,指定SQL Server資料庫認證。
PollingCredentials credentials = new PollingCredentials(); credentials.UserName.UserName = "<Enter user name here>"; credentials.UserName.Password = "<Enter password here>";
建立在步驟 2 中建立之 WCF 服務的實例。
// create service instance PollingService service = new PollingService();
使用 WCF 服務和基底連線 URI 建立 System.ServiceModel.ServiceHost 的實例。 如果指定,基底連線 URI 不能包含輸入識別碼。 您也應該在這裡指定認證。
// Enable service host Uri[] baseUri = new Uri[] { new Uri("mssql://mysqlserver//mydatabase") }; ServiceHost serviceHost = new ServiceHost(service, baseUri); serviceHost.Description.Behaviors.Add(credentials);
將服務端點新增至服務主機。 作法如下:
使用步驟 4 中建立的系結。
指定包含認證的連線 URI,並視需要指定輸入識別碼。
將合約指定為 「PollingOperation」。
// Add service endpoint: be sure to specify PollingOperation as the contract Uri ConnectionUri = new Uri("mssql://mysqlserver//mydatabase?"); serviceHost.AddServiceEndpoint("PollingOperation", binding, ConnectionUri);
若要接收輪詢資料,請開啟服務主機。 每當查詢傳回結果集時,配接器就會傳回資料。
// Open the service host to begin polling serviceHost.Open();
若要終止輪詢,請關閉服務主機。
重要
配接器會繼續輪詢,直到服務主機關閉為止。
serviceHost.Close();
範例
下列範例顯示執行 Employee 資料表的輪詢查詢。 輪詢語句會執行下列工作:
從 Employee 資料表中選取所有記錄。
執行MOVE_EMP_DATA預存程式,將所有記錄從 Employee 資料表移至 EmployeeHistory 資料表。
執行ADD_EMP_DETAILS預存程式,將單一記錄新增至 Employee 資料表。
第一個輪詢訊息會包含 Employee 資料表中的所有記錄。 後續輪詢訊息只會包含ADD_EMP_DETAILS預存程式所插入的最後一筆記錄。 配接器會繼續輪詢,直到您按下
<RETURN>
關閉服務主機為止。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Adapters.Sql;
using Microsoft.ServiceModel.Channels;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.Collections.ObjectModel;
using System.Data;
namespace Polling_ServiceModel
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PollingService : SqlAdapterBindingNamespace.SqlAdapterBindingService
{
public override void Polling(Polling request)
{
Console.WriteLine("\nNew Polling Records Received");
Console.WriteLine("*************************************************");
DataSet[] dataArray = request.PolledData;
foreach (DataTable tab in dataArray[0].Tables)
{
foreach (DataRow row in tab.Rows)
{
for (int i = 0; i < tab.Columns.Count; i++)
{
Console.WriteLine(row[i]);
}
}
}
Console.WriteLine("*************************************************");
Console.WriteLine("\nHit <RETURN> to stop polling");
}
}
class PollingCredentials : ClientCredentials, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
bindingParameters.Add(this);
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{ }
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{ }
protected override ClientCredentials CloneCore()
{
ClientCredentials clone = new PollingCredentials();
clone.UserName.UserName = this.UserName.UserName;
clone.UserName.Password = this.UserName.Password;
return clone;
}
}
class Program
{
static void Main(string[] args)
{
ServiceHost serviceHost = null;
try
{
Console.WriteLine("Sample started...");
Console.WriteLine("Press any key to start polling...");
Console.ReadLine();
SqlAdapterBinding binding = new SqlAdapterBinding();
binding.InboundOperationType = InboundOperation.Polling;
binding.PolledDataAvailableStatement = "SELECT COUNT (*) FROM EMPLOYEE";
binding.PollingStatement = "SELECT * FROM Employee;EXEC MOVE_EMP_DATA;EXEC ADD_EMP_DETAILS John, Tester, 100000";
Console.WriteLine("Binding properties assigned...");
// This URI is used to specify the address for the ServiceEndpoint
// It must contain the InboundId (if any) that was used to generate
// the WCF service callback interface
Uri ConnectionUri = new Uri("mssql://mysqlserver//mydatabase?");
// This URI is used to initialize the ServiceHost. It cannot contain
// a query_string (InboundID); otherwise,an exception is thrown when
// the ServiceHost is initialized.
Uri[] baseUri = new Uri[] { new Uri("mssql://mysqlserver//mydatabase") };
PollingCredentials credentials = new PollingCredentials();
credentials.UserName.UserName = "<Enter user name here>";
credentials.UserName.Password = "<Enter password here>";
Console.WriteLine("Opening service host...");
PollingService service = new PollingService();
serviceHost = new ServiceHost(service, baseUri);
serviceHost.Description.Behaviors.Add(credentials);
serviceHost.AddServiceEndpoint("PollingOperation", binding, ConnectionUri);
serviceHost.Open();
Console.WriteLine("Service host opened...");
Console.WriteLine("Polling started...");
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine("Exception :" + e.Message);
Console.ReadLine();
/* If there is an error it will be specified in the inner exception */
if (e.InnerException != null)
{
Console.WriteLine("InnerException: " + e.InnerException.Message);
Console.ReadLine();
}
}
finally
{
// IMPORTANT: you must close the ServiceHost to stop polling
if (serviceHost.State == CommunicationState.Opened)
serviceHost.Close();
else
serviceHost.Abort();
}
}
}
}